From 2967116efa02e5a231761d1a13bb47a323c72fa5 Mon Sep 17 00:00:00 2001 From: yangyxd <2514718952@qq.com> Date: Sun, 6 Sep 2015 13:28:01 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9F=B3=E5=84=BF=E5=B0=8F=E7=99=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/test.json | 1 + bin/test.unicode.json | Bin 0 -> 23078 bytes .../CMOV.inc" | 30 + .../JSON_DataSet_Serialize_2007.dpr" | 15 + .../JSON_DataSet_Serialize_2007.dproj" | 143 + .../JSON_DataSet_Serialize_2007.res" | Bin 0 -> 5280 bytes .../JSON_DataSet_Serialize_XE6.dpr" | 15 + .../JSON_DataSet_Serialize_XE6.dproj" | 149 + .../JSON_DataSet_Serialize_XE6.res" | Bin 0 -> 62704 bytes .../Project1.res" | Bin 0 -> 62704 bytes .../Unit1.dfm" | 316 + .../Unit1.pas" | 533 ++ .../uQDBJson.pas" | 1027 ++ .../JSONTest.dpr" | 16 + .../JSONTest.dproj" | 76 + .../JSONTest.otares" | Bin 0 -> 96 bytes .../JSONTest.res" | Bin 0 -> 96048 bytes .../JSONTest_Icon2.ico" | Bin 0 -> 94198 bytes .../JSONTest_XE.dpr" | 17 + .../JSONTest_XE.dproj" | 213 + .../JSONTest_XE.res" | Bin 0 -> 95832 bytes .../JSONTest_d2007.dpr" | 17 + .../JSONTest_d2007.dproj" | 77 + .../JSONTest_d2007.res" | Bin 0 -> 4396 bytes .../JSONTest_d2007_Icon.ico" | Bin 0 -> 94198 bytes .../JSON_Test.dproj" | 165 + .../JSON_Test.res" | Bin 0 -> 95832 bytes .../JSON_Test_Icon.ico" | Bin 0 -> 94198 bytes .../Unit2.dfm" | 275 + .../Unit2.pas" | 426 + .../superobject.pas" | 6555 +++++++++++++ .../uJSON.pas" | 4389 +++++++++ .../uLkJSON.pas" | 2626 +++++ .../JSONTest.dpr" | 16 + .../JSONTest.dproj" | 146 + .../JSONTest.res" | Bin 0 -> 96112 bytes .../JSONTest_Icon2.ico" | Bin 0 -> 94198 bytes .../JSONTest_d2007.dpr" | 18 + .../JSONTest_d2007.dproj" | 101 + .../JSONTest_d2007_Icon.ico" | Bin 0 -> 94198 bytes .../JSON_Test_D2007.dproj" | 101 + .../JSON_Test_D2007.res" | Bin 0 -> 96112 bytes .../JSON_Test_XE.dpr" | 16 + .../JSON_Test_XE.dproj" | 183 + .../JSON_Test_XE.res" | Bin 0 -> 95832 bytes .../MSVCRT.LIB" | Bin 0 -> 129536 bytes .../main.dfm" | 92 + .../main.pas" | 739 ++ .../superobject.pas" | 6555 +++++++++++++ .../uJSON.pas" | 4389 +++++++++ .../uLkJSON.pas" | 2626 +++++ demo/YxdJson/JsonDebug/JSONDebug.dpr | 14 + demo/YxdJson/JsonDebug/JSONDebug.dproj | 149 + demo/YxdJson/JsonDebug/JSONDebug.res | Bin 0 -> 62704 bytes demo/YxdJson/JsonDebug/Project1.res | Bin 0 -> 62704 bytes demo/YxdJson/JsonDebug/Project1.skincfg | 46 + demo/YxdJson/JsonDebug/Unit1.dfm | 57 + demo/YxdJson/JsonDebug/Unit1.pas | 97 + .../YxdJson_VS_QJson/ProjectGroup1.groupproj | 36 + .../YxdJson_VS_QJson/jsondemo.12.2.dproj | 110 + .../YxdJson/YxdJson_VS_QJson/jsondemo.bdsproj | 186 + demo/YxdJson/YxdJson_VS_QJson/jsondemo.cfg | 39 + demo/YxdJson/YxdJson_VS_QJson/jsondemo.dpr | 15 + demo/YxdJson/YxdJson_VS_QJson/jsondemo.dproj | 433 + demo/YxdJson/YxdJson_VS_QJson/jsondemo.res | Bin 0 -> 92500 bytes .../YxdJson_VS_QJson/jsondemo_2007.dproj | 58 + .../YxdJson_VS_QJson/jsondemo_2007.res | Bin 0 -> 92780 bytes .../YxdJson_VS_QJson/jsondemo_Icon.ico | Bin 0 -> 91106 bytes demo/YxdJson/YxdJson_VS_QJson/main.dfm | 234 + demo/YxdJson/YxdJson_VS_QJson/main.pas | 1217 +++ demo/YxdJson/YxdJson_VS_QJson/qdac.inc | 10 + demo/clean.bat | 21 + qdac/QString.pas | 8482 +++++++++++++++++ qdac/qdac.inc | 9 + qdac/qjson.pas | 5889 ++++++++++++ qdac/qrbtree.pas | 1567 +++ source/Base64.pas | 382 + source/PerlRegEx.pas | 963 ++ source/YxdAdoStream.pas | 78 + source/YxdJson.pas | 6179 ++++++++++++ source/YxdRtti.pas | 1547 +++ source/YxdStr.pas | 3211 +++++++ source/pcre.pas | 1145 +++ source/pcre/makefile.mak | 130 + source/pcre/pcre_compile.obj | Bin 0 -> 34296 bytes source/pcre/pcre_config.obj | Bin 0 -> 1684 bytes source/pcre/pcre_default_tables.obj | Bin 0 -> 2619 bytes source/pcre/pcre_dfa_exec.obj | Bin 0 -> 26959 bytes source/pcre/pcre_exec.obj | Bin 0 -> 42191 bytes source/pcre/pcre_fullinfo.obj | Bin 0 -> 2098 bytes source/pcre/pcre_get.obj | Bin 0 -> 3505 bytes source/pcre/pcre_globals.obj | Bin 0 -> 1770 bytes source/pcre/pcre_info.obj | Bin 0 -> 1648 bytes source/pcre/pcre_maketables.obj | Bin 0 -> 2503 bytes source/pcre/pcre_newline.obj | Bin 0 -> 2340 bytes source/pcre/pcre_ord2utf8.obj | Bin 0 -> 1660 bytes source/pcre/pcre_refcount.obj | Bin 0 -> 1587 bytes source/pcre/pcre_study.obj | Bin 0 -> 3873 bytes source/pcre/pcre_tables.obj | Bin 0 -> 3559 bytes source/pcre/pcre_try_flipped.obj | Bin 0 -> 1888 bytes source/pcre/pcre_ucd.obj | Bin 0 -> 54707 bytes source/pcre/pcre_valid_utf8.obj | Bin 0 -> 1815 bytes source/pcre/pcre_version.obj | Bin 0 -> 1595 bytes source/pcre/pcre_xclass.obj | Bin 0 -> 2410 bytes 104 files changed, 64367 insertions(+) create mode 100644 bin/test.json create mode 100644 bin/test.unicode.json create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/CMOV.inc" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dpr" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dproj" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.res" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dpr" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dproj" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.res" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Project1.res" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Unit1.dfm" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Unit1.pas" create mode 100644 "demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/uQDBJson.pas" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dpr" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dproj" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.otares" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.res" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_Icon2.ico" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dpr" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dproj" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.res" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.dpr" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.dproj" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.res" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007_Icon.ico" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.dproj" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.res" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test_Icon.ico" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.dfm" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.pas" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/superobject.pas" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uJSON.pas" create mode 100644 "demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uLkJSON.pas" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dpr" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dproj" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.res" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_Icon2.ico" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dpr" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dproj" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007_Icon.ico" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_D2007.dproj" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_D2007.res" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dpr" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dproj" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.res" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/MSVCRT.LIB" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.dfm" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.pas" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/superobject.pas" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uJSON.pas" create mode 100644 "demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uLkJSON.pas" create mode 100644 demo/YxdJson/JsonDebug/JSONDebug.dpr create mode 100644 demo/YxdJson/JsonDebug/JSONDebug.dproj create mode 100644 demo/YxdJson/JsonDebug/JSONDebug.res create mode 100644 demo/YxdJson/JsonDebug/Project1.res create mode 100644 demo/YxdJson/JsonDebug/Project1.skincfg create mode 100644 demo/YxdJson/JsonDebug/Unit1.dfm create mode 100644 demo/YxdJson/JsonDebug/Unit1.pas create mode 100644 demo/YxdJson/YxdJson_VS_QJson/ProjectGroup1.groupproj create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo.12.2.dproj create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo.bdsproj create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo.cfg create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo.dpr create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo.dproj create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo.res create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.dproj create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.res create mode 100644 demo/YxdJson/YxdJson_VS_QJson/jsondemo_Icon.ico create mode 100644 demo/YxdJson/YxdJson_VS_QJson/main.dfm create mode 100644 demo/YxdJson/YxdJson_VS_QJson/main.pas create mode 100644 demo/YxdJson/YxdJson_VS_QJson/qdac.inc create mode 100644 demo/clean.bat create mode 100644 qdac/QString.pas create mode 100644 qdac/qdac.inc create mode 100644 qdac/qjson.pas create mode 100644 qdac/qrbtree.pas create mode 100644 source/Base64.pas create mode 100644 source/PerlRegEx.pas create mode 100644 source/YxdAdoStream.pas create mode 100644 source/YxdJson.pas create mode 100644 source/YxdRtti.pas create mode 100644 source/YxdStr.pas create mode 100644 source/pcre.pas create mode 100644 source/pcre/makefile.mak create mode 100644 source/pcre/pcre_compile.obj create mode 100644 source/pcre/pcre_config.obj create mode 100644 source/pcre/pcre_default_tables.obj create mode 100644 source/pcre/pcre_dfa_exec.obj create mode 100644 source/pcre/pcre_exec.obj create mode 100644 source/pcre/pcre_fullinfo.obj create mode 100644 source/pcre/pcre_get.obj create mode 100644 source/pcre/pcre_globals.obj create mode 100644 source/pcre/pcre_info.obj create mode 100644 source/pcre/pcre_maketables.obj create mode 100644 source/pcre/pcre_newline.obj create mode 100644 source/pcre/pcre_ord2utf8.obj create mode 100644 source/pcre/pcre_refcount.obj create mode 100644 source/pcre/pcre_study.obj create mode 100644 source/pcre/pcre_tables.obj create mode 100644 source/pcre/pcre_try_flipped.obj create mode 100644 source/pcre/pcre_ucd.obj create mode 100644 source/pcre/pcre_valid_utf8.obj create mode 100644 source/pcre/pcre_version.obj create mode 100644 source/pcre/pcre_xclass.obj diff --git a/bin/test.json b/bin/test.json new file mode 100644 index 0000000..6c44514 --- /dev/null +++ b/bin/test.json @@ -0,0 +1 @@ +{"status":"Success","returnType":19,"origin":{"cityName":"南京市","listType":0,"results":[{"name":"南京师范大学(随园校区)","address":"宁海路122号南京师范大学随园校区(近五台山体育馆北门)","telephone":"(025)83598010","location":{"lng":118.76910802631,"lat":32.053549002457}},{"name":"南京师范大学紫金校区","address":"南京市玄武区板仓街78号","telephone":"(025)85481074","location":{"lng":118.82148937875,"lat":32.071797107485}},{"name":"南京师范大学(商学院)","address":"杨家坟","telephone":"","location":{"lng":118.74468339485,"lat":31.924527172637}},{"name":"南京师范大学医院","address":"南京市鼓楼区","telephone":"","location":{"lng":118.76635014944,"lat":32.053867502814}},{"name":"南京师大自学考试办公室","address":"宁海路122","telephone":"","location":{"lng":118.76877004777,"lat":32.054465459609}},{"name":"南京师大劳动服务公司","address":"板仓街78号","telephone":"","location":{"lng":118.82260967369,"lat":32.071724518845}},{"name":"南京师大教师培训中心","address":"宁海路122","telephone":"","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师范大学美术学院陶瓷艺术研究所","address":"南京师范大学美术学院附近","telephone":"","location":{"lng":118.77077002302,"lat":32.052959806771}},{"name":"南京师大mba教育中心","address":"宁海路122","telephone":"(025)83598010","location":{"lng":118.77029969033,"lat":32.051798283464}},{"name":"南京师范大学出版社","address":"宁海路122-1","telephone":"(025)83598297","location":{"lng":118.7716101983,"lat":32.054596357149}},{"name":"南京师范大学","address":"近郊栖霞区文苑路1号南京师范大学仙林校区(近学海路)","telephone":"(025)85891111","location":{"lng":118.91053767444,"lat":32.100842038301}},{"name":"南京师范大学(南京师范大学茶苑西北)","address":"南京师范大学茶苑附近","telephone":"","location":{"lng":118.89749419942,"lat":32.100124476633}},{"name":"南京师大文化产业研究院","address":"宁海路122号","telephone":"","location":{"lng":118.76677027128,"lat":32.05341620449}},{"name":"南京师大动画教学实验室","address":"文苑路1号","telephone":"(025)85891111","location":{"lng":118.91293980185,"lat":32.103668274675}},{"name":"南京师大动感地带品牌店","address":"文苑路1号","telephone":"","location":{"lng":118.91386034804,"lat":32.11373422087}},{"name":"南京师大文教资料杂志社","address":"宁海路122号","telephone":"","location":{"lng":118.76677027128,"lat":32.05341620449}},{"name":"南京师大数理化图书馆","address":"文苑路1号","telephone":"(025)85898807","location":{"lng":118.91131993851,"lat":32.101370704782}},{"name":"南京师大实验动物中心","address":"文苑路1号","telephone":"(025)85891111","location":{"lng":118.91638979554,"lat":32.116474833066}},{"name":"南京师大老年学研究中心","address":"宁海路122","telephone":"","location":{"lng":118.77001049735,"lat":32.052155491725}},{"name":"南京师大(紫金校区)-南门","address":"板仓街78","telephone":"(025)85412341","location":{"lng":118.82176015911,"lat":32.070845574988}},{"name":"南京师大国际经济研究中心","address":"宁海路122","telephone":"","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师大幼儿教育发展中心","address":"宁海路128号南山专家楼7层","telephone":"","location":{"lng":118.76850053291,"lat":32.052734199139}},{"name":"南京师大中国经济研究中心","address":"宁海路122","telephone":"","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师大南师教育培训中心","address":"龙蟠中路317号","telephone":"(025)84868258","location":{"lng":118.80183941887,"lat":32.03041317888}},{"name":"南京师范大学图书情报管理研究所","address":"宁海路122号南京师范大学随园校区华夏图书馆1层","telephone":"(025)83598010","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师大老年体育工作者协会","address":"宁海路122","telephone":"","location":{"lng":118.77034005684,"lat":32.054443980065}},{"name":"南京师范大学环境科学研究所","address":"南京师范大学法学院附近","telephone":"","location":{"lng":118.77029969033,"lat":32.051798283464}},{"name":"南京师范大学社会发展学院","address":"宁海路","telephone":"","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师大(仙林校区)-3号门","address":"文苑路3号","telephone":"(025)85891111","location":{"lng":118.90674972446,"lat":32.098174130253}},{"name":"南京师大出版社·两江培训","address":"南京市鼓楼区","telephone":"(025)83283232","location":{"lng":118.77100978005,"lat":32.054668665478}},{"name":"南京师范大学后宰门学区","address":"后宰门西村9号","telephone":"(025)84804071","location":{"lng":118.81909959839,"lat":32.046478351732}},{"name":"南京师大学生社团联合会紫金分会","address":"板仓街78号","telephone":"","location":{"lng":118.82045021141,"lat":32.072399348362}},{"name":"南京师大(仙林校区)-1号主校门","address":"文范路1","telephone":"(025)85891111","location":{"lng":118.91263020472,"lat":32.100090985023}},{"name":"南京师范大学中北学院(学林路)","address":"南京仙林大学城学林路2号","telephone":"(025)85898048","location":{"lng":118.91000008439,"lat":32.115897456875}},{"name":"南京师大认知科学研究中心","address":"宁海路122号","telephone":"","location":{"lng":118.76596026715,"lat":32.052589616894}},{"name":"南京师范大学国际健身中心","address":"宁海路122号南京师范大学随园校区操场西侧体育馆2楼","telephone":"(025)83704511","location":{"lng":118.76596026715,"lat":32.052589616894}},{"name":"南京师范大学乳品生物技术研究所","address":"宁海路122号附近","telephone":"","location":{"lng":118.76987000731,"lat":32.051729772315}},{"name":"南京师大计算机科学与技术学院","address":"宁海路122","telephone":"(025)83598010","location":{"lng":118.76911977004,"lat":32.052845105619}},{"name":"南京师大社会工作研究与发展中心","address":"宁海路122","telephone":"","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师大音乐教育研究所","address":"宁海路122","telephone":"","location":{"lng":118.77081990226,"lat":32.052675880167}},{"name":"南京师范大学公共管理学院","address":"宁海路122号附近","telephone":"(025)83598010","location":{"lng":118.77009325504,"lat":32.052528741595}},{"name":"南京师范大学金陵营养与资源研究所","address":"宁海路122号附近","telephone":"","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师范大学附属扬子中学(水榭花苑东)","address":"杨村路146号","telephone":"","location":{"lng":118.76126115916,"lat":32.23904416039}},{"name":"师豪高校实业有限公司南京师范大学后勤实业中心","address":"文苑路1","telephone":"","location":{"lng":118.90597514248,"lat":32.099590976765}},{"name":"金门驾校(南京师范大学仙林校区报名处)","address":"南京师范大学仙林校区内","telephone":"(025)66167606,18013893689","location":{"lng":118.90795742823,"lat":32.101946287414}},{"name":"南京师大紫金校区学生第二餐厅","address":"南京玄武区板仓街78号(近南师大紫金校区)","telephone":"(025)85768899","location":{"lng":118.82188006769,"lat":32.070801725057}},{"name":"南京师大科技实业集团公司物联网包装应用材料研究所","address":"宁海路122","telephone":"","location":{"lng":118.76762038941,"lat":32.052635722885}},{"name":"南京师范大学生命科学学院遗传资源研究所","address":"南京师范大学随园校区28号楼","telephone":"(025)86083183","location":{"lng":118.76597028327,"lat":32.052482353281}},{"name":"南京师范大学法学院","address":"宁海路122南京师范大学逸夫楼","telephone":"(025)83598010","location":{"lng":118.77013031393,"lat":32.052474512836}},{"name":"南京师范大学文学院","address":"南京师范大学随园校区中大楼","telephone":"(025)83598450","location":{"lng":118.76739494533,"lat":32.053465931186}}]},"destination":{"cityName":"北京市","listType":0,"results":[{"name":"中国政法大学","address":"314路;314路;345快;345快;357路;357路;357区间;357区间;870路;870路;881路;881路;884路;884路;888快;888快;888路;888路;922路;922路;925路;925路;h63路;h63路;昌11路;昌11路;昌16路","telephone":"","location":{"lng":116.24540028074,"lat":40.22107025404}},{"name":"中国政法大学(学院路校区)","address":"海淀区西土城路25号","telephone":"(010)58909114","location":{"lng":116.35152026298,"lat":39.965765692502}},{"name":"中国政法大学(昌平校区)","address":"北京市昌平区府学路27号","telephone":"(010)58909162","location":{"lng":116.24758143785,"lat":40.222819419441}},{"name":"中国政法大学-东门","address":"北京市海淀区西土城路25号","telephone":"010-58909114","location":{"lng":116.35298127874,"lat":39.965010349286}},{"name":"中国政法大学-南门","address":"北京市海淀区西土城路25号","telephone":"010-58909114","location":{"lng":116.35172062395,"lat":39.964043105482}},{"name":"中国政法大学-北门","address":"北京市海淀区西土城路25号","telephone":"010-58909114","location":{"lng":116.35185167304,"lat":39.967012816241}},{"name":"中国政法大学校医院","address":"昌平区府学路","telephone":"(010)58909114","location":{"lng":116.35016092068,"lat":39.966345693083}},{"name":"中国政法大学医院","address":"东关路","telephone":"(010)58909228","location":{"lng":116.24755005558,"lat":40.225229615104}},{"name":"中国政法大学研究生院","address":"北京市海淀区西土城路25号法大培训区606室","telephone":"010-58908070","location":{"lng":116.3501716543,"lat":39.966718704908}},{"name":"中国政法大学司法考试学院","address":"北京市海淀区西土城路25号","telephone":"","location":{"lng":116.35302567056,"lat":39.964802550323}},{"name":"中国政法大学出版社","address":"北京市海淀区西土城路25号院5号楼","telephone":"010-58908302","location":{"lng":116.35280065402,"lat":39.9664150491}},{"name":"中国政法大学留学_政法大学","address":"北京市海淀区西土城路25号中国政法大学","telephone":"4006887125","location":{"lng":116.35299063827,"lat":39.965030970199}},{"name":"中国政法大学中国法律信息中心","address":"北京市海淀区","telephone":"","location":{"lng":116.35017646977,"lat":39.966103715419}},{"name":"中国政法大学-停车场","address":"西土城路25号附近","telephone":"","location":{"lng":116.35299027087,"lat":39.965037883995}},{"name":"中国政法大学第一食堂","address":"中国政法大学内","telephone":"(010)58909239","location":{"lng":116.24807063917,"lat":40.222604059532}},{"name":"中国政法大学篮球场","address":"北京市昌平区","telephone":"","location":{"lng":116.24786262229,"lat":40.224597477359}},{"name":"中国政法大学-停车场","address":"西土城路辅路附近","telephone":"","location":{"lng":116.35259347272,"lat":39.965035487141}},{"name":"中国政法大学门诊部","address":"北京市海淀区西土城路25号","telephone":"","location":{"lng":116.35299063827,"lat":39.965030970199}},{"name":"中国政法大学第二食堂","address":"中国政法大学内","telephone":"(010)58909239","location":{"lng":116.24921054347,"lat":40.222576543978}},{"name":"中国政法大学教育基金会","address":"北京市昌平区府学路27号中国政法大学发展规划与学科建设处","telephone":"","location":{"lng":116.24644725331,"lat":40.221373328051}},{"name":"中国政法大学第三食堂","address":"中国政法大学内","telephone":"(010)58909239","location":{"lng":116.24803007412,"lat":40.225124575975}},{"name":"中国政法大学斋8号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24415272767,"lat":40.228174468919}},{"name":"中国政法大学网球场","address":"北京市昌平区","telephone":"","location":{"lng":116.24758460452,"lat":40.224478563352}},{"name":"中国政法大学斋3号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24457248447,"lat":40.227643933114}},{"name":"中国政法大学斋5号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24469236426,"lat":40.22806869573}},{"name":"中国政法大学一食堂","address":"昌平区府学路27号","telephone":"","location":{"lng":116.24644725331,"lat":40.221373328051}},{"name":"中国政法大学医院-急诊","address":"北京市昌平区","telephone":"(010)58909227","location":{"lng":116.24741989084,"lat":40.225225072479}},{"name":"中国政法大学劳动法诊所","address":"北京市海淀区西土城路25号学院路中国政法大学正门南侧北京教育工会","telephone":"(010)62228839,(010)62260586","location":{"lng":116.35299063827,"lat":39.965030970199}},{"name":"中国政法大学6号楼","address":"北京市海淀区","telephone":"","location":{"lng":116.35269469079,"lat":39.964343388009}},{"name":"中国政法大学家属宿舍1号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24682079899,"lat":40.227473354607}},{"name":"中国政法大学家属宿舍12号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24928000732,"lat":40.226237928008}},{"name":"中国政法大学学生公寓9号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24586346816,"lat":40.225280421793}},{"name":"中国政法大学家属宿舍3号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24673756909,"lat":40.226751947191}},{"name":"中国政法大学留学服务中心","address":"昌平校区,昌平区府学路27号中国政法大学国际交流中心a座116室","telephone":"(010)58909583","location":{"lng":116.24506055761,"lat":40.223043306022}},{"name":"中国政法大学家属宿舍9号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24957102235,"lat":40.227850561384}},{"name":"中国政法大学(昌平校区)-东门","address":"北京市昌平区","telephone":"","location":{"lng":116.2500601188,"lat":40.223015517488}},{"name":"中国政法大学(昌平校区)-南门","address":"府学路27号","telephone":"(010)58909162","location":{"lng":116.24646039879,"lat":40.221283593573}},{"name":"中国政法大学停车场-出入口","address":"西土城路25号","telephone":"","location":{"lng":116.35287300898,"lat":39.965059958376}},{"name":"中国政法大学家属宿舍4号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24683001235,"lat":40.226392287523}},{"name":"中国政法大学家属宿舍11号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24957080414,"lat":40.226810969839}},{"name":"中国政法大学家属宿舍6号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24843575441,"lat":40.227595741973}},{"name":"中国政法大学家属宿舍10号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24954213809,"lat":40.227493078518}},{"name":"中国政法大学家属宿舍2号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24671364831,"lat":40.227110380853}},{"name":"中国政法大学家属宿舍5号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24690402712,"lat":40.226012303052}},{"name":"中国政法大学家属宿舍8号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.2466679079,"lat":40.227806557541}},{"name":"中国政法大学家属宿舍7号楼","address":"北京市昌平区","telephone":"","location":{"lng":116.24796013057,"lat":40.227315176044}},{"name":"中国政法大学-继续教育学院","address":"西土城路25中国政法大学","telephone":"","location":{"lng":116.35062103162,"lat":39.966797340775}},{"name":"中国政法大学(昌平校区)-北门","address":"县城东关路","telephone":"","location":{"lng":116.24768034751,"lat":40.224745341156}},{"name":"中国政法大学运输服务中心","address":"北三环西路辅路明光北里(北二门)附近","telephone":"","location":{"lng":116.35164484944,"lat":39.967015005851}},{"name":"中国政法大学国际交流中心","address":"东关路5号院13号","telephone":"(010)59915588","location":{"lng":116.24821959932,"lat":40.225258846643}}]}} \ No newline at end of file diff --git a/bin/test.unicode.json b/bin/test.unicode.json new file mode 100644 index 0000000000000000000000000000000000000000..a2c49268ef73a39962c3e35b2e75f99e49a83764 GIT binary patch literal 23078 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 literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/CMOV.inc" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/CMOV.inc" new file mode 100644 index 0000000..edf4e19 --- /dev/null +++ "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/CMOV.inc" @@ -0,0 +1,30 @@ +{˵ +ʹ YxdInclude Ӧ븴CMOVԼĸĿ¼} + +{.$DEFINE USE_DEBUGMODE} // Ƿʹõģʽ + +{$IFDEF USE_DEBUGMODE} + {$DEFINE DebugApp} // õ״̬ +{$ENDIF} + +{$DEFINE OuputFileLog} //Ƿ־ļ +{.$DEFINE OuputRemoteLog} //ǷԶ־Ϣ + +{$IFDEF DebugApp} + {$DEFINE WRITEDEBUG} //ǷдϢ + {$DEFINE OutputDebug} //ǷϢϢ +{$ENDIF} + +{YxdCommon} +{$DEFINE USE_URLFUNC} // ǷʹURL + +{YxdHTTP} +{.$DEFINE USE_CoXml} // ǷʹCoXMLʽGetPOST + +{YxdPerisistent} +{$DEFINE USE_DBREADWRITE} // ǷԴݿж +{$DEFINE USE_XMLREADWRITE} // ǷʹXMLд +{$DEFINE USE_JSONREADWRITE} // ǷʹJSONд + + + diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dpr" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dpr" new file mode 100644 index 0000000..59a2232 --- /dev/null +++ "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dpr" @@ -0,0 +1,15 @@ +program JSON_DataSet_Serialize_2007; + +uses + Forms, + Unit1 in 'Unit1.pas' {Form1}; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dproj" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dproj" new file mode 100644 index 0000000..4dd23e8 --- /dev/null +++ "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.dproj" @@ -0,0 +1,143 @@ + + + + {cc48b93a-3e6b-4fe0-92f9-869afc3ef4c1} + Debug + AnyCPU + DCC32 + ..\..\..\bin\JSON_DataSet_Serialize_2007.exe + JSON_DataSet_Serialize_2007.dpr + + + 7.0 + False + False + 0 + RELEASE + + + 7.0 + DEBUG + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + + + Delphi.Personality + + +FalseTrueFalseFalseFalse1000FalseFalseFalseFalseFalse20529361.0.0.01.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + JSON_DataSet_Serialize_2007.dpr + + + + + + + + 'D:\Program Files\Autodesk\3ds Max 2014\' + 'C:\ProgramData' + 'D:\Program Files\Java\Android\android-sdk' + 'D:\' + 'C:\Users\Administrator\AppData\Roaming' + 'd:\program files (x86)\codegear\rad studio\5.0' + 'C:\Users\Public\Documents\RAD Studio\5.0' + 'D:\My Documents\Documents\RAD Studio\Projects' + 'D:\My Documents\Documents\RAD Studio\5.0' + 'D:\Program Files\Java\jdk1.7.0\lib;D:\Program Files\Java\jdk1.7.0\lib\tools.jar' + 'C:\Program Files (x86)\Common Files\Autodesk Shared\Materials\' + 'C:\Program Files (x86)\Common Files' + 'C:\Program Files (x86)\Common Files' + 'C:\Program Files\Common Files' + 'YANGYXD-HOME' + 'C:\Windows\system32\cmd.exe' + 'd:\program files (x86)\codegear\rad studio\5.0' + 'C:\Program Files (x86)\Microsoft DirectX SDK (March 2009)\' + 'NO' + 'C:' + '\Users\Administrator' + 'C:\Program Files (x86)\Common Files\Autodesk Shared\Materials\' + 'C:\Program Files (x86)\Common Files\Autodesk Shared\Materials\' + 'D:\Program Files\Java\jdk1.7.0' + 'C:\Users\Administrator\AppData\Local' + '\\YANGYXD-HOME' + '8' + 'Windows_NT' + 'C:\Program Files\Common Files\Microsoft Shared\Windows Live;d:\Program Files (x86)\CodeGear\RAD Studio\5.0\bin;C:\Users\Public\Documents\RAD Studio\5.0\Bpl;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Common Files\Thunder Network\KanKan\Codecs;C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;C:\Program Files\Microsoft SQL Server\100\Tools\Binn\;C:\Program Files\Microsoft SQL Server\100\DTS\Binn\;D:\Program Files\Java\jdk1.7.0\bin;D:\Program Files\Java\jdk1.7.0\jre\bin;D:\Program Files\Java\Android\android-sdk\tools;D:\Program Files\Java\Android\android-sdk\;D:\Program Files\Java\Android\android-sdk\platform-tools;C:\Program Files (x86)\Common Files\Autodesk Shared\;C:\Program Files (x86)\backburner 2\;C:\Program Files\Common Files\Autodesk Shared\;C:\Program Files (x86)\Autodesk\Backburner\;C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\;C:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\;C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\;C:\Users\Public\Documents\Embarcadero\Studio\14.0\Bpl;%CommonProgramFiles%\Microsoft Shared\Windows Live;C:\Users\Public\Documents\RAD Studio\5.0\Bpl' + '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC' + 'x86' + 'AMD64' + 'Intel64 Family 6 Model 58 Stepping 9, GenuineIntel' + '6' + '3a09' + 'C:\ProgramData' + 'C:\Program Files (x86)' + 'C:\Program Files (x86)' + 'C:\Program Files' + 'C:\Windows\system32\WindowsPowerShell\v1.0\Modules\' + 'C:\Users\Public' + 'Console' + 'C:' + 'C:\Windows' + 'C:\Users\ADMINI~1\AppData\Local\Temp' + 'C:\Users\ADMINI~1\AppData\Local\Temp' + 'YANGYXD-HOME' + 'Administrator' + 'C:\Users\Administrator' + 'D:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools\' + 'C:\Windows' + '3' + 'C:\BVTBin\Tests\installpackage\csilogfile.log' + + + + + + + MainSource + + +
Form1
+
+ +
+
\ No newline at end of file diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.res" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_2007.res" new file mode 100644 index 0000000000000000000000000000000000000000..e877da3fba0640448ae1b372a251d7c7ca9a0caa GIT binary patch literal 5280 zcmbVQO>Y}j6n#;UKuACrEZBME6^fLvHZ4#km?(TGf&wZvEJvkw8OSLK0bDNmWgNE?Dyx-UAxtGT%&%e)~=7+ zeC6N7hZu%!o@2%%pXM@OfLETiT@DU)6F##SuFZ8*B$Mq~p?$2q>SGvfAD3~AOCDxU z$2Aw^MQrBb90!aEKC!dhxies&83(ixbKZwIw0v6ST5<~L-UOJmOvaFg}gg%yF4#w2gy!^kp66A&%r<_AviTB@b$pGeQn>tR)BG zadufhbIO>agX{&^CS8Z zPxMa70qq&EY1nVMFV1jYvlq@dkP|T#n{hop*0Ju1$vV(BbL@FG;9J-iDhFyLhMd3Q z&O8UyC+8%mMd8Rn+PEJaXAXq}v1tuGf58NIa$=iYB?rKgS_C)s>|fPJ?y-+Jyu&3n z;^~=s{l=kH>1WOz;!;2J!#k}!D*nbxJoFGlv>`rm^=)B(ao>nvH^kE#_PRsQr2)=} zC1>vS+Zo9@G0#{_9O{y@2S2{Ka!-NFabh!uwCCV+zvR0GelT)TL;Zmp@3X!Es$Xh@ zILt@r&uGdu$bUwyo&(y3uYSIM$vF?d$nqFY#f- zG~+zmynp9%@6SQ&j_(&aYAnX1emQ@%saNt8B0kn}USiG^OXjiH-bT*ZePCPq*?fGh zY3lZ8$N121_!8UWE4I$(nXg~O)%=4S#te&|-x27|81rHdw)cT=5eh1<`mvQv@PL;I@UR*NZmss-NcwDPNV)J8M=HYXUwJ7;+ zRr$8OpYtv1R_%6odC!Pla>%sJT+~0D=e>FzhqC4y#2}ZtKab5ESMMy_6$fHn{BVpm z?=Bc8o)Oo`JJT|}%Ip6S-TDy*6tze=i`Tc=LF7ka?yyk?luk$euud9bkyvVoYaq90R zIou4}_!$R!(RPhI$hw)&1$6UlERMy!ski;~%*SiFoSw=&*V(?C;$5}g;$7EY73bo3 zapGKo!GB@@z+S;#!cM#$!_#j1ImXWpv=iqRX)m378?iqCMgv$aU>3#6V)3DKZVgrx z@4E+X+wHpVVLR>zc>C0U*Wj-i_&p;C`_b3SpEiSZ+=+XgK@vw>&7&x7wmyIF-OWy#M#IO0 zqW}TobgMZT#rMWGCrG-$b2J@NoOf;HUAV*H8R>B?L-VZhy9R zdu4rX;}d}E{)17{jnXt3ZI8N7`s1iOo{SK1sky2=deK1?gG2X7`e0@w<880kPh3fQ zK5>pJ`3IJpm0$XCFL|1->?Ffs5-;y0@pzOBQZI!&N;&tS27k4wgURDTzx!=;^eFi? z!pYsc``O*c_cqoyHoD!t_1<1>>7WzuPdfXeQ`5lMZ%767tGWCg*1 z16$2bx*H!NH=|l6f?(3$?y>^5ntPo=8u@c)J}9`1ehGYY^&HjB)lxIqZLZp7K*zcN E0H(SnhyVZp literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dpr" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dpr" new file mode 100644 index 0000000..df43e7c --- /dev/null +++ "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dpr" @@ -0,0 +1,15 @@ +program JSON_DataSet_Serialize_XE6; + +uses + Vcl.Forms, + Unit1 in 'Unit1.pas' {Form1}; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dproj" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dproj" new file mode 100644 index 0000000..408da97 --- /dev/null +++ "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.dproj" @@ -0,0 +1,149 @@ + + + {FBE7FC34-5581-4645-A4A3-71E9B2BF28A1} + 15.4 + VCL + JSON_DataSet_Serialize_XE6.dpr + True + Release + Win32 + 1 + Application + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + $(BDS)\bin\delphi_PROJECTICON.ico + JSON_DataSet_Serialize_XE6 + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + true + 1033 + FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;FireDACPgDriver;fmx;IndySystem;TeeDB;tethering;inetdbbde;vclib;DBXInterBaseDriver;DataSnapClient;DataSnapServer;DataSnapCommon;DataSnapProviderClient;DBXSybaseASEDriver;DbxCommonDriver;vclimg;dbxcds;DatasnapConnectorsFreePascal;MetropolisUILiveTile;vcldb;vcldsnap;fmxFireDAC;DBXDb2Driver;DBXOracleDriver;CustomIPTransport;vclribbon;dsnap;IndyIPServer;fmxase;vcl;IndyCore;DBXMSSQLDriver;IndyIPCommon;CloudService;FmxTeeUI;FireDACIBDriver;CodeSiteExpressPkg;DataSnapFireDAC;FireDACDBXDriver;soapserver;inetdbxpress;dsnapxml;FireDACInfxDriver;FireDACDb2Driver;adortl;FireDACASADriver;bindcompfmx;vcldbx;FireDACODBCDriver;RESTBackendComponents;rtl;dbrtl;DbxClientDriver;FireDACCommon;bindcomp;inetdb;Tee;DBXOdbcDriver;vclFireDAC;xmlrtl;DataSnapNativeClient;svnui;ibxpress;IndyProtocols;DBXMySQLDriver;FireDACCommonDriver;bindcompdbx;vclactnband;bindengine;soaprtl;FMXTee;TeeUI;bindcompvcl;vclie;FireDACADSDriver;vcltouch;VclSmp;FireDACMSSQLDriver;FireDAC;DBXInformixDriver;Intraweb;VCLRESTComponents;DataSnapConnectors;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;inet;fmxobj;FireDACMySQLDriver;soapmidas;vclx;svn;DBXSybaseASADriver;FireDACOracleDriver;fmxdae;RESTComponents;bdertl;FireDACMSAccDriver;dbexpress;DataSnapIndy10ServerTransport;IndyIPClient;$(DCC_UsePackage) + $(BDS)\bin\default_app.manifest + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;FireDACPgDriver;fmx;IndySystem;TeeDB;tethering;vclib;DBXInterBaseDriver;DataSnapClient;DataSnapServer;DataSnapCommon;DataSnapProviderClient;DBXSybaseASEDriver;DbxCommonDriver;vclimg;dbxcds;DatasnapConnectorsFreePascal;MetropolisUILiveTile;vcldb;vcldsnap;fmxFireDAC;DBXDb2Driver;DBXOracleDriver;CustomIPTransport;vclribbon;dsnap;IndyIPServer;fmxase;vcl;IndyCore;DBXMSSQLDriver;IndyIPCommon;CloudService;FmxTeeUI;FireDACIBDriver;DataSnapFireDAC;FireDACDBXDriver;soapserver;inetdbxpress;dsnapxml;FireDACInfxDriver;FireDACDb2Driver;adortl;FireDACASADriver;bindcompfmx;FireDACODBCDriver;RESTBackendComponents;rtl;dbrtl;DbxClientDriver;FireDACCommon;bindcomp;inetdb;Tee;DBXOdbcDriver;vclFireDAC;xmlrtl;DataSnapNativeClient;ibxpress;IndyProtocols;DBXMySQLDriver;FireDACCommonDriver;bindcompdbx;vclactnband;bindengine;soaprtl;FMXTee;TeeUI;bindcompvcl;vclie;FireDACADSDriver;vcltouch;VclSmp;FireDACMSSQLDriver;FireDAC;DBXInformixDriver;Intraweb;VCLRESTComponents;DataSnapConnectors;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;inet;fmxobj;FireDACMySQLDriver;soapmidas;vclx;DBXSybaseASADriver;FireDACOracleDriver;fmxdae;RESTComponents;FireDACMSAccDriver;dbexpress;DataSnapIndy10ServerTransport;IndyIPClient;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + ..\..\..\dcu + ..\..\..\bin + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + ..\..\..\bin + ..\..\..\dcu + true + 1033 + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + ..\..\..\dcu + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + true + 1033 + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + + + + MainSource + + +
Form1
+ dfm +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + JSON_DataSet_Serialize_XE6.dpr + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + True + False + + + 12 + + + +
diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.res" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/JSON_DataSet_Serialize_XE6.res" new file mode 100644 index 0000000000000000000000000000000000000000..d6cf6329cd078224d25f1d767092617dce629de1 GIT binary patch literal 62704 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"' + ' },' + ' {' + ' "FInterID": 22124,' + ' "FBillNo": "XSD20140722000447",' + ' "FDate": "2014-07-22T00:00:00",' + ' "FSupplyID": 36,' + ' "FMerchandizerID": 5,' + ' "FQCID": 5,' + ' "FDelDate": "2014-07-24T00:00:00",' + ' "FShipmentID": 0,' + ' "FDocumentoryID": 5,' + ' "FNote": "",' + ' "FFilePath": "",' + ' "FModifyTime": "1900-01-01T00:00:00",' + ' "FProgramID": 0,' + ' "FCreateUser": 1,' + ' "techDate": "2014-07-10T00:00:00",' + ' "loadingDate": "2014-07-24T00:00:00",' + ' "factoryDelDate": "2014-07-31T00:00:00",' + ' "FQty": 321,' + ' "Price": 17859,' + ' "FQCID2": 1,' + ' "FContact": "37",' + ' "FTranType": 0,' + ' "FPhone": "2",' + ' "FAddress": "12312",' + ' "FSupplierName": "321",' + ' "MerchandizerName": "13",' + ' "FQCName": "13",' + ' "FDocumentoryName": "13",' + ' "FShipmentName": null' + ' }' + ' ],' + ' "total": 2' + '}' + '') + ParentFont = False + ScrollBars = ssBoth + TabOrder = 0 + OnKeyDown = Memo1KeyDown + end + end + object TabSheet2: TTabSheet + Caption = 'QDBJson' + ImageIndex = 1 + ExplicitLeft = 0 + ExplicitTop = 0 + ExplicitWidth = 0 + ExplicitHeight = 0 + object Memo2: TMemo + Left = 0 + Top = 0 + Width = 1112 + Height = 367 + Align = alClient + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -12 + Font.Name = 'Courier New' + Font.Style = [] + HideSelection = False + ParentFont = False + ScrollBars = ssBoth + TabOrder = 0 + end + end + end + object Button10: TButton + Left = 462 + Top = 34 + Width = 107 + Height = 25 + Caption = #35835#21462'Json'#25991#20214 + TabOrder = 4 + OnClick = Button10Click + end + object ClientDataSet1: TClientDataSet + Aggregates = <> + Params = <> + Left = 280 + Top = 104 + end + object DataSource1: TDataSource + DataSet = ClientDataSet1 + Left = 312 + Top = 104 + end + object OpenDialog1: TOpenDialog + Filter = '*.json|*.json;*.txt|*.*|*.*' + Left = 280 + Top = 160 + end + object FindDialog1: TFindDialog + OnShow = FindDialog1Show + Options = [frDown, frHideMatchCase, frHideWholeWord, frHideUpDown] + OnFind = FindDialog1Find + Left = 312 + Top = 160 + end +end diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Unit1.pas" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Unit1.pas" new file mode 100644 index 0000000..78b3c00 --- /dev/null +++ "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Unit1.pas" @@ -0,0 +1,533 @@ +unit Unit1; + +interface + +uses + StrUtils, YxdAdoStream, + Windows, Messages, SysUtils, Variants, Classes, Graphics, + Controls, Forms, Dialogs, StdCtrls, DB, ADODB, ActiveX, Grids, + DBGrids, DBClient, ExtCtrls, ComCtrls; + +type + TForm1 = class(TForm) + ClientDataSet1: TClientDataSet; + DBGrid1: TDBGrid; + DataSource1: TDataSource; + Panel1: TPanel; + Button1: TButton; + Button2: TButton; + Button4: TButton; + Button3: TButton; + Label1: TLabel; + Edit1: TEdit; + Splitter1: TSplitter; + CheckBox1: TCheckBox; + Button6: TButton; + PageControl1: TPageControl; + TabSheet1: TTabSheet; + Memo1: TMemo; + TabSheet2: TTabSheet; + Memo2: TMemo; + Button7: TButton; + Button8: TButton; + Button9: TButton; + Button10: TButton; + OpenDialog1: TOpenDialog; + FindDialog1: TFindDialog; + procedure Button1Click(Sender: TObject); + procedure Button2Click(Sender: TObject); + procedure Button3Click(Sender: TObject); + procedure Button4Click(Sender: TObject); + procedure Button5Click(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure Button6Click(Sender: TObject); + procedure Button7Click(Sender: TObject); + procedure Button8Click(Sender: TObject); + procedure Button9Click(Sender: TObject); + procedure Button10Click(Sender: TObject); + procedure Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + procedure FindDialog1Show(Sender: TObject); + procedure FindDialog1Find(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + Form1: TForm1; +var + // λ + SoftPath: string; + +implementation + +{$R *.dfm} + + +uses YxdJson{$IFDEF UNICODE}, uQDBJson{$ENDIF}; + +type + TJsonArrayHelper = class helper for JSONArray + public + procedure ToDataSet(DataSet: TClientDataSet); + end; + + { TJsonArrayHelper } + +procedure TJsonArrayHelper.ToDataSet(DataSet: TClientDataSet); +var + ii, j: Integer; + lItem: JSONObject; + lValue: PJSONValue; + lList: TStringList; + lName: string; +begin + + with DataSet do + begin + FieldDefs.Clear; + Close; + + if self.count - 1 < 0 then + Exit; + + lList := TStringList.Create; + lItem := self.getJsonObject(0); + + for ii := 0 to lItem.count - 1 do + lList.Add(lItem.Items[ii].FName); // ֶ֪䵽List + + // ѭֱн + while lList.count - 1 >= 0 do + begin + + for ii := 0 to self.count - 1 do + begin + lItem := self.getJsonObject(ii); + for j := 0 to lItem.count - 1 do + begin + lValue := lItem.Items[j]; + + // ֶδھ + if FieldDefs.IndexOf(lValue.FName) >= 0 then + Continue; + + case lValue.FType of + jdtString: + FieldDefs.Add(lValue.FName, ftString, 200); + jdtInteger: + FieldDefs.Add(lValue.FName, ftInteger); + jdtFloat: + FieldDefs.Add(lValue.FName, ftFloat); + jdtBoolean: + FieldDefs.Add(lValue.FName, ftBoolean); + jdtDateTime: + FieldDefs.Add(lValue.FName, ftDateTime); + end; + + // ֪ʹӶƳ + if lValue.FType in [jdtString, jdtInteger, jdtFloat, jdtBoolean, jdtDateTime] then + begin + lList.Delete(lList.IndexOf(lValue.FName)); + end; + + end; + end; + end; + + // лֶ + for lName in lList do + begin + if Length(lName) > 0 then + FieldDefs.Add(lName, ftVariant); + end; + + + CreateDataSet; + for ii := 0 to self.count - 1 do + begin + lItem := self.getJsonObject(ii); + + Append; + + for j := 0 to lItem.count - 1 do + begin + lValue := lItem.Items[j]; + case lValue.FType of + jdtString: + FieldByName(lValue.FName).AsString := lValue.AsString; + jdtInteger: + FieldByName(lValue.FName).AsInteger := lValue.AsInteger; + jdtFloat: + FieldByName(lValue.FName).AsFloat := lValue.AsFloat; + jdtBoolean: + FieldByName(lValue.FName).AsBoolean := lValue.AsBoolean; + jdtDateTime: + FieldByName(lValue.FName).AsDateTime := lValue.AsDateTime; + jdtNull: + FieldByName(lValue.FName).AsVariant := Null; + end; + end; + + post; + end; + + lList.Free; + end; +end; + +procedure TForm1.Button10Click(Sender: TObject); +var + json: JSONObject; + I: Integer; + t: Cardinal; +begin + if OpenDialog1.Execute(Self.Handle) then begin + t := GetTickCount; + json := JSONObject.Create; + try + json.LoadFromFile(OpenDialog1.FileName); + Memo1.Text := json.ToString(4); + I := json.ToDataSet(ClientDataSet1); + finally + json.Free; + end; + t := GetTickCount - t; + ShowMessage(Format('%s'#13'ļسɹ, %d, ʱ %dms.', + [OpenDialog1.FileName, i, t])); + end; +end; + +procedure TForm1.Button1Click(Sender: TObject); +var + lObject: JSONObject; + lArray: JSONArray; +begin + lObject := JSONObject.parseObject(Memo1.Text); + lArray := lObject.getJsonArray('rows'); + lArray.ToDataSet(ClientDataSet1); + lObject.Free; +end; + +procedure TForm1.Button2Click(Sender: TObject); +var + lObject: JSONObject; + t: Cardinal; +begin + lObject := JSONObject.ParseObject(Memo1.Text); + t := GetTickCount; + lObject.ToDataSet(ClientDataSet1); + t := GetTickCount - t; + lObject.Free; + ShowMessage('ToDataSet ' + IntToStr(t) + 'ms.'); +end; + +procedure TForm1.Button3Click(Sender: TObject); +var + lObject: JSONObject; +begin + lObject := JSONObject.Create; + //lObject.PutDataSet('', ClientDataSet1); + lObject.PutDataSet('', ClientDataSet1, 0, 0, CheckBox1.Checked); + Memo1.Text := lObject.ToString(4); + lObject.Free; +end; + +var + Conn: TADOConnection = nil; //ݿ +function GetLinkStr(const dbFile: string): string; +begin + Result := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+dbFile+';Persist Security Info=False'; +end; + +procedure UnitADO(var Rs: TADOQuery); +begin + try + if Rs <> nil then begin + Rs.Close; FreeAndNil(Rs); + end; + except + end; + FreeAndNil(Conn); +end; + +procedure InitADO(var Rs: TADOQuery); +begin + UnitADO(Rs); + CoInitialize(Form1); + Conn := TADOConnection.Create(Form1); + Conn.ConnectionString := GetLinkStr(Form1.Edit1.Text); + Conn.Open; + Rs := TADOQuery.Create(Form1); + Rs.Connection := Conn; +end; + +procedure TForm1.Button4Click(Sender: TObject); +var + Rs: TADOQuery; + JSON: JSONObject; + t: Cardinal; +begin + try + InitADO(Rs); + t := GetTickCount; + JSON := JSONObject.Create; + Rs.SQL.Text := 'Select * from TBApp'; + Rs.Open; + Json.PutDataSet('', Rs, 0, 0, CheckBox1.Checked); + t := GetTickCount - t; + Memo1.Text := Json.ToString(4); + ShowMessage('PutDataSet ' + IntToStr(t) + 'ms.'); + finally + Rs.SaveToFile(SoftPath + 'tbapp_ado.xml', pfXML); + UnitADO(Rs); + FreeAndNil(JSON); + end; +end; + +procedure TForm1.Button5Click(Sender: TObject); +begin + if not ClientDataSet1.Active then begin + ShowMessage('ȼ, ToDataSet л.'); + Exit; + end; + if ClientDataSet1.RecordCount > 0 then begin + TBlobField(ClientDataSet1.FieldByName('Data')).SavetoFile(SoftPath + + ClientDataSet1.FieldByName('Name').AsString); + ShowMessage('Ѿļ' + SoftPath + + ClientDataSet1.FieldByName('Name').AsString); + end; +end; + +procedure TForm1.Button6Click(Sender: TObject); +var + Rs: TADOQuery; + JSON: JSONObject; + {$IFDEF UNICODE}QDBJson: TQDBJson; {$ENDIF} + t, t1: Cardinal; + s{$IFDEF UNICODE},s1{$ENDIF}: string; +begin + try + InitADO(Rs); + Rs.SQL.Text := 'Select * from TBApp'; + Rs.Open; + Rs.First; + + try + t := GetTickCount; + JSON := JSONObject.Create; + Json.PutDataSet('', Rs, 0, 0, CheckBox1.Checked); + s := Json.ToString(4); + t := GetTickCount - t; + finally + FreeAndNil(JSON); + end; + + {$IFDEF UNICODE} + Rs.First; + try + t1 := GetTickCount; + QDBJson := TQDBJson.Create; + QDBJson.DataSet2Json(Rs, True, True, False, CheckBox1.Checked, 0, 0, [], True); + s1 := QDBJson.ToString; + t1 := GetTickCount - t1; + finally + FreeAndNil(QDBJson); + end; + {$ENDIF} + + Memo1.Text := s; + {$IFDEF UNICODE} + Memo2.Text := s1; + {$ELSE} + t1 := 0; + {$ENDIF} + ShowMessage(Format('лԱȣ'#13'YxdJson %dms.'#13'QDBJson %dms.', [t, t1])); + finally + UnitADO(Rs); + end; +end; + +procedure TForm1.Button7Click(Sender: TObject); +{$IFDEF UNICODE} +var + lObject: TQDBJson; + t: Cardinal; +begin + t := GetTickCount; + TQDBJson.Json2DataSet(ClientDataSet1, Memo1.Text, nil); + t := GetTickCount - t; + lObject.Free; + ShowMessage('TQDBJson.Json2DataSet ' + IntToStr(t) + 'ms.'); +{$ELSE} +begin +{$ENDIF} +end; + +procedure TForm1.Button8Click(Sender: TObject); +var + Rs: TADOQuery; + JSON: JSONObject; + M: TMemoryStream; + t, t1, t2: Cardinal; + s, s1: string; + i: Integer; +begin + try + InitADO(Rs); + Rs.SQL.Text := 'Select * from TBApp'; + Rs.Open; + Rs.First; + + try + t := GetTickCount; + JSON := JSONObject.Create; + for i := 0 to 100 do begin + json.Clear; + Json.PutDataSet('', Rs, 0, 0, CheckBox1.Checked); + end; + json.SaveToFile(SoftPath + 'tbapp.json'); + t := GetTickCount - t; + s := Json.ToString(4); + JSON.Parse(S); + JSON.ToDataSet(ClientDataSet1); + finally + FreeAndNil(JSON); + end; + + Rs.First; + try + t1 := GetTickCount; + M := TMemoryStream.Create; + for i := 0 to 100 do begin + M.Clear; + YxdAdoStream.DataSetToStream(Rs, M); + end; + m.SaveToFile(SoftPath + 'tbapp.data'); + t1 := GetTickCount - t1; + finally + FreeAndNil(M); + end; + + ClientDataSet1.First; + t2 := GetTickCount; + for i := 0 to 100 do begin + s1 := ''; + s1 := ClientDataSet1.XMLData; + end; + ClientDataSet1.SaveToFile(SoftPath + 'tbapp.xml', dfXML); + t2 := GetTickCount - t2; + + + Memo1.Text := s; + Memo2.Text := s1; + ShowMessage(Format('лԱȣ'#13'YxdJson %dms.'#13'AdoStream %dms.'#13+ + 'ClientDataSet1.XMLData %dms.', [t, t1, t2])); + finally + UnitADO(Rs); + end; +end; + +procedure TForm1.Button9Click(Sender: TObject); +var + Rs: TADOQuery; + JSON: JSONObject; + M: TMemoryStream; + t, t1, t2: Cardinal; + s, s1: string; + i: Integer; +begin + DBGrid1.Visible := False; + try + try + InitADO(Rs); + Rs.SQL.Text := 'Select * from TBApp'; + Rs.Open; + Rs.First; + + try + JSON := JSONObject.Create; + Json.PutDataSet('', Rs, 0, 0, CheckBox1.Checked); + s := Json.ToString; + t := GetTickCount; + for i := 1 to 100 do begin + json.ToDataSet(ClientDataSet1); + end; + t := GetTickCount - t; + finally + FreeAndNil(JSON); + end; + + M := TMemoryStream.Create; + YxdAdoStream.DataSetToStream(Rs, M); + + finally + UnitADO(Rs); + end; + + Rs := TADOQuery.Create(Self); + try + t1 := GetTickCount; + for i := 1 to 100 do begin + M.Position := 0; + YxdAdoStream.StreamToDataSet(M, Rs); + end; + t1 := GetTickCount - t1; + finally + FreeAndNil(M); + Rs.Free; + end; + + ClientDataSet1.First; + s1 := ClientDataSet1.XMLData; + t2 := GetTickCount; + ClientDataSet1.DisableControls; + for i := 1 to 100 do begin + ClientDataSet1.XMLData := s1; + end; + ClientDataSet1.EnableControls; + t2 := GetTickCount - t2; + ClientDataSet1.SaveToFile(SoftPath + 'tbapp.xml', dfXML); + + ShowMessage(Format('лԱȣ'#13'YxdJson %dms.'#13'AdoStream %dms.'#13+ + 'ClientDataSet1.XMLData %dms.', [t, t1, t2])); + finally + DBGrid1.Visible := True; + end; +end; + +procedure TForm1.FindDialog1Find(Sender: TObject); +var + I: Integer; +begin + I := PosEx(FindDialog1.FindText, Memo1.Text, Memo1.SelStart + 2); + if I > 0 then begin + Memo1.SelStart := I - 1; + Memo1.SelLength := Length(FindDialog1.FindText); + end; +end; + +procedure TForm1.FindDialog1Show(Sender: TObject); +begin + FindDialog1.FindText := Memo1.SelText; +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin + Edit1.Text := SoftPath + 'db2.mdb'; + OpenDialog1.InitialDir := SoftPath; +end; + +procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (ssCtrl in Shift) and (Key = Ord('F')) then begin + FindDialog1.Execute(Self.Handle); + end; +end; + +initialization + SoftPath := ExtractFilePath(ParamStr(0)); + +end. diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/uQDBJson.pas" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/uQDBJson.pas" new file mode 100644 index 0000000..d97faa4 --- /dev/null +++ "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/uQDBJson.pas" @@ -0,0 +1,1027 @@ +{*******************************************************} +{ } +{ QJSONݼת } +{ } +{ Ȩ (C) 2014 ˮ } +{ ߣֺ QQ11241450 } +{ QJSONȨ QDACQQȺΪ:250530692 } +{ } +{ V1.0.1 - 2014.07.01 } +{*******************************************************} + +unit uQDBJson; + +interface + +uses SysUtils, classes, Variants, DB, Qjson; + +type + + TQDBJson = class + private + class function ISOStr2DateTime(DateStr: string): TDateTime; + class function Variant2Bytes(V: Variant): TBytes; + class function Bytes2Variant(Bytes: TBytes): Variant; + public + class constructor Create; + // ڴתַ + class function MemStream2Str(MemStream: TMemoryStream): string; static; + // ַת + class procedure Str2MemStream(StrValue: string; + MemStream: TMemoryStream); static; + + // json blob ת + class function JSONToStream(const Data: TQJson): TStream; static; + // json blob ת bytes + class function JSONToBytes(const Data: TQJson): TBytes; static; + // תjson + class function StreamToJSON(Stream: TStream; const Offset: Integer; + const ByteCount: Integer): TQJson; static; + // bytes תjson + class function BytesToJSON(const Bytes: TBytes; const Offset: Integer; + const ByteCount: Integer): TQJson; static; + // ݼתjson + class function DataSet2Json(DataSet: TDataSet; + const ShowMeata, ShowData, RowArray, StreamEncoded: Boolean; + const PageIndex, PageSize: Integer; const ArgsFields: Array of string; + const Included: Boolean): TQJson; static; + // תjson + class function DataSetRow2Json(DataSet: TDataSet; + const StreamEncoded: Boolean; JsonStream: TStringStream; + BlobStream: TMemoryStream; const ArgsFields: Array of string; + const Included: Boolean): TQJson; static; + // ¼ תjson + class function DataSetRow2JsonArray(DataSet: TDataSet; + const StreamEncoded: Boolean; JsonStream: TStringStream; + BlobStream: TMemoryStream; const ArgsFields: Array of string; + const Included: Boolean): TQJson; static; // array + // json תݼ + class function Json2DataSet(DataSet: TDataSet; const jsonStr: string; + DoDataSet: TProc): Integer; static; + + // Уʽ + class function CheckJsonValue(const jsonValue, jsonFmt: string; + out Info: string): Boolean; static; + // Params תjson + class function Params2Json(Params: TParams; + const OnlyOutput, RowArray, StreamEncoded: Boolean): TQJson; static; + end; + +implementation + +uses System.StrUtils, Soap.EncdDecd, System.Math, System.DateUtils, System.Rtti; + +type + TJsonDBOpts = record + ShowMeata, ShowData, RowArray, StreamEncoded: Boolean; + end; + +function IncrAfter(var Arg: Integer): Integer; +begin + Result := Arg; + Inc(Arg); +end; + +class function TQDBJson.Bytes2Variant(Bytes: TBytes): Variant; +var + p: pointer; +begin + Result := VarArrayCreate([0, High(Bytes) - Low(Bytes)], varByte); + p := VarArrayLock(Result); + try + if Length(Bytes) > 0 then + Move(Bytes[0], p^, Length(Bytes)); + finally + VarArrayUnlock(Result); + end; +end; + +class function TQDBJson.BytesToJSON(const Bytes: TBytes; + const Offset, ByteCount: Integer): TQJson; +var + i, NewByteCount: Integer; + Value: TValue; +begin + Result := TQJson.Create; + if Length(Bytes) = 0 then + Exit(); + + Value := TValue.From(Bytes); + Result.FromRtti(Value); +end; + +class function TQDBJson.CheckJsonValue(const jsonValue, jsonFmt: string; + out Info: string): Boolean; +var + jsonUrlData, jsonFmtParam: TQJson; + jsonCheckItem: TQJson; +begin + Result := True; + Info := ''; + + if not jsonValue.IsEmpty and not jsonFmt.IsEmpty then + begin + // URLݸʽ + jsonUrlData := TQJson.Create(); + jsonFmtParam := TQJson.Create(); + try + jsonUrlData.Parse(jsonValue); + // + + jsonFmtParam.Parse(jsonFmt); + + for jsonCheckItem in jsonFmtParam do + begin + if jsonUrlData.ItemByName(jsonCheckItem.Name) = nil then + begin + Info := Format('ָ json (%s)Ҳ', [jsonCheckItem.Name]); + Exit(False); + end + else + begin + // jsonUrlData.O[jsonCheckItem.Name].Self.DataType + case jsonUrlData.ItemByName(jsonCheckItem.Name).DataType of + // case jsonUrlData.Ancestor[jsonCheckItem.Name].DataType of + jdtNull: // ֵУ + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('NULL'); + if not Result then + Info := Format('ָ json (%s)Ϊֵ(NULL)', + [jsonCheckItem.Name]); + end; + jdtBoolean: // У + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('BOOLEAN'); + if not Result then + Info := Format('ָ json (%s)Ϊ(Boolean)', + [jsonCheckItem.Name]); + end; + jdtInteger: // У + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('INT'); + if not Result then + Info := Format('ָ json (%s)Ϊ(Integer)', + [jsonCheckItem.Name]); + end; + jdtFloat: // У + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('DOUBLE') or + jsonCheckItem.AsString.ToUpper.Equals('FLOAT'); + If not Result then + Info := Format('ָ json (%s)Ϊ(Double)', + [jsonCheckItem.Name]); + end; + jdtArray: + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('ARRAY'); + If not Result then + Info := Format('ָ json (%s)Ϊ(Array)', + [jsonCheckItem.Name]); + end; + jdtDateTime: + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('DATETIME'); + If not Result then + Info := Format('ָ json (%s)Ϊʱ(DATETIME)', + [jsonCheckItem.Name]); + end; + jdtString: // У ַ + begin + // ַп + // if CheckDate(jsonUrlData.S[jsonCheckItem.Name]) then + // begin + // Result := jsonCheckItem.AsString.ToUpper.Equals('DATETIME'); + // If not Result then + // Info := Format('ָ json (%s)Ϊʱ(DateTime)', + // [jsonCheckItem.Name]); + // end + // else + if jsonUrlData.ItemByName(jsonCheckItem.Name) + .AsString.StartsWith('[blob]<') and + jsonUrlData.ItemByName(jsonCheckItem.Name) + .AsString.EndsWith('>') then + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('BLOB'); + If not Result then + Info := Format('ָ json (%s)Ϊ BLOB (BLOB)', + [jsonCheckItem.Name]); + end + else + begin + Result := jsonCheckItem.AsString.ToUpper.Equals('STRING'); + If not Result then + Info := Format('ָ json (%s)Ϊ ַ (STRING)', + [jsonCheckItem.Name]); + end; + end; + end; + end; + if not Result then + Break; + end; // end of for... + finally + jsonUrlData.Free; + jsonFmtParam.Free; + end; + + end; +end; + +class constructor TQDBJson.Create; +begin + JsonCaseSensitive := False; // Сд +end; + +class function TQDBJson.DataSet2Json(DataSet: TDataSet; + const ShowMeata, ShowData, RowArray, StreamEncoded: Boolean; + const PageIndex, PageSize: Integer; const ArgsFields: Array of string; + const Included: Boolean): TQJson; +// Ԫݰ + function JsonValue4Field(Field: TField): TQJson; + begin + Result := TQJson.Create; + Result.DataType := jdtArray; + + Result.Add.AsString := Field.FieldName; + Result.Add.AsInteger := Ord(Field.DataType); + Result.Add.AsInteger := Field.Size; + Result.Add.AsBoolean := Field.Required; + Result.Add.AsString := Field.DisplayLabel; + Result.Add.AsString := Field.ClassName; + + end; + +var + Meta: TQJson; + Field: TField; + // BM: TBookmark; + JsonStream: TStringStream; + BlobStream: TMemoryStream; + MoveIndex, StepIndex: Integer; + Opts: TJsonDBOpts; +begin + Result := TQJson.Create; + + if not Assigned(DataSet) or not DataSet.Active then + Exit; + + // Ԫ + if ShowMeata then + begin + Result.Add('meta').DataType := jdtArray; + Result.Add('field').DataType := jdtArray; + + for Field in DataSet.Fields do + begin + Result.ItemByName('meta').Add(JsonValue4Field(Field)); + Result.ItemByName('field').Add.AsString := Field.FieldName; + end; + end; + Result.Add('opts').DataType := jdtObject; // ѡ + Opts.ShowMeata := ShowMeata; + Opts.ShowData := ShowData; + Opts.RowArray := RowArray; + Opts.StreamEncoded := StreamEncoded; + Result.ItemByName('opts').FromRecord(Opts); + + // raise Exception.Create(Result.AsJson); + + JsonStream := TStringStream.Create(); + BlobStream := TMemoryStream.Create; + // ӻ + DataSet.DisableControls; + try + // BM := DataSet.GetBookmark; + if ShowData then + begin + MoveIndex := 0; + Result.Add('data').DataType := jdtArray; + DataSet.First; + // ҳƶ¼ + if (PageIndex > 0) and (PageSize > 0) then + begin + MoveIndex := (PageIndex - 1) * PageSize; + DataSet.MoveBy(MoveIndex); + end; + StepIndex := 0; + while not DataSet.Eof do + begin + // ǰ + if RowArray then + Result.ItemByName('data') + .Add(DataSetRow2JsonArray(DataSet, StreamEncoded, JsonStream, + BlobStream, ArgsFields, Included)) + // Result.A['data'].Add(DataSetRow2JsonArray(DataSet, StreamEncoded, + // JsonStream, BlobStream, ArgsFields, Included)) + else + Result.ItemByName('data').Add(DataSetRow2Json(DataSet, StreamEncoded, + JsonStream, BlobStream, ArgsFields, Included)); + // + // Result.A['data'].Add(DataSetRow2Json(DataSet, StreamEncoded, + // JsonStream, BlobStream, ArgsFields, Included)); + + if (PageSize > 0) then + begin + Inc(StepIndex); + if StepIndex >= PageSize then + Break + else + begin + DataSet.Next; + Continue; + end; + end + else + DataSet.Next; + end; + end; + // DataSet.GotoBookmark(BM); + finally + JsonStream.Free; + BlobStream.Free; + // DataSet.FreeBookmark(BM); + DataSet.EnableControls; + end; +end; + +class function TQDBJson.DataSetRow2Json(DataSet: TDataSet; + const StreamEncoded: Boolean; JsonStream: TStringStream; + BlobStream: TMemoryStream; const ArgsFields: Array of string; + const Included: Boolean): TQJson; + +// ж鷶Χ + function CheckArrayExists(const Args: array of string; + const CheckName: string; const Included: Boolean): Boolean; + var + argsSize: Integer; + begin + Result := False; + argsSize := Length(Args); + // + Result := (Included and ((argsSize = 0) or (IndexText(CheckName, + Args) <> -1))) or + // + (not Included and ((argsSize = 0) or (IndexText(CheckName, Args) = -1))); + end; + +var + Field: TField; + ja: TQJson; + JsonStreamCreated, BlobStreamCreated: Boolean; +begin + // бΪģʽ + Result := TQJson.Create; + // Ƶ + JsonStreamCreated := False; + BlobStreamCreated := False; + if not Assigned(JsonStream) then + begin + JsonStream := TStringStream.Create(); + JsonStreamCreated := True; + end; + + if not Assigned(BlobStream) then + begin + BlobStream := TMemoryStream.Create; + BlobStreamCreated := True; + end; + + try + + for Field in DataSet.Fields do + begin + // жֶǷҪ + if not CheckArrayExists(ArgsFields, Field.FieldName, Included) then + Continue; + + if Field.IsNull then + Result.Add(Field.FieldName, null, jdtNull) + else + begin + case Field.DataType of + ftBoolean: + Result.Add(Field.FieldName).AsBoolean := Field.AsBoolean; + ftDate, ftTime, ftDateTime, ftTimeStamp: + Result.Add(Field.FieldName).AsDateTime := Field.AsDateTime; + ftInteger, ftAutoInc, ftWord, ftSmallint, ftShortint: + Result.Add(Field.FieldName).AsInteger := Field.AsInteger; + ftLargeint: + Result.Add(Field.FieldName).AsInt64 := Field.AsLargeInt; + ftFloat, ftSingle, ftBCD, ftCurrency: + Result.Add(Field.FieldName).AsFloat := Field.AsFloat; + ftString, ftWideString, ftGuid: + Result.Add(Field.FieldName).AsString := Field.AsString; + ftBlob, ftGraphic, ftMemo, ftTypedBinary: + begin + if not StreamEncoded then + begin + Result.ItemByName(Field.FieldName) + .Add(BytesToJSON(TBlobField(Field).Value, 0, 0)); + end + else // BASE64 ԽʡֽڴС + begin + JsonStream.Clear; + BlobStream.Clear; + TBlobField(Field).SaveToStream(BlobStream); + // JsonStream.Position :=0; + BlobStream.Position := 0; + EncodeStream(BlobStream, JsonStream); + Result.ItemByName(Field.FieldName).AsString := '[blob]<' + + JsonStream.DataString + '>'; + JsonStream.Clear; + BlobStream.Clear; + end; + end; + + else + Result.Add(Field.FieldName).AsString := Field.AsString; + end; + end; + end; + finally + if Assigned(JsonStream) and JsonStreamCreated then + JsonStream.Free; + + if Assigned(BlobStream) and BlobStreamCreated then + BlobStream.Free; + end; +end; + +class function TQDBJson.DataSetRow2JsonArray(DataSet: TDataSet; + const StreamEncoded: Boolean; JsonStream: TStringStream; + BlobStream: TMemoryStream; const ArgsFields: array of string; + const Included: Boolean): TQJson; +// ж鷶Χ + function CheckArrayExists(const Args: array of string; + const CheckName: string; const Included: Boolean): Boolean; + var + argsSize: Integer; + begin + Result := False; + argsSize := Length(Args); + // + Result := (Included and ((argsSize = 0) or (IndexText(CheckName, + Args) <> -1))) or + // + (not Included and ((argsSize = 0) or (IndexText(CheckName, Args) = -1))); + end; + +var + Field: TField; + JsonStreamCreated, BlobStreamCreated: Boolean; +begin + // бΪģʽ + Result := TQJson.Create; + Result.DataType := jdtArray; + // Ƶ + JsonStreamCreated := False; + BlobStreamCreated := False; + if not Assigned(JsonStream) then + begin + JsonStream := TStringStream.Create(); + JsonStreamCreated := True; + end; + + if not Assigned(BlobStream) then + begin + BlobStream := TMemoryStream.Create; + BlobStreamCreated := True; + end; + + try + for Field in DataSet.Fields do + begin + // жֶǷҪ + if not CheckArrayExists(ArgsFields, Field.FieldName, Included) then + Continue; + + if Field.IsNull then + Result.Add(null) + else + begin + case Field.DataType of + ftBoolean: + Result.Add.AsBoolean := Field.AsBoolean; + ftDate, ftTime, ftDateTime, ftTimeStamp, ftTimeStampOffset: + Result.Add.AsDateTime := Field.AsDateTime; + ftInteger, ftAutoInc, ftWord, ftSmallint, ftShortint: + Result.Add.AsInteger := Field.AsInteger; + ftLargeint: + Result.Add.AsInt64 := Field.AsLargeInt; + ftFloat, ftSingle, ftBCD: + Result.Add.AsFloat := Field.AsFloat; + ftCurrency: + Result.Add.AsFloat := Field.AsCurrency; + ftString, ftWideString, ftGuid: + Result.Add.AsString := Field.AsString; + ftBlob, ftGraphic, ftMemo, ftTypedBinary: + begin + if not StreamEncoded then + begin + Result.Add(BytesToJSON(TBlobField(Field).Value, 0, 0)); + end + else // BASE64 ԽʡֽڴС + begin + JsonStream.Clear; + BlobStream.Clear; + TBlobField(Field).SaveToStream(BlobStream); + // JsonStream.Position :=0; + BlobStream.Position := 0; + EncodeStream(BlobStream, JsonStream); + Result.Add.AsString := '[blob]<' + JsonStream.DataString + '>'; + JsonStream.Clear; + BlobStream.Clear; + end; + end; + + else + Result.Add(Field.AsString); + end; + end; + end; + finally + if Assigned(JsonStream) and JsonStreamCreated then + JsonStream.Free; + + if Assigned(BlobStream) and BlobStreamCreated then + BlobStream.Free; + end; +end; + +class function TQDBJson.ISOStr2DateTime(DateStr: string): TDateTime; +var + y, m, D, hh, mm, ss, ms: Word; + s2: string; + A: Integer; + function GetNum(const sep: string): Word; + begin + if DateStr = '' then + Result := 0 + else if sep = '' then + begin + Result := StrToInt(DateStr); + DateStr := ''; + end + else + begin + A := Pos(sep, DateStr); + if A <= 0 then + A := Length(DateStr) + 1; + try + Result := StrToInt(Copy(DateStr, 1, A - 1)); + except + raise EConvertError.Create('Invalid DateTime format.'); + end; + Delete(DateStr, 1, A); + DateStr := Trim(DateStr); + end; + end; + +begin + try + Result := 0; + A := Pos('T', DateStr); + if (A > 0) or (Pos(':', DateStr) < Low(DateStr)) then + // date included or time not included + begin + if Pos('-', DateStr) > 0 then + begin + y := GetNum('-'); + m := GetNum('-'); + D := GetNum('T'); + end + else + begin + if A > 0 then + begin + s2 := Copy(DateStr, 1, A - 1); + Delete(DateStr, 1, A); + end + else + begin + s2 := DateStr; + DateStr := ''; + end; + if Length(s2) >= 4 then + begin + y := StrToInt(Copy(s2, 1, 4)); + Delete(s2, 1, 4); + end + else + y := 0; + if Length(s2) >= 2 then + begin + m := StrToInt(Copy(s2, 1, 2)); + Delete(s2, 1, 2); + end + else + m := 0; + if Length(s2) >= 2 then + begin + D := StrToInt(Copy(s2, 1, 2)); + Delete(s2, 1, 2); + end + else + D := 0; + end; + + if (y > 0) or (m > 0) or (D > 0) then + Result := EncodeDate(y, m, D); + + if Length(s2) > 0 then + raise EConvertError.Create('Date Part too long.'); + end; + + if Length(DateStr) > 0 then // time included + begin + hh := GetNum(':'); + mm := GetNum(':'); + ss := GetNum('.'); + ms := GetNum('+'); + if (hh > 0) or (mm > 0) or (ss > 0) or (ms > 0) then + if Result >= 0 then + Result := Result + EncodeTime(hh, mm, ss, ms) + else + Result := Result - EncodeTime(hh, mm, ss, ms); + end; + except + on E: Exception do + raise EConvertError.Create(E.Message + #13#10'Invalid DateTime format.'); + end; +end; + +class function TQDBJson.Json2DataSet(DataSet: TDataSet; + const jsonStr: string; DoDataSet: TProc): Integer; + + function JsonValue2Var(json: TQJson; + JsonStream, BlobStream: TMemoryStream): Variant; + var + dt: TDateTime; + JsonStreamCreated, BlobStreamCreated: Boolean; + jsonBlobStr: string; + Args: TBytes; + begin + Result := null; + case json.DataType of + jdtNull: + Result := null; + jdtBoolean: + Result := json.AsBoolean; + jdtInteger: + Result := json.AsInteger; + jdtFloat: + Result := json.AsFloat; + jdtDateTime: + Result := json.AsDateTime; + jdtString: + begin + if json.AsString.StartsWith('[blob]<') And json.AsString.EndsWith('>') + then + begin + // Ƶ + try + JsonStreamCreated := False; + BlobStreamCreated := False; + if not Assigned(JsonStream) then // п base64 + begin + JsonStream := TStringStream.Create(); + JsonStreamCreated := True; + end; + + if not Assigned(BlobStream) then + begin + BlobStream := TMemoryStream.Create; + BlobStreamCreated := True; + end; + JsonStream.Clear; + BlobStream.Clear; + // ȥͷβ + jsonBlobStr := json.AsString.Substring(7, + json.AsString.Length - 8); + JsonStream.Write(jsonBlobStr[Low(jsonBlobStr)], + Length(jsonBlobStr) * SizeOf(Char)); + JsonStream.Position := 0; + + DecodeStream(JsonStream, BlobStream); + BlobStream.Position := 0; + + SetLength(Args, BlobStream.Size); + BlobStream.ReadBuffer(Args, 0, BlobStream.Size); + Result := Args; + finally + if Assigned(JsonStream) and JsonStreamCreated then + JsonStream.Free; + + if Assigned(BlobStream) and BlobStreamCreated then + BlobStream.Free; + end; + end + else + Result := json.AsString; + end; + jdtArray: + begin + // Result := TJsonHelper.JSONToBytes(json.AsArray) + Result := TQDBJson.JSONToBytes(json) + end; + end; + end; + +var + i: Integer; + json, FieldData: TQJson; + Meta, Data: TQJson; // + Item, json2: TQJson; + Field: TField; + JsonStream, BlobStream: TMemoryStream; + FldName: string; + Opts: TJsonDBOpts; +begin + Result := -1; + if jsonStr.Trim.IsEmpty then + Exit; + + JsonStream := TMemoryStream.Create; + BlobStream := TMemoryStream.Create; + try + DataSet.DisableControls; + DataSet.Close; + DataSet.FieldDefs.Clear; + + json := TQJson.Create; + json.Parse(jsonStr.Trim); + // or (json.N['meta'].DataType = stNull) + if (json.ItemByName('meta') = nil) or + (json.ItemByName('meta').DataType <> jdtArray) then + Exit; + + try + Meta := json.ItemByName('meta'); + if not Assigned(Meta) or (Meta.DataType<>jdtArray) then + raise Exception.Create('ֶԪϢΪջJsonͣ'); + + for json2 in Meta do + begin + //json2.Items[0].AsString + DataSet.FieldDefs.Add(json2.Items[0].AsString, + TFieldType(json2.Items[1].AsInteger), json2.Items[2].AsInteger, + json2.Items[3].AsBoolean); + end; + // DataSet.FieldDefs.Update; + + if Assigned(DoDataSet) then + DoDataSet; + + if not DataSet.Active then + DataSet.Open; + + Meta := json.ItemByName('field'); + + Data := json.ItemByName('data'); + if json.ItemByName('opts') <> nil then + json.ItemByName('opts').ToRtti(@Opts, TypeInfo(TJsonDBOpts)); + + if not Assigned(Data) or (Data.DataType <> jdtArray) then + Exit; + + // Data ѭݣһ + for json2 in Data do + begin + FldName := ''; + // ģʽ + if json2.DataType = jdtArray then + begin + DataSet.Append; + i := 0; + for Item in json2 do + begin + FldName := DataSet.Fields[i].FieldName; + DataSet.Fields[i].Value := JsonValue2Var(Item, JsonStream, + BlobStream); + Inc(i); + end; + DataSet.Post; + end // ģʽ + else if json2.DataType = jdtObject then + begin + for Item in json2 do + begin + if DataSet.FindField(Item.Name) = nil then + Continue; + FldName := Item.Name; + DataSet.FieldByName(Item.Name).Value := + JsonValue2Var(Item, JsonStream, BlobStream); + end; + end; + end; + except + raise Exception.CreateFmt('jsonֶ(%s)ֵݼ쳣', [FldName]); + end; + + finally + JsonStream.Free; + BlobStream.Free; + if DataSet.Active then + DataSet.First; + + json.Free; + DataSet.EnableControls; + end; +end; + +class function TQDBJson.JSONToBytes(const Data: TQJson): TBytes; +var + i: Integer; + ByteVal: Integer; + Member: TQJson; + // Value:TValue; +begin + SetLength(Result, 0); + if not Assigned(Data) or (Data.DataType <> jdtArray) then + Exit; + SetLength(Result, Data.Count); + // Value := Data.ToRttiValue; + // if not Value.IsEmpty then + // Result := Value.AsType; + i := 0; + for Member in Data do + begin + if (Member.DataType = jdtInteger) and (Member.AsInteger >= 0) and + (Member.AsInteger <= 255) then + begin + ByteVal := Member.AsInteger; + Result[i] := Byte(ByteVal); + end + else + Result[i] := 0; + + Inc(i); + // raise Exception.Create('Cannot convert JSON input into a stream'); + end; +end; + +class function TQDBJson.JSONToStream(const Data: TQJson): TStream; +var + Bytes: TArray; +begin + Result := nil; + if Assigned(Data) and (Data.DataType = jdtArray) then + begin + // TSuperArray.Create(jo.Self as TJSONArray); + Bytes := JSONToBytes(Data); + Result := TBytesStream.Create(Bytes); + end; +end; + +class function TQDBJson.MemStream2Str(MemStream: TMemoryStream): string; +var + StrSteam: TStringStream; +begin + StrSteam := TStringStream.Create('', TEncoding.UTF8); + try + MemStream.SaveToStream(StrSteam); + Result := StrSteam.DataString; + // result := EncodeString(StrSteam.DataString); + finally + StrSteam.Free; + end; +end; + +class function TQDBJson.Params2Json(Params: TParams; + const OnlyOutput, RowArray, StreamEncoded: Boolean): TQJson; + + procedure Var2Json(json: TQJson; const KeyName: string; const Value: Variant); + var + Dynarray: TArray; + Vaue: TValue; + begin + if not Assigned(json) then + Exit; + + if json.DataType = jdtArray then + begin + case VarType(Value) of + varEmpty, varNull: + json.Add(null); + varBoolean: + json.Add.AsBoolean := Boolean(Value); + varDate: + json.Add.AsDateTime := VarToDateTime(Value); + varSmallInt, varInteger, varShortInt, varByte, varWord, varLongWord, + varInt64, varUInt64: + json.Add.AsInt64 := Value; + varSingle, varDouble, varCurrency: + json.Add.AsFloat := Value; + varOleStr, varString, varUString: + json.Add.AsString := Value; + (varByte or varArray): + begin Vaue := + TValue.From(Variant2Bytes(Value)); + json.Add.FromRtti(Vaue); + end; + end; + end + else if json.DataType = jdtObject then + begin + case VarType(Value)of + varEmpty, varNull: + json.Add(KeyName).AsVariant := null; + varBoolean: + json.Add(KeyName).AsBoolean := Boolean(Value); + varDate: + json.Add(KeyName).AsDateTime := Value; + // FormatDateTime('yyyy-MM-dd', VarToDateTime(Value)); + varSmallInt, varInteger, varShortInt, varByte, varWord, varLongWord, + varInt64, varUInt64: + json.Add(KeyName).AsInt64 := Value; + varSingle, varDouble, varCurrency: + json.Add(KeyName).AsFloat := Value; + varOleStr, varString, varUString: + json.Add(KeyName).AsString := Value; + (varByte or varArray): + begin + Vaue := + TValue.From(Variant2Bytes(Value)); + json.Add(KeyName).FromRtti(Vaue); + end; + end; + end; + end; + + var + i:Integer; + begin + Result := TQJson.Create(); + for i := 0 to Params.Count - 1 do + begin + if RowArray then + begin + if OnlyOutput then + begin + if (Params[i].ParamType in [ptOutput, ptInputOutput]) then + Var2Json(Result, Params[i].Name, Params[i].Value) + end + else + Var2Json(Result, Params[i].Name, Params[i].Value) + end + else + begin + if OnlyOutput then + begin + if (Params[i].ParamType in [ptOutput, ptInputOutput]) then + Var2Json(Result, Params[i].Name, Params[i].Value) + end + else + Var2Json(Result, Params[i].Name, Params[i].Value) + end; + end; + end; + +class procedure TQDBJson.Str2MemStream(StrValue: string; + MemStream: TMemoryStream); +var + StrSteam: TStringStream; +begin + StrSteam := TStringStream.Create(StrValue, TEncoding.UTF8); + try + // StrValue := DecodeString(StrValue); + // StrSteam.Read(StrValue, length(StrValue)); + MemStream.LoadFromStream(StrSteam); + MemStream.Position := 0; + finally + StrSteam.Free; + end; +end; + +class function TQDBJson.StreamToJSON(Stream: TStream; + const Offset, ByteCount: Integer): TQJson; +var + ja: TQJson; + Bytes: TBytes; +begin + Result := nil; + + if Stream = nil then + Exit(ja); + Stream.Position := 0; + SetLength(Bytes, Stream.Size); + Stream.ReadBuffer(Bytes, Stream.Size); + Result := BytesToJSON(Bytes, Offset, ByteCount) +end; + +class function TQDBJson.Variant2Bytes(V: Variant): TBytes; +var + p: pointer; + Size: Int64; +begin + Size := VarArrayHighBound(V, 1) - VarArrayLowBound(V, 1) + 1; + SetLength(Result, Size); + p := VarArrayLock(V); + try + Move(p^, Result[0], Size); + finally + VarArrayUnlock(V); + end; +end; + +end. diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dpr" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dpr" new file mode 100644 index 0000000..58e4d3f --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dpr" @@ -0,0 +1,16 @@ +program JSON_Test; + +uses + Forms, + Unit2 in 'Unit2.pas' {Form2}, + uLkJSON in 'uLkJSON.pas'; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm2, Form2); + Application.Run; +end. diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dproj" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dproj" new file mode 100644 index 0000000..a049b1b --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.dproj" @@ -0,0 +1,76 @@ + + + {d1bcadc3-e5a0-46c3-8d93-203fd8ced567} + JSONTest.dpr + Debug + AnyCPU + DCC32 + JSONTest.exe + + + 7.0 + False + False + 0 + RELEASE + + + 7.0 + DEBUG + ..\..\..\qdac;..\..\..\source + ..\..\..\qdac;..\..\..\source + ..\..\..\qdac;..\..\..\source + ..\..\..\qdac;..\..\..\source + + + Delphi.Personality + VCLApplication + + + False + True + False + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + JSONTest.dpr + + + + + + + MainSource + + + +
Form2
+
+
+
\ No newline at end of file diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.otares" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.otares" new file mode 100644 index 0000000000000000000000000000000000000000..93e7e944f20ed0f0e50532c1b7ef56bb8d255d33 GIT binary patch literal 96 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%0D`}-x#Ef!JGE}DDK*DWak1CbnlyS; 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 Us8{z4)>);yx~C-to%nqJ4{7)`8~^|S literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_Icon2.ico" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_Icon2.ico" new file mode 100644 index 0000000000000000000000000000000000000000..9917d72b6fc189e3dd24cd85ce07c7d8c59f1642 GIT binary patch literal 94198 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 S9KcU3_)-!mPvG?N`Tie;#=;2z literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dpr" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dpr" new file mode 100644 index 0000000..d79448e --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dpr" @@ -0,0 +1,17 @@ +program JSONTest_XE; + +uses + Forms, + Unit2 in 'Unit2.pas' {Form2}, + uLkJSON in 'uLkJSON.pas', + QString in 'QString.pas'; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm2, Form2); + Application.Run; +end. diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dproj" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dproj" new file mode 100644 index 0000000..a699d6c --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.dproj" @@ -0,0 +1,213 @@ + + + {2778da2e-1090-4b05-b789-1366e08ab8a3} + Debug + DCC32 + bin\JSONTest.exe + JSONTest_XE.dpr + True + Debug + 3 + Application + VCL + 15.4 + Win64 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + JSONTest_Icon2.ico + $(BDS)\bin\default_app.manifest + JSONTest_XE + Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + 2052 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + 1033 + + + 1033 + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + + + 7.0 + 0 + False + 0 + RELEASE;$(DCC_Define) + + + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + true + ..\..\..\dcu + ..\..\..\dcu + true + ..\..\..\bin + 1033 + ..\..\..\bin + + + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\ + true + 1033 + + + 7.0 + DEBUG;$(DCC_Define) + .\bin + .\dcu + .\dcu + .\dcu + .\bin + .\dcu + + + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + ..\..\..\bin + ..\..\..\bin + true + ..\..\..\dcu + 1033 + ..\..\..\dcu + + + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + true + 1033 + + + Delphi.Personality.12 + + + + + False + True + False + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 2052 + 936 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + JSONTest_XE.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + + + + True + True + + + 12 + + + + + MainSource + + +
Form2
+
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + +
diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.res" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.res" new file mode 100644 index 0000000000000000000000000000000000000000..b60aad9b3fa46d9529921c15f67a5577121f65d9 GIT binary patch literal 95832 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%0D2)Y{S(;+*aDWv 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 + + {ace080c6-4d89-4142-a42b-c8ed2b60798c} + JSONTest_d2007.dpr + Debug + AnyCPU + DCC32 + ..\..\..\bin\JSONTest_d2007.exe + + + 7.0 + False + False + 0 + RELEASE + + + 7.0 + DEBUG + ..\..\..\qdac;..\..\..\source; + ..\..\..\qdac;..\..\..\source; + ..\..\..\qdac;..\..\..\source; + ..\..\..\qdac;..\..\..\source; + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + + + Delphi.Personality + VCLApplication + +FalseTrueFalseFalseFalse1000FalseFalseFalseFalseFalse20529361.0.0.01.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + JSONTest_d2007.dpr + + + + + MainSource + + + + +
Form2
+
+
+ \ No newline at end of file diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.res" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.res" new file mode 100644 index 0000000000000000000000000000000000000000..4f415bd9507f771e440cca8d2a6c3178c2017415 GIT binary patch literal 4396 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 + + {2778da2e-1090-4b05-b789-1366e08ab8a3} + Debug + DCC32 + ..\..\..\bin\JSONTest.exe + JSONTest.dpr + True + Debug + 1 + Application + VCL + 15.4 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + JSON_Test + Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + 1033 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + + + $(BDS)\bin\default_app.manifest + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + JSON_Test_Icon.ico + true + 1033 + + + $(BDS)\bin\default_app.manifest + JSON_Test_Icon.ico + + + 7.0 + 0 + False + 0 + RELEASE;$(DCC_Define) + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + ..\..\..\source\;..\..\..\qdac\;$(DCC_ResourcePath) + ..\..\..\source\;..\..\..\qdac\;$(DCC_ObjPath) + ..\..\..\source\;..\..\..\qdac\;$(DCC_IncludePath) + + + 7.0 + DEBUG;$(DCC_Define) + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + ..\..\..\source\;..\..\..\qdac\;$(DCC_ResourcePath) + ..\..\..\source\;..\..\..\qdac\;$(DCC_ObjPath) + ..\..\..\source\;..\..\..\qdac\;$(DCC_IncludePath) + + + Delphi.Personality.12 + + + + + False + True + False + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + JSONTest.dpr + + + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + False + + + 12 + + + + + MainSource + + +
Form2
+
+ + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + + diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.res" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.res" new file mode 100644 index 0000000000000000000000000000000000000000..6892d2849b4cd2981b5a1af65a745233965929a9 GIT binary patch literal 95832 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%0D2)Y{S(;+*aDWv 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.ComCtrls{$ELSE}ComCtrls{$IFEND}; + +type + TForm2 = class(TForm) + YxdPageControl1: TPageControl; + TabSheet1: TTabSheet; + Memo1: TMemo; + TabSheet2: TTabSheet; + Memo2: TMemo; + TabSheet3: TTabSheet; + ListBox1: TListBox; + Button1: TButton; + Button2: TButton; + Button3: TButton; + PopupMenu1: TPopupMenu; + N1: TMenuItem; + Button4: TButton; + Button5: TButton; + Button6: TButton; + Button7: TButton; + Button8: TButton; + Button9: TButton; + CheckBox1: TCheckBox; + CheckBox2: TCheckBox; + Button10: TButton; + Button11: TButton; + OpenDialog1: TOpenDialog; + Button12: TButton; + Button16: TButton; + Button17: TButton; + Button18: TButton; + procedure Button1Click(Sender: TObject); + procedure Button3Click(Sender: TObject); + procedure Button2Click(Sender: TObject); + procedure N1Click(Sender: TObject); + procedure Button4Click(Sender: TObject); + procedure Button5Click(Sender: TObject); + procedure Button6Click(Sender: TObject); + procedure Button7Click(Sender: TObject); + procedure Button8Click(Sender: TObject); + procedure Button9Click(Sender: TObject); + procedure Button10Click(Sender: TObject); + procedure Button11Click(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure Button12Click(Sender: TObject); + procedure Button16Click(Sender: TObject); + procedure Button17Click(Sender: TObject); + procedure Button18Click(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + Form2: TForm2; + +implementation + +{$R *.dfm} + +uses + YxdJson, qjson, uLkJSON, QString; + +const + STJSONSTR = '{"carno":"A12345","city":0,"cjhm":"","ctype":"","cubage":0,'+ + '"devno":"916704270865","dphone":"13058126562","driver":"","dw":32,"h":0,'+ + '"id":2,"laddr":"","lat":0,"lbstime":"2014-07-03 19:38:36","length":25,"line"'+ + ':"ӱ,ɹ,,Ϻ","link":"","lng":114.045173888889,"lphone":"","ltel":'+ + '"","public":false,"remark":"","sno":"","state":3,"time":"2014-07-22 08:53:45","w":0,"yyzs":""}'; +procedure TForm2.Button10Click(Sender: TObject); +var + json: YxdJson.JSONObject; +begin + json := YxdJson.JSONObject.parseObject(STJSONSTR); + try + json.getDouble('length'); + finally + json.Free; + end; + ListBox1.Items.Add(YxdJson.JSONObject.parseStringByName(STJSONSTR, 'dphone')); +end; + +procedure TForm2.Button11Click(Sender: TObject); +var + json: JSONObject; + t: Cardinal; +begin + if OpenDialog1.Execute(Self.Handle) then begin + t := GetTickCount; + json := JSONObject.Create; + try + json.LoadFromFile(OpenDialog1.FileName); + Memo2.Text := json.ToString(4); + finally + json.Free; + end; + t := GetTickCount - t; + ShowMessage(Format('%s'#13'ļسɹ, ʱ %dms.', + [OpenDialog1.FileName, t])); + end; +end; + +procedure TForm2.Button12Click(Sender: TObject); +var + json: JSONObject; + ja: JSONArray; + i, allowAccess: Integer; +begin + json := JSONObject.Create; + try + json.Parse(Memo2.Text); + //ja := json.GetJsonObject('data').GetJsonArray('album'); + ja := json.ItemByPath('data.album').AsJsonArray; + for I := 0 to ja.Count - 1 do + allowAccess := ja.GetJsonObject(i).GetInt('allowAccess'); + finally + json.Free; + end; +end; + +procedure TForm2.Button16Click(Sender: TObject); +var + json: JSONObject; +begin + ShowMessage(YxdJson.JSONObject.ParseStringByName(Memo1.Text, 'name')); + json := YxdJson.JSONObject.ParseObjectByName(Memo1.Text, 'name', 'resKey'); + if Assigned(json) then begin + ShowMessage(json.ToString()); + FreeAndNil(json); + end; + Exit; + ShowMessage(YxdJson.JSONObject.ParseStringByName(Memo1.Text, 'name')); + ShowMessage(YxdJson.JSONObject.ParseStringByName(Memo1.Text, 'asa_id')); + ShowMessage(YxdJson.JSONObject.ParseStringByName(Memo1.Text, 'price')); + ShowMessage(YxdJson.JSONObject.ParseStringByName(Memo1.Text, 'not_receive')); +end; + +procedure TForm2.Button17Click(Sender: TObject); +var + json: JSONObject; +begin + json := JSONObject.Create; + json.AddChildObject('test'); + ShowMessage(json.ToString()); + FreeAndNil(json); +end; + +procedure TForm2.Button18Click(Sender: TObject); +var + Json: YxdJson.JSONObject; +begin + Json := YxdJson.JSONObject.Create; + try + json['aaa.error'].AsString := 'yangyxd'; + ShowMessage(json.ToString() + #13#10 + json['aaa'].AsString); + ShowMessage(json.ToString() + #13#10 + json.S['aaa']); + finally + FreeAndNil(Json); + end; +end; + +procedure TForm2.Button1Click(Sender: TObject); +var + json: YxdJson.JSONObject; +begin + json := YxdJson.JSONObject.parseObject(Memo1.Text); + try + Memo2.Text := json.toString(); + //ShowMessage(json[Edit1.Text].AsString); + finally + json.Free; + end; +end; + +procedure TForm2.Button2Click(Sender: TObject); +var + yjson: YxdJson.JSONObject; + qjson: TQJson; + t, t1: Cardinal; + i, j: Integer; + s, v, v1: string; +begin + s := Memo1.Text; + + t := GetTickCount; + for i := 0 to 20000 - 1 do begin + yjson := YxdJson.JSONObject.parseObject(s); + v := yjson.toString(4); + yjson.Free; + end; + t := GetTickCount - t; + + + t1 := GetTickCount; + for j := 0 to 20000 - 1 do begin + qjson := TQJSon.Create(); + qjson.parse(s); + v1 := qjson.toString(); + qjson.Free; + end; + t1 := GetTickCount - t1; + + ListBox1.Items.Add(Format('YxdJson. count: %d, time: %dms.', [i, t])); + ListBox1.Items.Add(Format('TQJson. count: %d, time: %dms.', [j, t1])); + //ListBox1.Items.Add(Format('UJson. count: %d, time: %dms.', [m, t2])); + //ListBox1.Items.Add(Format('uLkJSON. count: %d, time: %dms.', [n, t3])); +end; + +procedure TForm2.Button3Click(Sender: TObject); +var + json: YxdJson.JSONObject; +begin + json := YxdJson.JSONObject.parseObject(Memo1.Text); + try + Memo2.Text := json.toString(4); + finally + json.Free; + end; +end; + +procedure TForm2.Button4Click(Sender: TObject); +var + yjson: YxdJson.JSONObject; + qjson: TQJson; + t, t1: Cardinal; + i, j: Integer; + s, v, v1: string; +begin + s := Memo1.Text; + + t := GetTickCount; + yjson := YxdJson.JSONObject.Create; + for i := 0 to 20000 - 1 do begin + yjson.put('test', 'string'); + yjson.putDateTime('time', Now); + yjson.put('num', 9999); + yjson.put('float', 8.88); + yjson.putJSON('value', '{"results":[],"status":5,"msg":"AK Illegal or Not Exist:"}'); + yjson.getFloat('float'); + yjson.getString('test'); + yjson.getString('num'); + yjson.getInt('num'); + v := yjson.toString(4); + //v := yjson.getJsonObject('value').Path; + //Memo2.Text := v; + yjson.Clear; + end; + yjson.Free; + t := GetTickCount - t; + + t1 := GetTickCount; + qjson := TQJSon.Create(); + for j := 0 to 20000 - 1 do begin + qjson.add('test', 'string', jdtString); + qjson.AddDateTime('time', Now); + qjson.add('num', 9999); + qjson.add('float', 8.88); + qjson.add('value', '{"results":[],"status":5,"msg":"AK Illegal or Not Exist:"}'); + qjson.ItemByName('float').AsFloat; + qjson.ItemByName('test').AsString; + qjson.ItemByName('num').AsString; + qjson.ItemByName('num').AsInteger; + v1 := qjson.toString(); + //v1 := qjson.ItemByName('value').Path; + qjson.Clear; + end; + qjson.Free; + t1 := GetTickCount - t1; + + Memo2.Text := v; + + ListBox1.Items.Add(Format('YxdJson. count: %d, time: %dms.', [i, t])); + ListBox1.Items.Add(Format('TQJson. count: %d, time: %dms.', [j, t1])); + //ListBox1.Items.Add(Format('UJson. count: %d, time: %dms.', [m, t2])); + //ListBox1.Items.Add(Format('uLkJSON. count: %d, time: %dms.', [n, t3])); +end; + +procedure TForm2.Button5Click(Sender: TObject); +var + json: TQJson; +begin + json := TQJson.Create; + try + json.Parse(Memo1.Text); + Memo2.Text := json.toString(); + finally + json.Free; + end; +end; + +procedure TForm2.Button6Click(Sender: TObject); +var + fname: string; + yjson: YxdJson.JSONObject; + json: TQJson; + t, t1: Cardinal; + I: Integer; + v, v1: string; +begin + fname := ExtractFilePath(Application.ExeName) + 'test.json'; + + t := GetTickCount; + for i := 0 to 1000 - 1 do begin + yjson := YxdJson.JSONObject.Create; + try + yjson.LoadFromFile(fname); + v := yjson.toString(4); + yjson.SaveToFile(fname + '.yxdjson.json'); + finally + yjson.Free; + end; + end; + t := GetTickCount - t; + + t1 := GetTickCount; + for i := 0 to 1000 - 1 do begin + json := TQJson.Create; + try + json.LoadFromFile(fname); + v1 := json.toString(); + json.SaveToFile(fname + '.qjson.json', {$IFDEF JSON_UNICODE}qstring.teUTF8{$ELSE}qstring.teAnsi{$ENDIF}, true); + finally + json.Free; + end; + end; + t1 := GetTickCount - t1; + + Memo2.text := v; + + ListBox1.Items.Add(Format('YxdJson.LoadFromFile count: %d, time: %dms.', [i, t])); + ListBox1.Items.Add(Format('TQJson. LoadFromFile count: %d, time: %dms.', [i, t1])); +end; + +procedure TForm2.Button7Click(Sender: TObject); +var + fname: string; + qjson: TQJson; +begin + fname := ExtractFilePath(Application.ExeName) + 'test.json'; + + qjson := TQJson.Create; + try + qjson.LoadFromFile(fname); + Memo2.Text := qjson.toString(); + finally + qjson.Free; + end; +end; + +procedure TForm2.Button8Click(Sender: TObject); +var + fname: string; + yjson: YxdJson.JSONObject; + t, t1, t2: Cardinal; + I: Integer; + v: string; +begin + fname := ExtractFilePath(Application.ExeName) + 'Preferences.txt'; + t := GetTickCount; + for i := 0 to 1 - 1 do begin + yjson := YxdJson.JSONObject.Create; + try + yjson.LoadFromFile(fname); + v := yjson.toString(4); + t1 := GetTickCount - t; + yjson.SaveToFile(fname + '.yxdjson.json'); + t2 := GetTickCount - t - t1; + finally + yjson.Free; + end; + end; + t := GetTickCount - t; + ListBox1.Items.add(Format('YJson ʱ%dms, ʱ%dms, %dms', [t1, t2, t])); + if CheckBox2.Checked then Memo2.Text := v; +end; + +procedure TForm2.Button9Click(Sender: TObject); +var + fname: string; + json: TQJson; + t, t1, t2: Cardinal; + I: Integer; + v: string; +begin + fname := ExtractFilePath(Application.ExeName) + 'Preferences.txt'; + + t := GetTickCount; + for i := 0 to 1 - 1 do begin + json := TQJson.Create; + try + json.LoadFromFile(fname); + v := json.toString(); + t1 := GetTickCount - t; + json.SaveToFile(fname + '.qjson.json', qstring.teAnsi, true); + t2 := GetTickCount - t - t1; + finally + json.Free; + end; + end; + t := GetTickCount - t; + ListBox1.Items.add(Format('QJson ʱ%dms, ʱ%dms, %dms', [t1, t2, t])); + if CheckBox2.Checked then Memo2.Text := v; +end; + +procedure TForm2.FormCreate(Sender: TObject); +begin + OpenDialog1.InitialDir := ExtractFilePath(ParamStr(0)); +end; + +procedure TForm2.N1Click(Sender: TObject); +begin + ListBox1.Clear; +end; + +end. diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/superobject.pas" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/superobject.pas" new file mode 100644 index 0000000..191d53d --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/superobject.pas" @@ -0,0 +1,6555 @@ +(* + * Super Object Toolkit + * + * Usage allowed under the restrictions of the Lesser GNU General Public License + * or alternatively the restrictions of the Mozilla Public License 1.1 + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + * the specific language governing rights and limitations under the License. + * + * Unit owner : Henri Gourvest + * Web site : http://www.progdigy.com + * + * This unit is inspired from the json c lib: + * Michael Clark + * http://oss.metaparadigm.com/json-c/ + * + * CHANGES: + * v1.2 + * + support of currency data type + * + right trim unquoted string + * + read Unicode Files and streams (Litle Endian with BOM) + * + Fix bug on javadate functions + windows nt compatibility + * + Now you can force to parse only the canonical syntax of JSON using the stric parameter + * + Delphi 2010 RTTI marshalling + * v1.1 + * + Double licence MPL or LGPL. + * + Delphi 2009 compatibility & Unicode support. + * + AsString return a string instead of PChar. + * + Escaped and Unascaped JSON serialiser. + * + Missed FormFeed added \f + * - Removed @ trick, uses forcepath() method instead. + * + Fixed parse error with uppercase E symbol in numbers. + * + Fixed possible buffer overflow when enlarging array. + * + Added "delete", "pack", "insert" methods for arrays and/or objects + * + Multi parametters when calling methods + * + Delphi Enumerator (for obj1 in obj2 do ...) + * + Format method ex: obj.format('<%name%>%tab[1]%') + * + ParseFile and ParseStream methods + * + Parser now understand hexdecimal c syntax ex: \xFF + * + Null Object Design Patern (ex: for obj in values.N['path'] do ...) + * v1.0 + * + renamed class + * + interfaced object + * + added a new data type: the method + * + parser can now evaluate properties and call methods + * - removed obselet rpc class + * - removed "find" method, now you can use "parse" method instead + * v0.6 + * + refactoring + * v0.5 + * + new find method to get or set value using a path syntax + * ex: obj.s['obj.prop[1]'] := 'string value'; + * obj.a['@obj.array'].b[n] := true; // @ -> create property if necessary + * v0.4 + * + bug corrected: AVL tree badly balanced. + * v0.3 + * + New validator partially based on the Kwalify syntax. + * + extended syntax to parse unquoted fields. + * + Freepascal compatibility win32/64 Linux32/64. + * + JavaToDelphiDateTime and DelphiToJavaDateTime improved for UTC. + * + new TJsonObject.Compare function. + * v0.2 + * + Hashed string list replaced with a faster AVL tree + * + JsonInt data type can be changed to int64 + * + JavaToDelphiDateTime and DelphiToJavaDateTime helper fonctions + * + from json-c v0.7 + * + Add escaping of backslash to json output + * + Add escaping of foward slash on tokenizing and output + * + Changes to internal tokenizer from using recursion to + * using a depth state structure to allow incremental parsing + * v0.1 + * + first release + *) + +{$IFDEF FPC} + {$MODE OBJFPC}{$H+} +{$ENDIF} + +{$DEFINE SUPER_METHOD} +{$DEFINE WINDOWSNT_COMPATIBILITY} +{.$DEFINE DEBUG} // track memory leack + +unit superobject; + +interface +uses + Classes +{$IFDEF VER210} + ,Generics.Collections, RTTI, TypInfo +{$ENDIF} + ; + +type +{$IFNDEF FPC} + PtrInt = longint; + PtrUInt = Longword; +{$ENDIF} + SuperInt = Int64; + +{$if (sizeof(Char) = 1)} + SOChar = WideChar; + SOIChar = Word; + PSOChar = PWideChar; + SOString = WideString; +{$else} + SOChar = Char; + SOIChar = Word; + PSOChar = PChar; + SOString = string; +{$ifend} + +const + SUPER_ARRAY_LIST_DEFAULT_SIZE = 32; + SUPER_TOKENER_MAX_DEPTH = 32; + + SUPER_AVL_MAX_DEPTH = sizeof(longint) * 8; + SUPER_AVL_MASK_HIGH_BIT = not ((not longword(0)) shr 1); + +type + // forward declarations + TSuperObject = class; + ISuperObject = interface; + TSuperArray = class; + +(* AVL Tree + * This is a "special" autobalanced AVL tree + * It use a hash value for fast compare + *) + +{$IFDEF SUPER_METHOD} + TSuperMethod = procedure(const This, Params: ISuperObject; var Result: ISuperObject); +{$ENDIF} + + + TSuperAvlBitArray = set of 0..SUPER_AVL_MAX_DEPTH - 1; + + TSuperAvlSearchType = (stEQual, stLess, stGreater); + TSuperAvlSearchTypes = set of TSuperAvlSearchType; + TSuperAvlIterator = class; + + TSuperAvlEntry = class + private + FGt, FLt: TSuperAvlEntry; + FBf: integer; + FHash: Cardinal; + FName: SOString; + FPtr: Pointer; + function GetValue: ISuperObject; + procedure SetValue(const val: ISuperObject); + public + class function Hash(const k: SOString): Cardinal; virtual; + constructor Create(const AName: SOString; Obj: Pointer); virtual; + property Name: SOString read FName; + property Ptr: Pointer read FPtr; + property Value: ISuperObject read GetValue write SetValue; + end; + + TSuperAvlTree = class + private + FRoot: TSuperAvlEntry; + FCount: Integer; + function balance(bal: TSuperAvlEntry): TSuperAvlEntry; + protected + procedure doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); virtual; + function CompareNodeNode(node1, node2: TSuperAvlEntry): integer; virtual; + function CompareKeyNode(const k: SOString; h: TSuperAvlEntry): integer; virtual; + function Insert(h: TSuperAvlEntry): TSuperAvlEntry; virtual; + function Search(const k: SOString; st: TSuperAvlSearchTypes = [stEqual]): TSuperAvlEntry; virtual; + public + constructor Create; virtual; + destructor Destroy; override; + function IsEmpty: boolean; + procedure Clear(all: boolean = false); virtual; + procedure Pack(all: boolean); + function Delete(const k: SOString): ISuperObject; + function GetEnumerator: TSuperAvlIterator; + property count: Integer read FCount; + end; + + TSuperTableString = class(TSuperAvlTree) + protected + procedure doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); override; + procedure PutO(const k: SOString; const value: ISuperObject); + function GetO(const k: SOString): ISuperObject; + procedure PutS(const k: SOString; const value: SOString); + function GetS(const k: SOString): SOString; + procedure PutI(const k: SOString; value: SuperInt); + function GetI(const k: SOString): SuperInt; + procedure PutD(const k: SOString; value: Double); + function GetD(const k: SOString): Double; + procedure PutB(const k: SOString; value: Boolean); + function GetB(const k: SOString): Boolean; +{$IFDEF SUPER_METHOD} + procedure PutM(const k: SOString; value: TSuperMethod); + function GetM(const k: SOString): TSuperMethod; +{$ENDIF} + procedure PutN(const k: SOString; const value: ISuperObject); + function GetN(const k: SOString): ISuperObject; + procedure PutC(const k: SOString; value: Currency); + function GetC(const k: SOString): Currency; + public + property O[const k: SOString]: ISuperObject read GetO write PutO; default; + property S[const k: SOString]: SOString read GetS write PutS; + property I[const k: SOString]: SuperInt read GetI write PutI; + property D[const k: SOString]: Double read GetD write PutD; + property B[const k: SOString]: Boolean read GetB write PutB; +{$IFDEF SUPER_METHOD} + property M[const k: SOString]: TSuperMethod read GetM write PutM; +{$ENDIF} + property N[const k: SOString]: ISuperObject read GetN write PutN; + property C[const k: SOString]: Currency read GetC write PutC; + + function GetValues: ISuperObject; + function GetNames: ISuperObject; + end; + + TSuperAvlIterator = class + private + FTree: TSuperAvlTree; + FBranch: TSuperAvlBitArray; + FDepth: LongInt; + FPath: array[0..SUPER_AVL_MAX_DEPTH - 2] of TSuperAvlEntry; + public + constructor Create(tree: TSuperAvlTree); virtual; + procedure Search(const k: SOString; st: TSuperAvlSearchTypes = [stEQual]); + procedure First; + procedure Last; + function GetIter: TSuperAvlEntry; + procedure Next; + procedure Prior; + // delphi enumerator + function MoveNext: Boolean; + property Current: TSuperAvlEntry read GetIter; + end; + + TSuperObjectArray = array[0..(high(PtrInt) div sizeof(TSuperObject))-1] of ISuperObject; + PSuperObjectArray = ^TSuperObjectArray; + + TSuperArray = class + private + FArray: PSuperObjectArray; + FLength: Integer; + FSize: Integer; + procedure Expand(max: Integer); + protected + function GetO(const index: integer): ISuperObject; + procedure PutO(const index: integer; const Value: ISuperObject); + function GetB(const index: integer): Boolean; + procedure PutB(const index: integer; Value: Boolean); + function GetI(const index: integer): SuperInt; + procedure PutI(const index: integer; Value: SuperInt); + function GetD(const index: integer): Double; + procedure PutD(const index: integer; Value: Double); + function GetC(const index: integer): Currency; + procedure PutC(const index: integer; Value: Currency); + function GetS(const index: integer): SOString; + procedure PutS(const index: integer; const Value: SOString); +{$IFDEF SUPER_METHOD} + function GetM(const index: integer): TSuperMethod; + procedure PutM(const index: integer; Value: TSuperMethod); +{$ENDIF} + function GetN(const index: integer): ISuperObject; + procedure PutN(const index: integer; const Value: ISuperObject); + public + constructor Create; virtual; + destructor Destroy; override; + function Add(const Data: ISuperObject): Integer; + function Delete(index: Integer): ISuperObject; + procedure Insert(index: Integer; const value: ISuperObject); + procedure Clear(all: boolean = false); + procedure Pack(all: boolean); + property Length: Integer read FLength; + + property N[const index: integer]: ISuperObject read GetN write PutN; + property O[const index: integer]: ISuperObject read GetO write PutO; default; + property B[const index: integer]: boolean read GetB write PutB; + property I[const index: integer]: SuperInt read GetI write PutI; + property D[const index: integer]: Double read GetD write PutD; + property C[const index: integer]: Currency read GetC write PutC; + property S[const index: integer]: SOString read GetS write PutS; +{$IFDEF SUPER_METHOD} + property M[const index: integer]: TSuperMethod read GetM write PutM; +{$ENDIF} +// property A[const index: integer]: TSuperArray read GetA; + end; + + TSuperWriter = class + public + // abstact methods to overide + function Append(buf: PSOChar; Size: Integer): Integer; overload; virtual; abstract; + function Append(buf: PSOChar): Integer; overload; virtual; abstract; + procedure Reset; virtual; abstract; + end; + + TSuperWriterString = class(TSuperWriter) + private + FBuf: PSOChar; + FBPos: integer; + FSize: integer; + public + function Append(buf: PSOChar; Size: Integer): Integer; overload; override; + function Append(buf: PSOChar): Integer; overload; override; + procedure Reset; override; + procedure TrimRight; + constructor Create; virtual; + destructor Destroy; override; + function GetString: SOString; + property Data: PSOChar read FBuf; + property Size: Integer read FSize; + property Position: integer read FBPos; + end; + + TSuperWriterStream = class(TSuperWriter) + private + FStream: TStream; + public + function Append(buf: PSOChar): Integer; override; + procedure Reset; override; + constructor Create(AStream: TStream); reintroduce; virtual; + end; + + TSuperAnsiWriterStream = class(TSuperWriterStream) + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + end; + + TSuperUnicodeWriterStream = class(TSuperWriterStream) + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + end; + + TSuperWriterFake = class(TSuperWriter) + private + FSize: Integer; + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + function Append(buf: PSOChar): Integer; override; + procedure Reset; override; + constructor Create; reintroduce; virtual; + property size: integer read FSize; + end; + + TSuperWriterSock = class(TSuperWriter) + private + FSocket: longint; + FSize: Integer; + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + function Append(buf: PSOChar): Integer; override; + procedure Reset; override; + constructor Create(ASocket: longint); reintroduce; virtual; + property Socket: longint read FSocket; + property Size: Integer read FSize; + end; + + TSuperTokenizerError = ( + teSuccess, + teContinue, + teDepth, + teParseEof, + teParseUnexpected, + teParseNull, + teParseBoolean, + teParseNumber, + teParseArray, + teParseObjectKeyName, + teParseObjectKeySep, + teParseObjectValueSep, + teParseString, + teParseComment, + teEvalObject, + teEvalArray, + teEvalMethod, + teEvalInt + ); + + TSuperTokenerState = ( + tsEatws, + tsStart, + tsFinish, + tsNull, + tsCommentStart, + tsComment, + tsCommentEol, + tsCommentEnd, + tsString, + tsStringEscape, + tsIdentifier, + tsEscapeUnicode, + tsEscapeHexadecimal, + tsBoolean, + tsNumber, + tsArray, + tsArrayAdd, + tsArraySep, + tsObjectFieldStart, + tsObjectField, + tsObjectUnquotedField, + tsObjectFieldEnd, + tsObjectValue, + tsObjectValueAdd, + tsObjectSep, + tsEvalProperty, + tsEvalArray, + tsEvalMethod, + tsParamValue, + tsParamPut, + tsMethodValue, + tsMethodPut + ); + + PSuperTokenerSrec = ^TSuperTokenerSrec; + TSuperTokenerSrec = record + state, saved_state: TSuperTokenerState; + obj: ISuperObject; + current: ISuperObject; + field_name: SOString; + parent: ISuperObject; + gparent: ISuperObject; + end; + + TSuperTokenizer = class + public + str: PSOChar; + pb: TSuperWriterString; + depth, is_double, floatcount, st_pos, char_offset: Integer; + err: TSuperTokenizerError; + ucs_char: Word; + quote_char: SOChar; + stack: array[0..SUPER_TOKENER_MAX_DEPTH-1] of TSuperTokenerSrec; + line, col: Integer; + public + constructor Create; virtual; + destructor Destroy; override; + procedure ResetLevel(adepth: integer); + procedure Reset; + end; + + // supported object types + TSuperType = ( + stNull, + stBoolean, + stDouble, + stCurrency, + stInt, + stObject, + stArray, + stString +{$IFDEF SUPER_METHOD} + ,stMethod +{$ENDIF} + ); + + TSuperValidateError = ( + veRuleMalformated, + veFieldIsRequired, + veInvalidDataType, + veFieldNotFound, + veUnexpectedField, + veDuplicateEntry, + veValueNotInEnum, + veInvalidLength, + veInvalidRange + ); + + TSuperFindOption = ( + foCreatePath, + foPutValue, + foDelete +{$IFDEF SUPER_METHOD} + ,foCallMethod +{$ENDIF} + ); + + TSuperFindOptions = set of TSuperFindOption; + TSuperCompareResult = (cpLess, cpEqu, cpGreat, cpError); + TSuperOnValidateError = procedure(sender: Pointer; error: TSuperValidateError; const objpath: SOString); + + TSuperEnumerator = class + private + FObj: ISuperObject; + FObjEnum: TSuperAvlIterator; + FCount: Integer; + public + constructor Create(const obj: ISuperObject); virtual; + destructor Destroy; override; + function MoveNext: Boolean; + function GetCurrent: ISuperObject; + property Current: ISuperObject read GetCurrent; + end; + + ISuperObject = interface + ['{4B86A9E3-E094-4E5A-954A-69048B7B6327}'] + function GetEnumerator: TSuperEnumerator; + function GetDataType: TSuperType; + function GetProcessing: boolean; + procedure SetProcessing(value: boolean); + function ForcePath(const path: SOString; dataType: TSuperType = stObject): ISuperObject; + function Format(const str: SOString; BeginSep: SOChar = '%'; EndSep: SOChar = '%'): SOString; + + function GetO(const path: SOString): ISuperObject; + procedure PutO(const path: SOString; const Value: ISuperObject); + function GetB(const path: SOString): Boolean; + procedure PutB(const path: SOString; Value: Boolean); + function GetI(const path: SOString): SuperInt; + procedure PutI(const path: SOString; Value: SuperInt); + function GetD(const path: SOString): Double; + procedure PutC(const path: SOString; Value: Currency); + function GetC(const path: SOString): Currency; + procedure PutD(const path: SOString; Value: Double); + function GetS(const path: SOString): SOString; + procedure PutS(const path: SOString; const Value: SOString); +{$IFDEF SUPER_METHOD} + function GetM(const path: SOString): TSuperMethod; + procedure PutM(const path: SOString; Value: TSuperMethod); +{$ENDIF} + function GetA(const path: SOString): TSuperArray; + + // Null Object Design patern + function GetN(const path: SOString): ISuperObject; + procedure PutN(const path: SOString; const Value: ISuperObject); + + // Writers + function Write(writer: TSuperWriter; indent: boolean; escape: boolean; level: integer): Integer; + function SaveTo(stream: TStream; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(const FileName: string; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(socket: longint; indent: boolean = false; escape: boolean = true): integer; overload; + function CalcSize(indent: boolean = false; escape: boolean = true): integer; + + // convert + function AsBoolean: Boolean; + function AsInteger: SuperInt; + function AsDouble: Double; + function AsCurrency: Currency; + function AsString: SOString; + function AsArray: TSuperArray; + function AsObject: TSuperTableString; +{$IFDEF SUPER_METHOD} + function AsMethod: TSuperMethod; +{$ENDIF} + function AsJSon(indent: boolean = false; escape: boolean = true): SOString; + + procedure Clear(all: boolean = false); + procedure Pack(all: boolean = false); + + property N[const path: SOString]: ISuperObject read GetN write PutN; + property O[const path: SOString]: ISuperObject read GetO write PutO; default; + property B[const path: SOString]: boolean read GetB write PutB; + property I[const path: SOString]: SuperInt read GetI write PutI; + property D[const path: SOString]: Double read GetD write PutD; + property C[const path: SOString]: Currency read GetC write PutC; + property S[const path: SOString]: SOString read GetS write PutS; +{$IFDEF SUPER_METHOD} + property M[const path: SOString]: TSuperMethod read GetM write PutM; +{$ENDIF} + property A[const path: SOString]: TSuperArray read GetA; + +{$IFDEF SUPER_METHOD} + function call(const path: SOString; const param: ISuperObject = nil): ISuperObject; overload; + function call(const path, param: SOString): ISuperObject; overload; +{$ENDIF} + // clone a node + function Clone: ISuperObject; + function Delete(const path: SOString): ISuperObject; + // merges tow objects of same type, if reference is true then nodes are not cloned + procedure Merge(const obj: ISuperObject; reference: boolean = false); overload; + procedure Merge(const str: SOString); overload; + + // validate methods + function Validate(const rules: SOString; const defs: SOString = ''; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + function Validate(const rules: ISuperObject; const defs: ISuperObject = nil; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + + // compare + function Compare(const obj: ISuperObject): TSuperCompareResult; overload; + function Compare(const str: SOString): TSuperCompareResult; overload; + + // the data type + function IsType(AType: TSuperType): boolean; + property DataType: TSuperType read GetDataType; + property Processing: boolean read GetProcessing write SetProcessing; + + function GetDataPtr: Pointer; + procedure SetDataPtr(const Value: Pointer); + property DataPtr: Pointer read GetDataPtr write SetDataPtr; + end; + + TSuperObject = class(TObject, ISuperObject) + private + FRefCount: Integer; + FProcessing: boolean; + FDataType: TSuperType; + FDataPtr: Pointer; +{.$if true} + FO: record + case TSuperType of + stBoolean: (c_boolean: boolean); + stDouble: (c_double: double); + stCurrency: (c_currency: Currency); + stInt: (c_int: SuperInt); + stObject: (c_object: TSuperTableString); + stArray: (c_array: TSuperArray); +{$IFDEF SUPER_METHOD} + stMethod: (c_method: TSuperMethod); +{$ENDIF} + end; +{.$ifend} + FOString: SOString; + function GetDataType: TSuperType; + function GetDataPtr: Pointer; + procedure SetDataPtr(const Value: Pointer); + protected + function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall; + function _AddRef: Integer; virtual; stdcall; + function _Release: Integer; virtual; stdcall; + + function GetO(const path: SOString): ISuperObject; + procedure PutO(const path: SOString; const Value: ISuperObject); + function GetB(const path: SOString): Boolean; + procedure PutB(const path: SOString; Value: Boolean); + function GetI(const path: SOString): SuperInt; + procedure PutI(const path: SOString; Value: SuperInt); + function GetD(const path: SOString): Double; + procedure PutD(const path: SOString; Value: Double); + procedure PutC(const path: SOString; Value: Currency); + function GetC(const path: SOString): Currency; + function GetS(const path: SOString): SOString; + procedure PutS(const path: SOString; const Value: SOString); +{$IFDEF SUPER_METHOD} + function GetM(const path: SOString): TSuperMethod; + procedure PutM(const path: SOString; Value: TSuperMethod); +{$ENDIF} + function GetA(const path: SOString): TSuperArray; + function Write(writer: TSuperWriter; indent: boolean; escape: boolean; level: integer): Integer; virtual; + public + function GetEnumerator: TSuperEnumerator; + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + class function NewInstance: TObject; override; + property RefCount: Integer read FRefCount; + + function GetProcessing: boolean; + procedure SetProcessing(value: boolean); + + // Writers + function SaveTo(stream: TStream; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(const FileName: string; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(socket: longint; indent: boolean = false; escape: boolean = true): integer; overload; + function CalcSize(indent: boolean = false; escape: boolean = true): integer; + function AsJSon(indent: boolean = false; escape: boolean = true): SOString; + + // parser ... owned! + class function ParseString(s: PSOChar; strict: Boolean; partial: boolean = true; const this: ISuperObject = nil; options: TSuperFindOptions = []; + const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + class function ParseStream(stream: TStream; strict: Boolean; partial: boolean = true; const this: ISuperObject = nil; options: TSuperFindOptions = []; + const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + class function ParseFile(const FileName: string; strict: Boolean; partial: boolean = true; const this: ISuperObject = nil; options: TSuperFindOptions = []; + const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + class function ParseEx(tok: TSuperTokenizer; str: PSOChar; len: integer; strict: Boolean; const this: ISuperObject = nil; + options: TSuperFindOptions = []; const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + + // constructors / destructor + constructor Create(jt: TSuperType = stObject); overload; virtual; + constructor Create(b: boolean); overload; virtual; + constructor Create(i: SuperInt); overload; virtual; + constructor Create(d: double); overload; virtual; + constructor CreateCurrency(c: Currency); overload; virtual; + constructor Create(const s: SOString); overload; virtual; +{$IFDEF SUPER_METHOD} + constructor Create(m: TSuperMethod); overload; virtual; +{$ENDIF} + destructor Destroy; override; + + // convert + function AsBoolean: Boolean; virtual; + function AsInteger: SuperInt; virtual; + function AsDouble: Double; virtual; + function AsCurrency: Currency; virtual; + function AsString: SOString; virtual; + function AsArray: TSuperArray; virtual; + function AsObject: TSuperTableString; virtual; +{$IFDEF SUPER_METHOD} + function AsMethod: TSuperMethod; virtual; +{$ENDIF} + procedure Clear(all: boolean = false); virtual; + procedure Pack(all: boolean = false); virtual; + function GetN(const path: SOString): ISuperObject; + procedure PutN(const path: SOString; const Value: ISuperObject); + function ForcePath(const path: SOString; dataType: TSuperType = stObject): ISuperObject; + function Format(const str: SOString; BeginSep: SOChar = '%'; EndSep: SOChar = '%'): SOString; + + property N[const path: SOString]: ISuperObject read GetN write PutN; + property O[const path: SOString]: ISuperObject read GetO write PutO; default; + property B[const path: SOString]: boolean read GetB write PutB; + property I[const path: SOString]: SuperInt read GetI write PutI; + property D[const path: SOString]: Double read GetD write PutD; + property C[const path: SOString]: Currency read GetC write PutC; + property S[const path: SOString]: SOString read GetS write PutS; +{$IFDEF SUPER_METHOD} + property M[const path: SOString]: TSuperMethod read GetM write PutM; +{$ENDIF} + property A[const path: SOString]: TSuperArray read GetA; + +{$IFDEF SUPER_METHOD} + function call(const path: SOString; const param: ISuperObject = nil): ISuperObject; overload; virtual; + function call(const path, param: SOString): ISuperObject; overload; virtual; +{$ENDIF} + // clone a node + function Clone: ISuperObject; virtual; + function Delete(const path: SOString): ISuperObject; + // merges tow objects of same type, if reference is true then nodes are not cloned + procedure Merge(const obj: ISuperObject; reference: boolean = false); overload; + procedure Merge(const str: SOString); overload; + + // validate methods + function Validate(const rules: SOString; const defs: SOString = ''; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + function Validate(const rules: ISuperObject; const defs: ISuperObject = nil; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + + // compare + function Compare(const obj: ISuperObject): TSuperCompareResult; overload; + function Compare(const str: SOString): TSuperCompareResult; overload; + + // the data type + function IsType(AType: TSuperType): boolean; + property DataType: TSuperType read GetDataType; + // a data pointer to link to something ele, a treeview for example + property DataPtr: Pointer read GetDataPtr write SetDataPtr; + property Processing: boolean read GetProcessing; + end; + +{$IFDEF VER210} + TSuperRttiContext = class; + + TSerialFromJson = function(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; + TSerialToJson = function(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; + + TSuperAttribute = class(TCustomAttribute) + private + FName: string; + public + constructor Create(const AName: string); + property Name: string read FName; + end; + + SOName = class(TSuperAttribute); + SODefault = class(TSuperAttribute); + + + TSuperRttiContext = class + private + class function GetFieldName(r: TRttiField): string; + class function GetFieldDefault(r: TRttiField; const obj: ISuperObject): ISuperObject; + public + Context: TRttiContext; + SerialFromJson: TDictionary; + SerialToJson: TDictionary; + constructor Create; virtual; + destructor Destroy; override; + function FromJson(TypeInfo: PTypeInfo; const obj: ISuperObject; var Value: TValue): Boolean; virtual; + function ToJson(var value: TValue; const index: ISuperObject): ISuperObject; virtual; + function AsType(const obj: ISuperObject): T; + function AsJson(const obj: T; const index: ISuperObject = nil): ISuperObject; + end; + + TSuperObjectHelper = class helper for TObject + public + function ToJson(ctx: TSuperRttiContext = nil): ISuperObject; + constructor FromJson(const obj: ISuperObject; ctx: TSuperRttiContext = nil); overload; + constructor FromJson(const str: string; ctx: TSuperRttiContext = nil); overload; + end; +{$ENDIF} + + TSuperObjectIter = record + key: SOString; + val: ISuperObject; + Ite: TSuperAvlIterator; + end; + +function ObjectIsError(obj: TSuperObject): boolean; +function ObjectIsType(const obj: ISuperObject; typ: TSuperType): boolean; +function ObjectGetType(const obj: ISuperObject): TSuperType; + +function ObjectFindFirst(const obj: ISuperObject; var F: TSuperObjectIter): boolean; +function ObjectFindNext(var F: TSuperObjectIter): boolean; +procedure ObjectFindClose(var F: TSuperObjectIter); + +function SO(const s: SOString = '{}'): ISuperObject; overload; +function SO(const value: Variant): ISuperObject; overload; +function SO(const Args: array of const): ISuperObject; overload; + +function SA(const Args: array of const): ISuperObject; overload; + +function JavaToDelphiDateTime(const dt: int64): TDateTime; +function DelphiToJavaDateTime(const dt: TDateTime): int64; + +{$IFDEF VER210} + +type + TSuperInvokeResult = ( + irSuccess, + irMethothodError, // method don't exist + irParamError, // invalid parametters + irError // other error + ); + +function TrySOInvoke(var ctx: TSuperRttiContext; const obj: TValue; const method: string; const params: ISuperObject; var Return: ISuperObject): TSuperInvokeResult; overload; +function SOInvoke(const obj: TValue; const method: string; const params: ISuperObject; ctx: TSuperRttiContext = nil): ISuperObject; overload; +function SOInvoke(const obj: TValue; const method: string; const params: string; ctx: TSuperRttiContext = nil): ISuperObject; overload; +{$ENDIF} + +implementation +uses sysutils, +{$IFDEF UNIX} + baseunix, unix, DateUtils +{$ELSE} + Windows +{$ENDIF} +{$IFDEF FPC} + ,sockets +{$ELSE} + ,WinSock +{$ENDIF}; + +{$IFDEF DEBUG} +var + debugcount: integer = 0; +{$ENDIF} + +const + super_number_chars_set = ['0'..'9','.','+','-','e','E']; + super_hex_chars: PSOChar = '0123456789abcdef'; + super_hex_chars_set = ['0'..'9','a'..'f','A'..'F']; + + ESC_BS: PSOChar = '\b'; + ESC_LF: PSOChar = '\n'; + ESC_CR: PSOChar = '\r'; + ESC_TAB: PSOChar = '\t'; + ESC_FF: PSOChar = '\f'; + ESC_QUOT: PSOChar = '\"'; + ESC_SL: PSOChar = '\\'; + ESC_SR: PSOChar = '\/'; + ESC_ZERO: PSOChar = '\u0000'; + + TOK_CRLF: PSOChar = #13#10; + TOK_SP: PSOChar = #32; + TOK_BS: PSOChar = #8; + TOK_TAB: PSOChar = #9; + TOK_LF: PSOChar = #10; + TOK_FF: PSOChar = #12; + TOK_CR: PSOChar = #13; +// TOK_SL: PSOChar = '\'; +// TOK_SR: PSOChar = '/'; + TOK_NULL: PSOChar = 'null'; + TOK_CBL: PSOChar = '{'; // curly bracket left + TOK_CBR: PSOChar = '}'; // curly bracket right + TOK_ARL: PSOChar = '['; + TOK_ARR: PSOChar = ']'; + TOK_ARRAY: PSOChar = '[]'; + TOK_OBJ: PSOChar = '{}'; // empty object + TOK_COM: PSOChar = ','; // Comma + TOK_DQT: PSOChar = '"'; // Double Quote + TOK_TRUE: PSOChar = 'true'; + TOK_FALSE: PSOChar = 'false'; + +{$if (sizeof(Char) = 1)} +function StrLComp(const Str1, Str2: PSOChar; MaxLen: Cardinal): Integer; +var + P1, P2: PWideChar; + I: Cardinal; + C1, C2: WideChar; +begin + P1 := Str1; + P2 := Str2; + I := 0; + while I < MaxLen do + begin + C1 := P1^; + C2 := P2^; + + if (C1 <> C2) or (C1 = #0) then + begin + Result := Ord(C1) - Ord(C2); + Exit; + end; + + Inc(P1); + Inc(P2); + Inc(I); + end; + Result := 0; +end; + +function StrComp(const Str1, Str2: PSOChar): Integer; +var + P1, P2: PWideChar; + C1, C2: WideChar; +begin + P1 := Str1; + P2 := Str2; + while True do + begin + C1 := P1^; + C2 := P2^; + + if (C1 <> C2) or (C1 = #0) then + begin + Result := Ord(C1) - Ord(C2); + Exit; + end; + + Inc(P1); + Inc(P2); + end; +end; + +function StrLen(const Str: PSOChar): Cardinal; +var + p: PSOChar; +begin + Result := 0; + if Str <> nil then + begin + p := Str; + while p^ <> #0 do inc(p); + Result := (p - Str); + end; +end; +{$ifend} + +function CurrToStr(c: Currency): SOString; +var + p: PSOChar; + i, len: Integer; +begin + Result := IntToStr(Abs(PInt64(@c)^)); + len := Length(Result); + SetLength(Result, len+1); + if c <> 0 then + begin + while len <= 4 do + begin + Result := '0' + Result; + inc(len); + end; + + p := PSOChar(Result); + inc(p, len-1); + i := 0; + repeat + if p^ <> '0' then + begin + len := len - i + 1; + repeat + p[1] := p^; + dec(p); + inc(i); + until i > 3; + Break; + end; + dec(p); + inc(i); + if i > 3 then + begin + len := len - i + 1; + Break; + end; + until false; + p[1] := '.'; + SetLength(Result, len); + if c < 0 then + Result := '-' + Result; + end; +end; + +{$IFDEF UNIX} + {$linklib c} +{$ENDIF} +function gcvt(value: Double; ndigit: longint; buf: PAnsiChar): PAnsiChar; cdecl; + external {$IFDEF MSWINDOWS} 'msvcrt.dll' name '_gcvt'{$ENDIF}; + +{$IFDEF UNIX} +type + ptm = ^tm; + tm = record + tm_sec: Integer; (* Seconds: 0-59 (K&R says 0-61?) *) + tm_min: Integer; (* Minutes: 0-59 *) + tm_hour: Integer; (* Hours since midnight: 0-23 *) + tm_mday: Integer; (* Day of the month: 1-31 *) + tm_mon: Integer; (* Months *since* january: 0-11 *) + tm_year: Integer; (* Years since 1900 *) + tm_wday: Integer; (* Days since Sunday (0-6) *) + tm_yday: Integer; (* Days since Jan. 1: 0-365 *) + tm_isdst: Integer; (* +1 Daylight Savings Time, 0 No DST, -1 don't know *) + end; + +function mktime(p: ptm): LongInt; cdecl; external; +function gmtime(const t: PLongint): ptm; cdecl; external; +function localtime (const t: PLongint): ptm; cdecl; external; + +function DelphiToJavaDateTime(const dt: TDateTime): Int64; +var + p: ptm; + l, ms: Integer; + v: Int64; +begin + v := Round((dt - 25569) * 86400000); + ms := v mod 1000; + l := v div 1000; + p := localtime(@l); + Result := Int64(mktime(p)) * 1000 + ms; +end; + +function JavaToDelphiDateTime(const dt: int64): TDateTime; +var + p: ptm; + l, ms: Integer; +begin + l := dt div 1000; + ms := dt mod 1000; + p := gmtime(@l); + Result := EncodeDateTime(p^.tm_year+1900, p^.tm_mon+1, p^.tm_mday, p^.tm_hour, p^.tm_min, p^.tm_sec, ms); +end; +{$ELSE} + +{$IFDEF WINDOWSNT_COMPATIBILITY} +function DayLightCompareDate(const date: PSystemTime; + const compareDate: PSystemTime): Integer; +var + limit_day, dayinsecs, weekofmonth: Integer; + First: Word; +begin + if (date^.wMonth < compareDate^.wMonth) then + begin + Result := -1; (* We are in a month before the date limit. *) + Exit; + end; + + if (date^.wMonth > compareDate^.wMonth) then + begin + Result := 1; (* We are in a month after the date limit. *) + Exit; + end; + + (* if year is 0 then date is in day-of-week format, otherwise + * it's absolute date. + *) + if (compareDate^.wYear = 0) then + begin + (* compareDate.wDay is interpreted as number of the week in the month + * 5 means: the last week in the month *) + weekofmonth := compareDate^.wDay; + (* calculate the day of the first DayOfWeek in the month *) + First := (6 + compareDate^.wDayOfWeek - date^.wDayOfWeek + date^.wDay) mod 7 + 1; + limit_day := First + 7 * (weekofmonth - 1); + (* check needed for the 5th weekday of the month *) + if (limit_day > MonthDays[(date^.wMonth=2) and IsLeapYear(date^.wYear)][date^.wMonth - 1]) then + dec(limit_day, 7); + end + else + limit_day := compareDate^.wDay; + + (* convert to seconds *) + limit_day := ((limit_day * 24 + compareDate^.wHour) * 60 + compareDate^.wMinute ) * 60; + dayinsecs := ((date^.wDay * 24 + date^.wHour) * 60 + date^.wMinute ) * 60 + date^.wSecond; + (* and compare *) + + if dayinsecs < limit_day then + Result := -1 else + if dayinsecs > limit_day then + Result := 1 else + Result := 0; (* date is equal to the date limit. *) +end; + +function CompTimeZoneID(const pTZinfo: PTimeZoneInformation; + lpFileTime: PFileTime; islocal: Boolean): LongWord; +var + ret: Integer; + beforeStandardDate, afterDaylightDate: Boolean; + llTime: Int64; + SysTime: TSystemTime; + ftTemp: TFileTime; +begin + llTime := 0; + + if (pTZinfo^.DaylightDate.wMonth <> 0) then + begin + (* if year is 0 then date is in day-of-week format, otherwise + * it's absolute date. + *) + if ((pTZinfo^.StandardDate.wMonth = 0) or + ((pTZinfo^.StandardDate.wYear = 0) and + ((pTZinfo^.StandardDate.wDay < 1) or + (pTZinfo^.StandardDate.wDay > 5) or + (pTZinfo^.DaylightDate.wDay < 1) or + (pTZinfo^.DaylightDate.wDay > 5)))) then + begin + SetLastError(ERROR_INVALID_PARAMETER); + Result := TIME_ZONE_ID_INVALID; + Exit; + end; + + if (not islocal) then + begin + llTime := PInt64(lpFileTime)^; + dec(llTime, Int64(pTZinfo^.Bias + pTZinfo^.DaylightBias) * 600000000); + PInt64(@ftTemp)^ := llTime; + lpFileTime := @ftTemp; + end; + + FileTimeToSystemTime(lpFileTime^, SysTime); + + (* check for daylight savings *) + ret := DayLightCompareDate(@SysTime, @pTZinfo^.StandardDate); + if (ret = -2) then + begin + Result := TIME_ZONE_ID_INVALID; + Exit; + end; + + beforeStandardDate := ret < 0; + + if (not islocal) then + begin + dec(llTime, Int64(pTZinfo^.StandardBias - pTZinfo^.DaylightBias) * 600000000); + PInt64(@ftTemp)^ := llTime; + FileTimeToSystemTime(lpFileTime^, SysTime); + end; + + ret := DayLightCompareDate(@SysTime, @pTZinfo^.DaylightDate); + if (ret = -2) then + begin + Result := TIME_ZONE_ID_INVALID; + Exit; + end; + + afterDaylightDate := ret >= 0; + + Result := TIME_ZONE_ID_STANDARD; + if( pTZinfo^.DaylightDate.wMonth < pTZinfo^.StandardDate.wMonth ) then + begin + (* Northern hemisphere *) + if( beforeStandardDate and afterDaylightDate) then + Result := TIME_ZONE_ID_DAYLIGHT; + end else (* Down south *) + if( beforeStandardDate or afterDaylightDate) then + Result := TIME_ZONE_ID_DAYLIGHT; + end else + (* No transition date *) + Result := TIME_ZONE_ID_UNKNOWN; +end; + +function GetTimezoneBias(const pTZinfo: PTimeZoneInformation; + lpFileTime: PFileTime; islocal: Boolean; pBias: PLongint): Boolean; +var + bias: LongInt; + tzid: LongWord; +begin + bias := pTZinfo^.Bias; + tzid := CompTimeZoneID(pTZinfo, lpFileTime, islocal); + + if( tzid = TIME_ZONE_ID_INVALID) then + begin + Result := False; + Exit; + end; + if (tzid = TIME_ZONE_ID_DAYLIGHT) then + inc(bias, pTZinfo^.DaylightBias) + else if (tzid = TIME_ZONE_ID_STANDARD) then + inc(bias, pTZinfo^.StandardBias); + pBias^ := bias; + Result := True; +end; + +function SystemTimeToTzSpecificLocalTime( + lpTimeZoneInformation: PTimeZoneInformation; + lpUniversalTime, lpLocalTime: PSystemTime): BOOL; +var + ft: TFileTime; + lBias: LongInt; + llTime: Int64; + tzinfo: TTimeZoneInformation; +begin + if (lpTimeZoneInformation <> nil) then + tzinfo := lpTimeZoneInformation^ else + if (GetTimeZoneInformation(tzinfo) = TIME_ZONE_ID_INVALID) then + begin + Result := False; + Exit; + end; + + if (not SystemTimeToFileTime(lpUniversalTime^, ft)) then + begin + Result := False; + Exit; + end; + llTime := PInt64(@ft)^; + if (not GetTimezoneBias(@tzinfo, @ft, False, @lBias)) then + begin + Result := False; + Exit; + end; + (* convert minutes to 100-nanoseconds-ticks *) + dec(llTime, Int64(lBias) * 600000000); + PInt64(@ft)^ := llTime; + Result := FileTimeToSystemTime(ft, lpLocalTime^); +end; + +function TzSpecificLocalTimeToSystemTime( + const lpTimeZoneInformation: PTimeZoneInformation; + const lpLocalTime: PSystemTime; lpUniversalTime: PSystemTime): BOOL; +var + ft: TFileTime; + lBias: LongInt; + t: Int64; + tzinfo: TTimeZoneInformation; +begin + if (lpTimeZoneInformation <> nil) then + tzinfo := lpTimeZoneInformation^ + else + if (GetTimeZoneInformation(tzinfo) = TIME_ZONE_ID_INVALID) then + begin + Result := False; + Exit; + end; + + if (not SystemTimeToFileTime(lpLocalTime^, ft)) then + begin + Result := False; + Exit; + end; + t := PInt64(@ft)^; + if (not GetTimezoneBias(@tzinfo, @ft, True, @lBias)) then + begin + Result := False; + Exit; + end; + (* convert minutes to 100-nanoseconds-ticks *) + inc(t, Int64(lBias) * 600000000); + PInt64(@ft)^ := t; + Result := FileTimeToSystemTime(ft, lpUniversalTime^); +end; +{$ELSE} +function TzSpecificLocalTimeToSystemTime( + lpTimeZoneInformation: PTimeZoneInformation; + lpLocalTime, lpUniversalTime: PSystemTime): BOOL; stdcall; external 'kernel32.dll'; + +function SystemTimeToTzSpecificLocalTime( + lpTimeZoneInformation: PTimeZoneInformation; + lpUniversalTime, lpLocalTime: PSystemTime): BOOL; stdcall; external 'kernel32.dll'; +{$ENDIF} + +function JavaToDelphiDateTime(const dt: int64): TDateTime; +var + t: TSystemTime; +begin + DateTimeToSystemTime(25569 + (dt / 86400000), t); + SystemTimeToTzSpecificLocalTime(nil, @t, @t); + Result := SystemTimeToDateTime(t); +end; + +function DelphiToJavaDateTime(const dt: TDateTime): int64; +var + t: TSystemTime; +begin + DateTimeToSystemTime(dt, t); + TzSpecificLocalTimeToSystemTime(nil, @t, @t); + Result := Round((SystemTimeToDateTime(t) - 25569) * 86400000) +end; +{$ENDIF} + + +function SO(const s: SOString): ISuperObject; overload; +begin + Result := TSuperObject.ParseString(PSOChar(s), False); +end; + +function SA(const Args: array of const): ISuperObject; overload; +type + TByteArray = array[0..sizeof(integer) - 1] of byte; + PByteArray = ^TByteArray; +var + j: Integer; + intf: IInterface; +begin + Result := TSuperObject.Create(stArray); + for j := 0 to length(Args) - 1 do + with Result.AsArray do + case TVarRec(Args[j]).VType of + vtInteger : Add(TSuperObject.Create(TVarRec(Args[j]).VInteger)); + vtInt64 : Add(TSuperObject.Create(TVarRec(Args[j]).VInt64^)); + vtBoolean : Add(TSuperObject.Create(TVarRec(Args[j]).VBoolean)); + vtChar : Add(TSuperObject.Create(SOString(TVarRec(Args[j]).VChar))); + vtWideChar: Add(TSuperObject.Create(SOChar(TVarRec(Args[j]).VWideChar))); + vtExtended: Add(TSuperObject.Create(TVarRec(Args[j]).VExtended^)); + vtCurrency: Add(TSuperObject.CreateCurrency(TVarRec(Args[j]).VCurrency^)); + vtString : Add(TSuperObject.Create(SOString(TVarRec(Args[j]).VString^))); + vtPChar : Add(TSuperObject.Create(SOString(TVarRec(Args[j]).VPChar^))); + vtAnsiString: Add(TSuperObject.Create(SOString(AnsiString(TVarRec(Args[j]).VAnsiString)))); + vtWideString: Add(TSuperObject.Create(SOString(PWideChar(TVarRec(Args[j]).VWideString)))); + vtInterface: + if TVarRec(Args[j]).VInterface = nil then + Add(nil) else + if IInterface(TVarRec(Args[j]).VInterface).QueryInterface(ISuperObject, intf) = 0 then + Add(ISuperObject(intf)) else + Add(nil); + vtPointer : + if TVarRec(Args[j]).VPointer = nil then + Add(nil) else + Add(TSuperObject.Create(PtrInt(TVarRec(Args[j]).VPointer))); + vtVariant: + Add(SO(TVarRec(Args[j]).VVariant^)); + vtObject: + if TVarRec(Args[j]).VPointer = nil then + Add(nil) else + Add(TSuperObject.Create(PtrInt(TVarRec(Args[j]).VPointer))); + vtClass: + if TVarRec(Args[j]).VPointer = nil then + Add(nil) else + Add(TSuperObject.Create(PtrInt(TVarRec(Args[j]).VPointer))); +{$if declared(vtUnicodeString)} + vtUnicodeString: + Add(TSuperObject.Create(SOString(string(TVarRec(Args[j]).VUnicodeString)))); +{$ifend} + else + assert(false); + end; +end; + +function SO(const Args: array of const): ISuperObject; overload; +var + j: Integer; + arr: ISuperObject; +begin + Result := TSuperObject.Create(stObject); + arr := SA(Args); + with arr.AsArray do + for j := 0 to (Length div 2) - 1 do + Result.AsObject.PutO(O[j*2].AsString, O[(j*2) + 1]); +end; + +function SO(const value: Variant): ISuperObject; overload; +begin + with TVarData(value) do + case VType of + varNull: Result := nil; + varEmpty: Result := nil; + varSmallInt: Result := TSuperObject.Create(VSmallInt); + varInteger: Result := TSuperObject.Create(VInteger); + varSingle: Result := TSuperObject.Create(VSingle); + varDouble: Result := TSuperObject.Create(VDouble); + varCurrency: Result := TSuperObject.CreateCurrency(VCurrency); + varDate: Result := TSuperObject.Create(DelphiToJavaDateTime(vDate)); + varOleStr: Result := TSuperObject.Create(SOString(VOleStr)); + varBoolean: Result := TSuperObject.Create(VBoolean); + varShortInt: Result := TSuperObject.Create(VShortInt); + varByte: Result := TSuperObject.Create(VByte); + varWord: Result := TSuperObject.Create(VWord); + varLongWord: Result := TSuperObject.Create(VLongWord); + varInt64: Result := TSuperObject.Create(VInt64); + varString: Result := TSuperObject.Create(SOString(AnsiString(VString))); +{$if declared(varUString)} + varUString: Result := TSuperObject.Create(SOString(string(VUString))); +{$ifend} + else + raise Exception.CreateFmt('Unsuported variant data type: %d', [VType]); + end; +end; + +function ObjectIsError(obj: TSuperObject): boolean; +begin + Result := PtrUInt(obj) > PtrUInt(-4000); +end; + +function ObjectIsType(const obj: ISuperObject; typ: TSuperType): boolean; +begin + if obj <> nil then + Result := typ = obj.DataType else + Result := typ = stNull; +end; + +function ObjectGetType(const obj: ISuperObject): TSuperType; +begin + if obj <> nil then + Result := obj.DataType else + Result := stNull; +end; + +function ObjectFindFirst(const obj: ISuperObject; var F: TSuperObjectIter): boolean; +var + i: TSuperAvlEntry; +begin + if ObjectIsType(obj, stObject) then + begin + F.Ite := TSuperAvlIterator.Create(obj.AsObject); + F.Ite.First; + i := F.Ite.GetIter; + if i <> nil then + begin + f.key := i.Name; + f.val := i.Value; + Result := true; + end else + Result := False; + end else + Result := False; +end; + +function ObjectFindNext(var F: TSuperObjectIter): boolean; +var + i: TSuperAvlEntry; +begin + F.Ite.Next; + i := F.Ite.GetIter; + if i <> nil then + begin + f.key := i.FName; + f.val := i.Value; + Result := true; + end else + Result := False; +end; + +procedure ObjectFindClose(var F: TSuperObjectIter); +begin + F.Ite.Free; + F.val := nil; +end; + +{$IFDEF VER210} + +function serialtoboolean(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; +begin + Result := TSuperObject.Create(TValueData(value).FAsSLong <> 0); +end; + +function serialtodatetime(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; +begin + Result := TSuperObject.Create(DelphiToJavaDateTime(TValueData(value).FAsDouble)); +end; + +function serialtoguid(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; +var + g: TGUID; +begin + value.ExtractRawData(@g); + Result := TSuperObject.Create( + format('%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x', + [g.D1, g.D2, g.D3, + g.D4[0], g.D4[1], g.D4[2], + g.D4[3], g.D4[4], g.D4[5], + g.D4[6], g.D4[7]]) + ); +end; + +function serialfromboolean(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; +var + o: ISuperObject; +begin + case ObjectGetType(obj) of + stBoolean: + begin + TValueData(Value).FAsSLong := obj.AsInteger; + Result := True; + end; + stInt: + begin + TValueData(Value).FAsSLong := ord(obj.AsInteger <> 0); + Result := True; + end; + stString: + begin + o := SO(obj.AsString); + if not ObjectIsType(o, stString) then + Result := serialfromboolean(ctx, SO(obj.AsString), Value) else + Result := False; + end; + else + Result := False; + end; +end; + +function serialfromdatetime(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; +var + dt: TDateTime; +begin + case ObjectGetType(obj) of + stInt: + begin + TValueData(Value).FAsDouble := JavaToDelphiDateTime(obj.AsInteger); + Result := True; + end; + stString: + begin + if TryStrToDateTime(obj.AsString, dt) then + begin + TValueData(Value).FAsDouble := dt; + Result := True; + end else + Result := False; + end; + else + Result := False; + end; +end; + +function UuidFromString(const s: PSOChar; Uuid: PGUID): Boolean; +const + hex2bin: array[#0..#102] of short = ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x00 *) + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x10 *) + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x20 *) + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, (* 0x30 *) + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x40 *) + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x50 *) + -1,10,11,12,13,14,15); (* 0x60 *) +var + i: Integer; +begin + if (strlen(s) <> 36) then Exit(False); + + if ((s[8] <> '-') or (s[13] <> '-') or (s[18] <> '-') or (s[23] <> '-')) then + Exit(False); + + for i := 0 to 35 do + begin + if not i in [8,13,18,23] then + if ((s[i] > 'f') or ((hex2bin[s[i]] = -1) and (s[i] <> ''))) then + Exit(False); + end; + + uuid.D1 := ((hex2bin[s[0]] shl 28) or (hex2bin[s[1]] shl 24) or (hex2bin[s[2]] shl 20) or (hex2bin[s[3]] shl 16) or + (hex2bin[s[4]] shl 12) or (hex2bin[s[5]] shl 8) or (hex2bin[s[6]] shl 4) or hex2bin[s[7]]); + uuid.D2 := (hex2bin[s[9]] shl 12) or (hex2bin[s[10]] shl 8) or (hex2bin[s[11]] shl 4) or hex2bin[s[12]]; + uuid.D3 := (hex2bin[s[14]] shl 12) or (hex2bin[s[15]] shl 8) or (hex2bin[s[16]] shl 4) or hex2bin[s[17]]; + + uuid.D4[0] := (hex2bin[s[19]] shl 4) or hex2bin[s[20]]; + uuid.D4[1] := (hex2bin[s[21]] shl 4) or hex2bin[s[22]]; + uuid.D4[2] := (hex2bin[s[24]] shl 4) or hex2bin[s[25]]; + uuid.D4[3] := (hex2bin[s[26]] shl 4) or hex2bin[s[27]]; + uuid.D4[4] := (hex2bin[s[28]] shl 4) or hex2bin[s[29]]; + uuid.D4[5] := (hex2bin[s[30]] shl 4) or hex2bin[s[31]]; + uuid.D4[6] := (hex2bin[s[32]] shl 4) or hex2bin[s[33]]; + uuid.D4[7] := (hex2bin[s[34]] shl 4) or hex2bin[s[35]]; + Result := True; +end; + +function serialfromguid(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; +begin + case ObjectGetType(obj) of + stNull: + begin + FillChar(Value.GetReferenceToRawData^, SizeOf(TGUID), 0); + Result := True; + end; + stString: Result := UuidFromString(PSOChar(obj.AsString), Value.GetReferenceToRawData); + else + Result := False; + end; +end; + +function SOInvoke(const obj: TValue; const method: string; const params: ISuperObject; ctx: TSuperRttiContext): ISuperObject; overload; +var + owned: Boolean; +begin + if ctx = nil then + begin + ctx := TSuperRttiContext.Create; + owned := True; + end else + owned := False; + try + if TrySOInvoke(ctx, obj, method, params, Result) <> irSuccess then + raise Exception.Create('Invalid method call'); + finally + if owned then + ctx.Free; + end; +end; + +function SOInvoke(const obj: TValue; const method: string; const params: string; ctx: TSuperRttiContext): ISuperObject; overload; +begin + Result := SOInvoke(obj, method, so(params), ctx) +end; + +function TrySOInvoke(var ctx: TSuperRttiContext; const obj: TValue; + const method: string; const params: ISuperObject; + var Return: ISuperObject): TSuperInvokeResult; +var + t: TRttiInstanceType; + m: TRttiMethod; + a: TArray; + ps: TArray; + v: TValue; + index: ISuperObject; + + function GetParams: Boolean; + var + i: Integer; + begin + case ObjectGetType(params) of + stArray: + for i := 0 to Length(ps) - 1 do + if (pfOut in ps[i].Flags) then + TValue.Make(nil, ps[i].ParamType.Handle, a[i]) else + if not ctx.FromJson(ps[i].ParamType.Handle, params.AsArray[i], a[i]) then + Exit(False); + stObject: + for i := 0 to Length(ps) - 1 do + if (pfOut in ps[i].Flags) then + TValue.Make(nil, ps[i].ParamType.Handle, a[i]) else + if not ctx.FromJson(ps[i].ParamType.Handle, params.AsObject[ps[i].Name], a[i]) then + Exit(False); + stNull: ; + else + Exit(False); + end; + Result := True; + end; + + procedure SetParams; + var + i: Integer; + begin + case ObjectGetType(params) of + stArray: + for i := 0 to Length(ps) - 1 do + if (ps[i].Flags * [pfVar, pfOut]) <> [] then + params.AsArray[i] := ctx.ToJson(a[i], index); + stObject: + for i := 0 to Length(ps) - 1 do + if (ps[i].Flags * [pfVar, pfOut]) <> [] then + params.AsObject[ps[i].Name] := ctx.ToJson(a[i], index); + end; + end; + +begin + Result := irSuccess; + index := SO; + case obj.Kind of + tkClass: + begin + t := TRttiInstanceType(ctx.Context.GetType(obj.AsObject.ClassType)); + m := t.GetMethod(method); + if m = nil then Exit(irMethothodError); + ps := m.GetParameters; + SetLength(a, Length(ps)); + if not GetParams then Exit(irParamError); + if m.IsClassMethod then + begin + v := m.Invoke(obj.AsObject.ClassType, a); + Return := ctx.ToJson(v, index); + SetParams; + end else + begin + v := m.Invoke(obj, a); + Return := ctx.ToJson(v, index); + SetParams; + end; + end; + tkClassRef: + begin + t := TRttiInstanceType(ctx.Context.GetType(obj.AsClass)); + m := t.GetMethod(method); + if m = nil then Exit(irMethothodError); + ps := m.GetParameters; + SetLength(a, Length(ps)); + + if not GetParams then Exit(irParamError); + if m.IsClassMethod then + begin + v := m.Invoke(obj, a); + Return := ctx.ToJson(v, index); + SetParams; + end else + Exit(irError); + end; + else + Exit(irError); + end; +end; + +{$ENDIF} + +{ TSuperEnumerator } + +constructor TSuperEnumerator.Create(const obj: ISuperObject); +begin + FObj := obj; + FCount := -1; + if ObjectIsType(FObj, stObject) then + FObjEnum := FObj.AsObject.GetEnumerator else + FObjEnum := nil; +end; + +destructor TSuperEnumerator.Destroy; +begin + if FObjEnum <> nil then + FObjEnum.Free; +end; + +function TSuperEnumerator.MoveNext: Boolean; +begin + case ObjectGetType(FObj) of + stObject: Result := FObjEnum.MoveNext; + stArray: + begin + inc(FCount); + if FCount < FObj.AsArray.Length then + Result := True else + Result := False; + end; + else + Result := false; + end; +end; + +function TSuperEnumerator.GetCurrent: ISuperObject; +begin + case ObjectGetType(FObj) of + stObject: Result := FObjEnum.Current.Value; + stArray: Result := FObj.AsArray.GetO(FCount); + else + Result := FObj; + end; +end; + +{ TSuperObject } + +constructor TSuperObject.Create(jt: TSuperType); +begin + inherited Create; +{$IFDEF DEBUG} + InterlockedIncrement(debugcount); +{$ENDIF} + + FProcessing := false; + FDataPtr := nil; + FDataType := jt; + case FDataType of + stObject: FO.c_object := TSuperTableString.Create; + stArray: FO.c_array := TSuperArray.Create; + stString: FOString := ''; + else + FO.c_object := nil; + end; +end; + +constructor TSuperObject.Create(b: boolean); +begin + Create(stBoolean); + FO.c_boolean := b; +end; + +constructor TSuperObject.Create(i: SuperInt); +begin + Create(stInt); + FO.c_int := i; +end; + +constructor TSuperObject.Create(d: double); +begin + Create(stDouble); + FO.c_double := d; +end; + +constructor TSuperObject.CreateCurrency(c: Currency); +begin + Create(stCurrency); + FO.c_currency := c; +end; + +destructor TSuperObject.Destroy; +begin +{$IFDEF DEBUG} + InterlockedDecrement(debugcount); +{$ENDIF} + case FDataType of + stObject: FO.c_object.Free; + stArray: FO.c_array.Free; + end; + inherited; +end; + +function TSuperObject.Write(writer: TSuperWriter; indent: boolean; escape: boolean; level: integer): Integer; +function DoEscape(str: PSOChar; len: Integer): Integer; +var + pos, start_offset: Integer; + c: SOChar; + buf: array[0..5] of SOChar; +type + TByteChar = record + case integer of + 0: (a, b: Byte); + 1: (c: WideChar); + end; + begin + if str = nil then + begin + Result := 0; + exit; + end; + pos := 0; start_offset := 0; + with writer do + while pos < len do + begin + c := str[pos]; + case c of + #8,#9,#10,#12,#13,'"','\','/': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + + if(c = #8) then Append(ESC_BS, 2) + else if (c = #9) then Append(ESC_TAB, 2) + else if (c = #10) then Append(ESC_LF, 2) + else if (c = #12) then Append(ESC_FF, 2) + else if (c = #13) then Append(ESC_CR, 2) + else if (c = '"') then Append(ESC_QUOT, 2) + else if (c = '\') then Append(ESC_SL, 2) + else if (c = '/') then Append(ESC_SR, 2); + inc(pos); + start_offset := pos; + end; + else + if (SOIChar(c) > 255) then + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + buf[0] := '\'; + buf[1] := 'u'; + buf[2] := super_hex_chars[TByteChar(c).b shr 4]; + buf[3] := super_hex_chars[TByteChar(c).b and $f]; + buf[4] := super_hex_chars[TByteChar(c).a shr 4]; + buf[5] := super_hex_chars[TByteChar(c).a and $f]; + Append(@buf, 6); + inc(pos); + start_offset := pos; + end else + if (c < #32) or (c > #127) then + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + buf[0] := '\'; + buf[1] := 'u'; + buf[2] := '0'; + buf[3] := '0'; + buf[4] := super_hex_chars[ord(c) shr 4]; + buf[5] := super_hex_chars[ord(c) and $f]; + Append(buf, 6); + inc(pos); + start_offset := pos; + end else + inc(pos); + end; + end; + if(pos - start_offset > 0) then + writer.Append(str + start_offset, pos - start_offset); + Result := 0; + end; + +function DoMinimalEscape(str: PSOChar; len: Integer): Integer; +var + pos, start_offset: Integer; + c: SOChar; +type + TByteChar = record + case integer of + 0: (a, b: Byte); + 1: (c: WideChar); + end; + begin + if str = nil then + begin + Result := 0; + exit; + end; + pos := 0; start_offset := 0; + with writer do + while pos < len do + begin + c := str[pos]; + case c of + #0: + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_ZERO, 6); + inc(pos); + start_offset := pos; + end; + '"': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_QUOT, 2); + inc(pos); + start_offset := pos; + end; + '\': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_SL, 2); + inc(pos); + start_offset := pos; + end; + '/': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_SR, 2); + inc(pos); + start_offset := pos; + end; + else + inc(pos); + end; + end; + if(pos - start_offset > 0) then + writer.Append(str + start_offset, pos - start_offset); + Result := 0; + end; + + + procedure _indent(i: shortint; r: boolean); + begin + inc(level, i); + if r then + with writer do + begin +{$IFDEF MSWINDOWS} + Append(TOK_CRLF, 2); +{$ELSE} + Append(TOK_LF, 1); +{$ENDIF} + for i := 0 to level - 1 do + Append(TOK_SP, 1); + end; + end; +var + k,j: Integer; + iter: TSuperObjectIter; + st: AnsiString; + val: ISuperObject; + fbuffer: array[0..31] of AnsiChar; +const + ENDSTR_A: PSOChar = '": '; + ENDSTR_B: PSOChar = '":'; +begin + + if FProcessing then + begin + Result := writer.Append(TOK_NULL, 4); + Exit; + end; + + FProcessing := true; + with writer do + try + case FDataType of + stObject: + if FO.c_object.FCount > 0 then + begin + k := 0; + Append(TOK_CBL, 1); + if indent then _indent(1, false); + if ObjectFindFirst(Self, iter) then + repeat + {$IFDEF SUPER_METHOD} + if (iter.val = nil) or not ObjectIsType(iter.val, stMethod) then + begin + {$ENDIF} + if (iter.val = nil) or (not iter.val.Processing) then + begin + if(k <> 0) then + Append(TOK_COM, 1); + if indent then _indent(0, true); + Append(TOK_DQT, 1); + if escape then + doEscape(PSOChar(iter.key), Length(iter.key)) else + DoMinimalEscape(PSOChar(iter.key), Length(iter.key)); + if indent then + Append(ENDSTR_A, 3) else + Append(ENDSTR_B, 2); + if(iter.val = nil) then + Append(TOK_NULL, 4) else + iter.val.write(writer, indent, escape, level); + inc(k); + end; + {$IFDEF SUPER_METHOD} + end; + {$ENDIF} + until not ObjectFindNext(iter); + ObjectFindClose(iter); + if indent then _indent(-1, true); + Result := Append(TOK_CBR, 1); + end else + Result := Append(TOK_OBJ, 2); + stBoolean: + begin + if (FO.c_boolean) then + Result := Append(TOK_TRUE, 4) else + Result := Append(TOK_FALSE, 5); + end; + stInt: + begin + str(FO.c_int, st); + Result := Append(PSOChar(SOString(st))); + end; + stDouble: + Result := Append(PSOChar(SOString(gcvt(FO.c_double, 15, fbuffer)))); + stCurrency: + begin + Result := Append(PSOChar(CurrToStr(FO.c_currency))); + end; + stString: + begin + Append(TOK_DQT, 1); + if escape then + doEscape(PSOChar(FOString), Length(FOString)) else + DoMinimalEscape(PSOChar(FOString), Length(FOString)); + Append(TOK_DQT, 1); + Result := 0; + end; + stArray: + if FO.c_array.FLength > 0 then + begin + Append(TOK_ARL, 1); + if indent then _indent(1, true); + k := 0; + j := 0; + while k < FO.c_array.FLength do + begin + + val := FO.c_array.GetO(k); + {$IFDEF SUPER_METHOD} + if not ObjectIsType(val, stMethod) then + begin + {$ENDIF} + if (val = nil) or (not val.Processing) then + begin + if (j <> 0) then + Append(TOK_COM, 1); + if(val = nil) then + Append(TOK_NULL, 4) else + val.write(writer, indent, escape, level); + inc(j); + end; + {$IFDEF SUPER_METHOD} + end; + {$ENDIF} + inc(k); + end; + if indent then _indent(-1, false); + Result := Append(TOK_ARR, 1); + end else + Result := Append(TOK_ARRAY, 2); + stNull: + Result := Append(TOK_NULL, 4); + else + Result := 0; + end; + finally + FProcessing := false; + end; +end; + +function TSuperObject.IsType(AType: TSuperType): boolean; +begin + Result := AType = FDataType; +end; + +function TSuperObject.AsBoolean: boolean; +begin + case FDataType of + stBoolean: Result := FO.c_boolean; + stInt: Result := (FO.c_int <> 0); + stDouble: Result := (FO.c_double <> 0); + stCurrency: Result := (FO.c_currency <> 0); + stString: Result := (Length(FOString) <> 0); + stNull: Result := False; + else + Result := True; + end; +end; + +function TSuperObject.AsInteger: SuperInt; +var + code: integer; + cint: SuperInt; +begin + case FDataType of + stInt: Result := FO.c_int; + stDouble: Result := round(FO.c_double); + stCurrency: Result := round(FO.c_currency); + stBoolean: Result := ord(FO.c_boolean); + stString: + begin + Val(FOString, cint, code); + if code = 0 then + Result := cint else + Result := 0; + end; + else + Result := 0; + end; +end; + +function TSuperObject.AsDouble: Double; +var + code: integer; + cdouble: double; +begin + case FDataType of + stDouble: Result := FO.c_double; + stCurrency: Result := FO.c_currency; + stInt: Result := FO.c_int; + stBoolean: Result := ord(FO.c_boolean); + stString: + begin + Val(FOString, cdouble, code); + if code = 0 then + Result := cdouble else + Result := 0.0; + end; + else + Result := 0.0; + end; +end; + +function TSuperObject.AsCurrency: Currency; +var + code: integer; + cdouble: double; +begin + case FDataType of + stDouble: Result := FO.c_double; + stCurrency: Result := FO.c_currency; + stInt: Result := FO.c_int; + stBoolean: Result := ord(FO.c_boolean); + stString: + begin + Val(FOString, cdouble, code); + if code = 0 then + Result := cdouble else + Result := 0.0; + end; + else + Result := 0.0; + end; +end; + +function TSuperObject.AsString: SOString; +begin + if FDataType = stString then + Result := FOString else + Result := AsJSon(false, false); +end; + +function TSuperObject.GetEnumerator: TSuperEnumerator; +begin + Result := TSuperEnumerator.Create(Self); +end; + +procedure TSuperObject.AfterConstruction; +begin + InterlockedDecrement(FRefCount); +end; + +procedure TSuperObject.BeforeDestruction; +begin + if RefCount <> 0 then + raise Exception.Create('Invalid pointer'); +end; + +function TSuperObject.AsArray: TSuperArray; +begin + if FDataType = stArray then + Result := FO.c_array else + Result := nil; +end; + +function TSuperObject.AsObject: TSuperTableString; +begin + if FDataType = stObject then + Result := FO.c_object else + Result := nil; +end; + +function TSuperObject.AsJSon(indent, escape: boolean): SOString; +var + pb: TSuperWriterString; +begin + pb := TSuperWriterString.Create; + try + if(Write(pb, indent, escape, 0) < 0) then + begin + Result := ''; + Exit; + end; + if pb.FBPos > 0 then + Result := pb.FBuf else + Result := ''; + finally + pb.Free; + end; +end; + +class function TSuperObject.ParseString(s: PSOChar; strict: Boolean; partial: boolean; const this: ISuperObject; + options: TSuperFindOptions; const put: ISuperObject; dt: TSuperType): ISuperObject; +var + tok: TSuperTokenizer; + obj: ISuperObject; +begin + tok := TSuperTokenizer.Create; + obj := ParseEx(tok, s, -1, strict, this, options, put, dt); + if(tok.err <> teSuccess) or (not partial and (s[tok.char_offset] <> #0)) then + Result := nil else + Result := obj; + tok.Free; +end; + +class function TSuperObject.ParseStream(stream: TStream; strict: Boolean; + partial: boolean; const this: ISuperObject; options: TSuperFindOptions; + const put: ISuperObject; dt: TSuperType): ISuperObject; +const + BUFFER_SIZE = 1024; +var + tok: TSuperTokenizer; + buffera: array[0..BUFFER_SIZE-1] of AnsiChar; + bufferw: array[0..BUFFER_SIZE-1] of SOChar; + bom: array[0..1] of byte; + unicode: boolean; + j, size: Integer; + st: string; +begin + st := ''; + tok := TSuperTokenizer.Create; + + if (stream.Read(bom, sizeof(bom)) = 2) and (bom[0] = $FF) and (bom[1] = $FE) then + begin + unicode := true; + size := stream.Read(bufferw, BUFFER_SIZE * SizeOf(SoChar)) div SizeOf(SoChar); + end else + begin + unicode := false; + stream.Seek(0, soFromBeginning); + size := stream.Read(buffera, BUFFER_SIZE); + end; + + while size > 0 do + begin + if not unicode then + for j := 0 to size - 1 do + bufferw[j] := SOChar(buffera[j]); + ParseEx(tok, bufferw, size, strict, this, options, put, dt); + + if tok.err = teContinue then + begin + if not unicode then + size := stream.Read(buffera, BUFFER_SIZE) else + size := stream.Read(bufferw, BUFFER_SIZE * SizeOf(SoChar)) div SizeOf(SoChar); + end else + Break; + end; + if(tok.err <> teSuccess) or (not partial and (st[tok.char_offset] <> #0)) then + Result := nil else + Result := tok.stack[tok.depth].current; + tok.Free; +end; + +class function TSuperObject.ParseFile(const FileName: string; strict: Boolean; + partial: boolean; const this: ISuperObject; options: TSuperFindOptions; + const put: ISuperObject; dt: TSuperType): ISuperObject; +var + stream: TFileStream; +begin + stream := TFileStream.Create(FileName, fmOpenRead, fmShareDenyWrite); + try + Result := ParseStream(stream, strict, partial, this, options, put, dt); + finally + stream.Free; + end; +end; + +class function TSuperObject.ParseEx(tok: TSuperTokenizer; str: PSOChar; len: integer; + strict: Boolean; const this: ISuperObject; options: TSuperFindOptions; const put: ISuperObject; dt: TSuperType): ISuperObject; + +const + spaces = [#32,#8,#9,#10,#12,#13]; + delimiters = ['"', '.', '[', ']', '{', '}', '(', ')', ',', ':', #0]; + reserved = delimiters + spaces; + path = ['a'..'z', 'A'..'Z', '.', '_']; + + function hexdigit(x: SOChar): byte; + begin + if x <= '9' then + Result := byte(x) - byte('0') else + Result := (byte(x) and 7) + 9; + end; + function min(v1, v2: integer): integer; begin if v1 < v2 then result := v1 else result := v2 end; + +var + obj: ISuperObject; + v: SOChar; +{$IFDEF SUPER_METHOD} + sm: TSuperMethod; +{$ENDIF} + numi: SuperInt; + numd: Double; + code: integer; + TokRec: PSuperTokenerSrec; + evalstack: integer; + p: PSOChar; + + function IsEndDelimiter(v: AnsiChar): Boolean; + begin + if tok.depth > 0 then + case tok.stack[tok.depth - 1].state of + tsArrayAdd: Result := v in [',', ']', #0]; + tsObjectValueAdd: Result := v in [',', '}', #0]; + else + Result := v = #0; + end else + Result := v = #0; + end; + +label out, redo_char; +begin + evalstack := 0; + obj := nil; + Result := nil; + TokRec := @tok.stack[tok.depth]; + + tok.char_offset := 0; + tok.err := teSuccess; + + repeat + if (tok.char_offset = len) then + begin + if (tok.depth = 0) and (TokRec^.state = tsEatws) and + (TokRec^.saved_state = tsFinish) then + tok.err := teSuccess else + tok.err := teContinue; + goto out; + end; + + v := str^; + + case v of + #10: + begin + inc(tok.line); + tok.col := 0; + end; + #9: inc(tok.col, 4); + else + inc(tok.col); + end; + +redo_char: + case TokRec^.state of + tsEatws: + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in spaces) then {nop} else + if (v = '/') then + begin + tok.pb.Reset; + tok.pb.Append(@v, 1); + TokRec^.state := tsCommentStart; + end else begin + TokRec^.state := TokRec^.saved_state; + goto redo_char; + end + end; + + tsStart: + case v of + '"', + '''': + begin + TokRec^.state := tsString; + tok.pb.Reset; + tok.quote_char := v; + end; + '-': + begin + TokRec^.state := tsNumber; + tok.pb.Reset; + tok.is_double := 0; + tok.floatcount := -1; + goto redo_char; + end; + + '0'..'9': + begin + if (tok.depth = 0) then + case ObjectGetType(this) of + stObject: + begin + TokRec^.state := tsIdentifier; + TokRec^.current := this; + goto redo_char; + end; + end; + TokRec^.state := tsNumber; + tok.pb.Reset; + tok.is_double := 0; + tok.floatcount := -1; + goto redo_char; + end; + '{': + begin + TokRec^.state := tsEatws; + TokRec^.saved_state := tsObjectFieldStart; + TokRec^.current := TSuperObject.Create(stObject); + end; + '[': + begin + TokRec^.state := tsEatws; + TokRec^.saved_state := tsArray; + TokRec^.current := TSuperObject.Create(stArray); + end; +{$IFDEF SUPER_METHOD} + '(': + begin + if (tok.depth = 0) and ObjectIsType(this, stMethod) then + begin + TokRec^.current := this; + TokRec^.state := tsParamValue; + end; + end; +{$ENDIF} + 'N', + 'n': + begin + TokRec^.state := tsNull; + tok.pb.Reset; + tok.st_pos := 0; + goto redo_char; + end; + 'T', + 't', + 'F', + 'f': + begin + TokRec^.state := tsBoolean; + tok.pb.Reset; + tok.st_pos := 0; + goto redo_char; + end; + else + TokRec^.state := tsIdentifier; + tok.pb.Reset; + goto redo_char; + end; + + tsFinish: + begin + if(tok.depth = 0) then goto out; + obj := TokRec^.current; + tok.ResetLevel(tok.depth); + dec(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + + tsNull: + begin + tok.pb.Append(@v, 1); + if (StrLComp(TOK_NULL, PSOChar(tok.pb.FBuf), min(tok.st_pos + 1, 4)) = 0) then + begin + if (tok.st_pos = 4) then + if (((SOIChar(v) < 256) and (AnsiChar(v) in path)) or (SOIChar(v) >= 256)) then + TokRec^.state := tsIdentifier else + begin + TokRec^.current := TSuperObject.Create(stNull); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end; + end else + begin + TokRec^.state := tsIdentifier; + tok.pb.FBuf[tok.st_pos] := #0; + dec(tok.pb.FBPos); + goto redo_char; + end; + inc(tok.st_pos); + end; + + tsCommentStart: + begin + if(v = '*') then + begin + TokRec^.state := tsComment; + end else + if (v = '/') then + begin + TokRec^.state := tsCommentEol; + end else + begin + tok.err := teParseComment; + goto out; + end; + tok.pb.Append(@v, 1); + end; + + tsComment: + begin + if(v = '*') then + TokRec^.state := tsCommentEnd; + tok.pb.Append(@v, 1); + end; + + tsCommentEol: + begin + if (v = #10) then + TokRec^.state := tsEatws else + tok.pb.Append(@v, 1); + end; + + tsCommentEnd: + begin + tok.pb.Append(@v, 1); + if (v = '/') then + TokRec^.state := tsEatws else + TokRec^.state := tsComment; + end; + + tsString: + begin + if (v = tok.quote_char) then + begin + TokRec^.current := TSuperObject.Create(SOString(tok.pb.GetString)); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsString; + TokRec^.state := tsStringEscape; + end else + begin + tok.pb.Append(@v, 1); + end + end; + + tsEvalProperty: + begin + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stObject); + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, TokRec^.current) + end else + if not ObjectIsType(TokRec^.current, stObject) then + begin + tok.err := teEvalObject; + goto out; + end; + tok.pb.Reset; + TokRec^.state := tsIdentifier; + goto redo_char; + end; + + tsEvalArray: + begin + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stArray); + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, TokRec^.current) + end else + if not ObjectIsType(TokRec^.current, stArray) then + begin + tok.err := teEvalArray; + goto out; + end; + tok.pb.Reset; + TokRec^.state := tsParamValue; + goto redo_char; + end; +{$IFDEF SUPER_METHOD} + tsEvalMethod: + begin + if ObjectIsType(TokRec^.current, stMethod) and assigned(TokRec^.current.AsMethod) then + begin + tok.pb.Reset; + TokRec^.obj := TSuperObject.Create(stArray); + TokRec^.state := tsMethodValue; + goto redo_char; + end else + begin + tok.err := teEvalMethod; + goto out; + end; + end; + + tsMethodValue: + begin + case v of + ')': + TokRec^.state := tsIdentifier; + else + if (tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + inc(evalstack); + TokRec^.state := tsMethodPut; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + end; + + tsMethodPut: + begin + TokRec^.obj.AsArray.Add(obj); + case v of + ',': + begin + tok.pb.Reset; + TokRec^.saved_state := tsMethodValue; + TokRec^.state := tsEatws; + end; + ')': + begin + if TokRec^.obj.AsArray.Length = 1 then + TokRec^.obj := TokRec^.obj.AsArray.GetO(0); + dec(evalstack); + tok.pb.Reset; + TokRec^.saved_state := tsIdentifier; + TokRec^.state := tsEatws; + end; + else + tok.err := teEvalMethod; + goto out; + end; + end; +{$ENDIF} + tsParamValue: + begin + case v of + ']': + TokRec^.state := tsIdentifier; + else + if (tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + inc(evalstack); + TokRec^.state := tsParamPut; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + end; + + tsParamPut: + begin + dec(evalstack); + TokRec^.obj := obj; + tok.pb.Reset; + TokRec^.saved_state := tsIdentifier; + TokRec^.state := tsEatws; + if v <> ']' then + begin + tok.err := teEvalArray; + goto out; + end; + end; + + tsIdentifier: + begin + if (this = nil) then + begin + if (SOIChar(v) < 256) and IsEndDelimiter(AnsiChar(v)) then + begin + if not strict then + begin + tok.pb.TrimRight; + TokRec^.current := TSuperObject.Create(tok.pb.Fbuf); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end else + begin + tok.err := teParseString; + goto out; + end; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsIdentifier; + TokRec^.state := tsStringEscape; + end else + tok.pb.Append(@v, 1); + end else + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in reserved) then + begin + TokRec^.gparent := TokRec^.parent; + if TokRec^.current = nil then + TokRec^.parent := this else + TokRec^.parent := TokRec^.current; + + case ObjectGetType(TokRec^.parent) of + stObject: + case v of + '.': + begin + TokRec^.state := tsEvalProperty; + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + end; + '[': + begin + TokRec^.state := tsEvalArray; + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + end; + '(': + begin + TokRec^.state := tsEvalMethod; + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + end; + else + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + if (foPutValue in options) and (evalstack = 0) then + begin + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, put); + TokRec^.current := put + end else + if (foDelete in options) and (evalstack = 0) then + begin + TokRec^.current := TokRec^.parent.AsObject.Delete(tok.pb.Fbuf); + end else + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(dt); + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, TokRec^.current); + end; + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + TokRec^.state := tsFinish; + goto redo_char; + end; + stArray: + begin + if TokRec^.obj <> nil then + begin + if not ObjectIsType(TokRec^.obj, stInt) or (TokRec^.obj.AsInteger < 0) then + begin + tok.err := teEvalInt; + TokRec^.obj := nil; + goto out; + end; + numi := TokRec^.obj.AsInteger; + TokRec^.obj := nil; + + TokRec^.current := TokRec^.parent.AsArray.GetO(numi); + case v of + '.': + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stObject); + TokRec^.parent.AsArray.PutO(numi, TokRec^.current); + end else + if (TokRec^.current = nil) then + begin + tok.err := teEvalObject; + goto out; + end; + '[': + begin + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stArray); + TokRec^.parent.AsArray.Add(TokRec^.current); + end else + if (TokRec^.current = nil) then + begin + tok.err := teEvalArray; + goto out; + end; + TokRec^.state := tsEvalArray; + end; + '(': TokRec^.state := tsEvalMethod; + else + if (foPutValue in options) and (evalstack = 0) then + begin + TokRec^.parent.AsArray.PutO(numi, put); + TokRec^.current := put; + end else + if (foDelete in options) and (evalstack = 0) then + begin + TokRec^.current := TokRec^.parent.AsArray.Delete(numi); + end else + TokRec^.current := TokRec^.parent.AsArray.GetO(numi); + TokRec^.state := tsFinish; + goto redo_char + end; + end else + begin + case v of + '.': + begin + if (foPutValue in options) then + begin + TokRec^.current := TSuperObject.Create(stObject); + TokRec^.parent.AsArray.Add(TokRec^.current); + end else + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1); + end; + '[': + begin + if (foPutValue in options) then + begin + TokRec^.current := TSuperObject.Create(stArray); + TokRec^.parent.AsArray.Add(TokRec^.current); + end else + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1); + TokRec^.state := tsEvalArray; + end; + '(': + begin + if not (foPutValue in options) then + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1) else + TokRec^.current := nil; + + TokRec^.state := tsEvalMethod; + end; + else + if (foPutValue in options) and (evalstack = 0) then + begin + TokRec^.parent.AsArray.Add(put); + TokRec^.current := put; + end else + if tok.pb.FBPos = 0 then + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1); + TokRec^.state := tsFinish; + goto redo_char + end; + end; + end; +{$IFDEF SUPER_METHOD} + stMethod: + case v of + '.': + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.obj := nil; + end; + '[': + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.state := tsEvalArray; + TokRec^.obj := nil; + end; + '(': + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.state := tsEvalMethod; + TokRec^.obj := nil; + end; + else + if not (foPutValue in options) or (evalstack > 0) then + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.obj := nil; + TokRec^.state := tsFinish; + goto redo_char + end else + begin + tok.err := teEvalMethod; + TokRec^.obj := nil; + goto out; + end; + end; +{$ENDIF} + end; + end else + tok.pb.Append(@v, 1); + end; + end; + + tsStringEscape: + case v of + 'b', + 'n', + 'r', + 't', + 'f': + begin + if(v = 'b') then tok.pb.Append(TOK_BS, 1) + else if(v = 'n') then tok.pb.Append(TOK_LF, 1) + else if(v = 'r') then tok.pb.Append(TOK_CR, 1) + else if(v = 't') then tok.pb.Append(TOK_TAB, 1) + else if(v = 'f') then tok.pb.Append(TOK_FF, 1); + TokRec^.state := TokRec^.saved_state; + end; + 'u': + begin + tok.ucs_char := 0; + tok.st_pos := 0; + TokRec^.state := tsEscapeUnicode; + end; + 'x': + begin + tok.ucs_char := 0; + tok.st_pos := 0; + TokRec^.state := tsEscapeHexadecimal; + end + else + tok.pb.Append(@v, 1); + TokRec^.state := TokRec^.saved_state; + end; + + tsEscapeUnicode: + begin + if ((SOIChar(v) < 256) and (AnsiChar(v) in super_hex_chars_set)) then + begin + inc(tok.ucs_char, (Word(hexdigit(v)) shl ((3-tok.st_pos)*4))); + inc(tok.st_pos); + if (tok.st_pos = 4) then + begin + tok.pb.Append(@tok.ucs_char, 1); + TokRec^.state := TokRec^.saved_state; + end + end else + begin + tok.err := teParseString; + goto out; + end + end; + tsEscapeHexadecimal: + begin + if ((SOIChar(v) < 256) and (AnsiChar(v) in super_hex_chars_set)) then + begin + inc(tok.ucs_char, (Word(hexdigit(v)) shl ((1-tok.st_pos)*4))); + inc(tok.st_pos); + if (tok.st_pos = 2) then + begin + tok.pb.Append(@tok.ucs_char, 1); + TokRec^.state := TokRec^.saved_state; + end + end else + begin + tok.err := teParseString; + goto out; + end + end; + tsBoolean: + begin + tok.pb.Append(@v, 1); + if (StrLComp('true', PSOChar(tok.pb.FBuf), min(tok.st_pos + 1, 4)) = 0) then + begin + if (tok.st_pos = 4) then + if (((SOIChar(v) < 256) and (AnsiChar(v) in path)) or (SOIChar(v) >= 256)) then + TokRec^.state := tsIdentifier else + begin + TokRec^.current := TSuperObject.Create(true); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end + end else + if (StrLComp('false', PSOChar(tok.pb.FBuf), min(tok.st_pos + 1, 5)) = 0) then + begin + if (tok.st_pos = 5) then + if (((SOIChar(v) < 256) and (AnsiChar(v) in path)) or (SOIChar(v) >= 256)) then + TokRec^.state := tsIdentifier else + begin + TokRec^.current := TSuperObject.Create(false); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end + end else + begin + TokRec^.state := tsIdentifier; + tok.pb.FBuf[tok.st_pos] := #0; + dec(tok.pb.FBPos); + goto redo_char; + end; + inc(tok.st_pos); + end; + + tsNumber: + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in super_number_chars_set) then + begin + tok.pb.Append(@v, 1); + if (SOIChar(v) < 256) then + case v of + '.': begin + tok.is_double := 1; + tok.floatcount := 0; + end; + 'e','E': + begin + tok.is_double := 1; + tok.floatcount := -1; + end; + '0'..'9': + begin + + if (tok.is_double = 1) and (tok.floatcount >= 0) then + begin + inc(tok.floatcount); + if tok.floatcount > 4 then + tok.floatcount := -1; + end; + end; + end; + end else + begin + if (tok.is_double = 0) then + begin + val(tok.pb.FBuf, numi, code); + if ObjectIsType(this, stArray) then + begin + if (foPutValue in options) and (evalstack = 0) then + begin + this.AsArray.PutO(numi, put); + TokRec^.current := put; + end else + if (foDelete in options) and (evalstack = 0) then + TokRec^.current := this.AsArray.Delete(numi) else + TokRec^.current := this.AsArray.GetO(numi); + end else + TokRec^.current := TSuperObject.Create(numi); + + end else + if (tok.is_double <> 0) then + begin + if tok.floatcount >= 0 then + begin + p := tok.pb.FBuf; + while p^ <> '.' do inc(p); + for code := 0 to tok.floatcount - 1 do + begin + p^ := p[1]; + inc(p); + end; + p^ := #0; + val(tok.pb.FBuf, numi, code); + case tok.floatcount of + 0: numi := numi * 10000; + 1: numi := numi * 1000; + 2: numi := numi * 100; + 3: numi := numi * 10; + end; + TokRec^.current := TSuperObject.CreateCurrency(PCurrency(@numi)^); + end else + begin + val(tok.pb.FBuf, numd, code); + TokRec^.current := TSuperObject.Create(numd); + end; + end else + begin + tok.err := teParseNumber; + goto out; + end; + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end + end; + + tsArray: + begin + if (v = ']') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + begin + if(tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + TokRec^.state := tsArrayAdd; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end + end; + + tsArrayAdd: + begin + TokRec^.current.AsArray.Add(obj); + TokRec^.saved_state := tsArraySep; + TokRec^.state := tsEatws; + goto redo_char; + end; + + tsArraySep: + begin + if (v = ']') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (v = ',') then + begin + TokRec^.saved_state := tsArray; + TokRec^.state := tsEatws; + end else + begin + tok.err := teParseArray; + goto out; + end + end; + + tsObjectFieldStart: + begin + if (v = '}') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (SOIChar(v) < 256) and (AnsiChar(v) in ['"', '''']) then + begin + tok.quote_char := v; + tok.pb.Reset; + TokRec^.state := tsObjectField; + end else + if not((SOIChar(v) < 256) and ((AnsiChar(v) in reserved) or strict)) then + begin + TokRec^.state := tsObjectUnquotedField; + tok.pb.Reset; + goto redo_char; + end else + begin + tok.err := teParseObjectKeyName; + goto out; + end + end; + + tsObjectField: + begin + if (v = tok.quote_char) then + begin + TokRec^.field_name := tok.pb.FBuf; + TokRec^.saved_state := tsObjectFieldEnd; + TokRec^.state := tsEatws; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsObjectField; + TokRec^.state := tsStringEscape; + end else + begin + tok.pb.Append(@v, 1); + end + end; + + tsObjectUnquotedField: + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in [':', #0]) then + begin + TokRec^.field_name := tok.pb.FBuf; + TokRec^.saved_state := tsObjectFieldEnd; + TokRec^.state := tsEatws; + goto redo_char; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsObjectUnquotedField; + TokRec^.state := tsStringEscape; + end else + tok.pb.Append(@v, 1); + end; + + tsObjectFieldEnd: + begin + if (v = ':') then + begin + TokRec^.saved_state := tsObjectValue; + TokRec^.state := tsEatws; + end else + begin + tok.err := teParseObjectKeySep; + goto out; + end + end; + + tsObjectValue: + begin + if (tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + TokRec^.state := tsObjectValueAdd; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + + tsObjectValueAdd: + begin + TokRec^.current.AsObject.PutO(TokRec^.field_name, obj); + TokRec^.field_name := ''; + TokRec^.saved_state := tsObjectSep; + TokRec^.state := tsEatws; + goto redo_char; + end; + + tsObjectSep: + begin + if (v = '}') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (v = ',') then + begin + TokRec^.saved_state := tsObjectFieldStart; + TokRec^.state := tsEatws; + end else + begin + tok.err := teParseObjectValueSep; + goto out; + end + end; + end; + inc(str); + inc(tok.char_offset); + until v = #0; + + if(TokRec^.state <> tsFinish) and + (TokRec^.saved_state <> tsFinish) then + tok.err := teParseEof; + + out: + if(tok.err in [teSuccess]) then + begin +{$IFDEF SUPER_METHOD} + if (foCallMethod in options) and ObjectIsType(TokRec^.current, stMethod) and assigned(TokRec^.current.AsMethod) then + begin + sm := TokRec^.current.AsMethod; + sm(TokRec^.parent, put, Result); + end else +{$ENDIF} + Result := TokRec^.current; + end else + Result := nil; +end; + +procedure TSuperObject.PutO(const path: SOString; const Value: ISuperObject); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], Value); +end; + +procedure TSuperObject.PutB(const path: SOString; Value: Boolean); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +procedure TSuperObject.PutD(const path: SOString; Value: Double); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +procedure TSuperObject.PutC(const path: SOString; Value: Currency); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.CreateCurrency(Value)); +end; + +procedure TSuperObject.PutI(const path: SOString; Value: SuperInt); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +procedure TSuperObject.PutS(const path: SOString; const Value: SOString); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +function TSuperObject.QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; +begin + if GetInterface(IID, Obj) then + Result := 0 + else + Result := E_NOINTERFACE; +end; + +function TSuperObject.SaveTo(stream: TStream; indent, escape: boolean): integer; +var + pb: TSuperWriterStream; +begin + if escape then + pb := TSuperAnsiWriterStream.Create(stream) else + pb := TSuperUnicodeWriterStream.Create(stream); + + if(Write(pb, indent, escape, 0) < 0) then + begin + pb.Reset; + pb.Free; + Result := 0; + Exit; + end; + Result := stream.Size; + pb.Free; +end; + +function TSuperObject.CalcSize(indent, escape: boolean): integer; +var + pb: TSuperWriterFake; +begin + pb := TSuperWriterFake.Create; + if(Write(pb, indent, escape, 0) < 0) then + begin + pb.Free; + Result := 0; + Exit; + end; + Result := pb.FSize; + pb.Free; +end; + +function TSuperObject.SaveTo(socket: Integer; indent, escape: boolean): integer; +var + pb: TSuperWriterSock; +begin + pb := TSuperWriterSock.Create(socket); + if(Write(pb, indent, escape, 0) < 0) then + begin + pb.Free; + Result := 0; + Exit; + end; + Result := pb.FSize; + pb.Free; +end; + +constructor TSuperObject.Create(const s: SOString); +begin + Create(stString); + FOString := s; +end; + +procedure TSuperObject.Clear(all: boolean); +begin + if FProcessing then exit; + FProcessing := true; + try + case FDataType of + stBoolean: FO.c_boolean := false; + stDouble: FO.c_double := 0.0; + stCurrency: FO.c_currency := 0.0; + stInt: FO.c_int := 0; + stObject: FO.c_object.Clear(all); + stArray: FO.c_array.Clear(all); + stString: FOString := ''; +{$IFDEF SUPER_METHOD} + stMethod: FO.c_method := nil; +{$ENDIF} + end; + finally + FProcessing := false; + end; +end; + +procedure TSuperObject.Pack(all: boolean = false); +begin + if FProcessing then exit; + FProcessing := true; + try + case FDataType of + stObject: FO.c_object.Pack(all); + stArray: FO.c_array.Pack(all); + end; + finally + FProcessing := false; + end; +end; + +function TSuperObject.GetN(const path: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, true, self); + if Result = nil then + Result := TSuperObject.Create(stNull); +end; + +procedure TSuperObject.PutN(const path: SOString; const Value: ISuperObject); +begin + if Value = nil then + ParseString(PSOChar(path), False, True, self, [foCreatePath, foPutValue], TSuperObject.Create(stNull)) else + ParseString(PSOChar(path), False, True, self, [foCreatePath, foPutValue], Value); +end; + +function TSuperObject.Delete(const path: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, true, self, [foDelete]); +end; + +function TSuperObject.Clone: ISuperObject; +var + ite: TSuperObjectIter; + arr: TSuperArray; + j: integer; +begin + case FDataType of + stBoolean: Result := TSuperObject.Create(FO.c_boolean); + stDouble: Result := TSuperObject.Create(FO.c_double); + stCurrency: Result := TSuperObject.CreateCurrency(FO.c_currency); + stInt: Result := TSuperObject.Create(FO.c_int); + stString: Result := TSuperObject.Create(FOString); +{$IFDEF SUPER_METHOD} + stMethod: Result := TSuperObject.Create(FO.c_method); +{$ENDIF} + stObject: + begin + Result := TSuperObject.Create(stObject); + if ObjectFindFirst(self, ite) then + with Result.AsObject do + repeat + PutO(ite.key, ite.val.Clone); + until not ObjectFindNext(ite); + ObjectFindClose(ite); + end; + stArray: + begin + Result := TSuperObject.Create(stArray); + arr := AsArray; + with Result.AsArray do + for j := 0 to arr.Length - 1 do + Add(arr.GetO(j).Clone); + end; + else + Result := nil; + end; +end; + +procedure TSuperObject.Merge(const obj: ISuperObject; reference: boolean); +var + prop1, prop2: ISuperObject; + ite: TSuperObjectIter; + arr: TSuperArray; + j: integer; +begin + if ObjectIsType(obj, FDataType) then + case FDataType of + stBoolean: FO.c_boolean := obj.AsBoolean; + stDouble: FO.c_double := obj.AsDouble; + stCurrency: FO.c_currency := obj.AsCurrency; + stInt: FO.c_int := obj.AsInteger; + stString: FOString := obj.AsString; +{$IFDEF SUPER_METHOD} + stMethod: FO.c_method := obj.AsMethod; +{$ENDIF} + stObject: + begin + if ObjectFindFirst(obj, ite) then + with FO.c_object do + repeat + prop1 := FO.c_object.GetO(ite.key); + if (prop1 <> nil) and (ite.val <> nil) and (prop1.DataType = ite.val.DataType) then + prop1.Merge(ite.val) else + if reference then + PutO(ite.key, ite.val) else + PutO(ite.key, ite.val.Clone); + until not ObjectFindNext(ite); + ObjectFindClose(ite); + end; + stArray: + begin + arr := obj.AsArray; + with FO.c_array do + for j := 0 to arr.Length - 1 do + begin + prop1 := GetO(j); + prop2 := arr.GetO(j); + if (prop1 <> nil) and (prop2 <> nil) and (prop1.DataType = prop2.DataType) then + prop1.Merge(prop2) else + if reference then + PutO(j, prop2) else + PutO(j, prop2.Clone); + end; + end; + end; +end; + +procedure TSuperObject.Merge(const str: SOString); +begin + Merge(TSuperObject.ParseString(PSOChar(str), False), true); +end; + +class function TSuperObject.NewInstance: TObject; +begin + Result := inherited NewInstance; + TSuperObject(Result).FRefCount := 1; +end; + +function TSuperObject.ForcePath(const path: SOString; dataType: TSuperType = stObject): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self, [foCreatePath], nil, dataType); +end; + +function TSuperObject.Format(const str: SOString; BeginSep: SOChar; EndSep: SOChar): SOString; +var + p1, p2: PSOChar; +begin + Result := ''; + p2 := PSOChar(str); + p1 := p2; + while true do + if p2^ = BeginSep then + begin + if p2 > p1 then + Result := Result + Copy(p1, 0, p2-p1); + inc(p2); + p1 := p2; + while true do + if p2^ = EndSep then Break else + if p2^ = #0 then Exit else + inc(p2); + Result := Result + GetS(copy(p1, 0, p2-p1)); + inc(p2); + p1 := p2; + end + else if p2^ = #0 then + begin + if p2 > p1 then + Result := Result + Copy(p1, 0, p2-p1); + Break; + end else + inc(p2); +end; + +function TSuperObject.GetO(const path: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self); +end; + +function TSuperObject.GetA(const path: SOString): TSuperArray; +var + obj: ISuperObject; +begin + obj := ParseString(PSOChar(path), False, True, Self); + if obj <> nil then + Result := obj.AsArray else + Result := nil; +end; + +function TSuperObject.GetB(const path: SOString): Boolean; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsBoolean else + Result := false; +end; + +function TSuperObject.GetD(const path: SOString): Double; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsDouble else + Result := 0.0; +end; + +function TSuperObject.GetC(const path: SOString): Currency; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsCurrency else + Result := 0.0; +end; + +function TSuperObject.GetI(const path: SOString): SuperInt; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsInteger else + Result := 0; +end; + +function TSuperObject.GetDataPtr: Pointer; +begin + Result := FDataPtr; +end; + +function TSuperObject.GetDataType: TSuperType; +begin + Result := FDataType +end; + +function TSuperObject.GetS(const path: SOString): SOString; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsString else + Result := ''; +end; + +function TSuperObject.SaveTo(const FileName: string; indent, escape: boolean): integer; +var + stream: TFileStream; +begin + stream := TFileStream.Create(FileName, fmCreate); + try + Result := SaveTo(stream, indent, escape); + finally + stream.Free; + end; +end; + +function TSuperObject.Validate(const rules: SOString; const defs: SOString = ''; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; +begin + Result := Validate(TSuperObject.ParseString(PSOChar(rules), False), TSuperObject.ParseString(PSOChar(defs), False), callback, sender); +end; + +function TSuperObject.Validate(const rules: ISuperObject; const defs: ISuperObject = nil; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; +type + TDataType = (dtUnknown, dtStr, dtInt, dtFloat, dtNumber, dtText, dtBool, + dtMap, dtSeq, dtScalar, dtAny); +var + datatypes: ISuperObject; + names: ISuperObject; + + function FindInheritedProperty(const prop: PSOChar; p: ISuperObject): ISuperObject; + var + o: ISuperObject; + e: TSuperAvlEntry; + begin + o := p[prop]; + if o <> nil then + result := o else + begin + o := p['inherit']; + if (o <> nil) and ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + Result := FindInheritedProperty(prop, e.Value) else + Result := nil; + end else + Result := nil; + end; + end; + + function FindDataType(o: ISuperObject): TDataType; + var + e: TSuperAvlEntry; + obj: ISuperObject; + begin + obj := FindInheritedProperty('type', o); + if obj <> nil then + begin + e := datatypes.AsObject.Search(obj.AsString); + if e <> nil then + Result := TDataType(e.Value.AsInteger) else + Result := dtUnknown; + end else + Result := dtUnknown; + end; + + procedure GetNames(o: ISuperObject); + var + obj: ISuperObject; + f: TSuperObjectIter; + begin + obj := o['name']; + if ObjectIsType(obj, stString) then + names[obj.AsString] := o; + + case FindDataType(o) of + dtMap: + begin + obj := o['mapping']; + if ObjectIsType(obj, stObject) then + begin + if ObjectFindFirst(obj, f) then + repeat + if ObjectIsType(f.val, stObject) then + GetNames(f.val); + until not ObjectFindNext(f); + ObjectFindClose(f); + end; + end; + dtSeq: + begin + obj := o['sequence']; + if ObjectIsType(obj, stObject) then + GetNames(obj); + end; + end; + end; + + function FindInheritedField(const prop: SOString; p: ISuperObject): ISuperObject; + var + o: ISuperObject; + e: TSuperAvlEntry; + begin + o := p['mapping']; + if ObjectIsType(o, stObject) then + begin + o := o.AsObject.GetO(prop); + if o <> nil then + begin + Result := o; + Exit; + end; + end; + + o := p['inherit']; + if ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + Result := FindInheritedField(prop, e.Value) else + Result := nil; + end else + Result := nil; + end; + + function InheritedFieldExist(const obj: ISuperObject; p: ISuperObject; const name: SOString = ''): boolean; + var + o: ISuperObject; + e: TSuperAvlEntry; + j: TSuperAvlIterator; + begin + Result := true; + o := p['mapping']; + if ObjectIsType(o, stObject) then + begin + j := TSuperAvlIterator.Create(o.AsObject); + try + j.First; + e := j.GetIter; + while e <> nil do + begin + if obj.AsObject.Search(e.Name) = nil then + begin + Result := False; + if assigned(callback) then + callback(sender, veFieldNotFound, name + '.' + e.Name); + end; + j.Next; + e := j.GetIter; + end; + + finally + j.Free; + end; + end; + + o := p['inherit']; + if ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + Result := InheritedFieldExist(obj, e.Value, name) and Result; + end; + end; + + function getInheritedBool(f: PSOChar; p: ISuperObject; default: boolean = false): boolean; + var + o: ISuperObject; + begin + o := FindInheritedProperty(f, p); + case ObjectGetType(o) of + stBoolean: Result := o.AsBoolean; + stNull: Result := Default; + else + Result := default; + if assigned(callback) then + callback(sender, veRuleMalformated, f); + end; + end; + + procedure GetInheritedFieldList(list: ISuperObject; p: ISuperObject); + var + o: ISuperObject; + e: TSuperAvlEntry; + i: TSuperAvlIterator; + begin + Result := true; + o := p['mapping']; + if ObjectIsType(o, stObject) then + begin + i := TSuperAvlIterator.Create(o.AsObject); + try + i.First; + e := i.GetIter; + while e <> nil do + begin + if list.AsObject.Search(e.Name) = nil then + list[e.Name] := e.Value; + i.Next; + e := i.GetIter; + end; + + finally + i.Free; + end; + end; + + o := p['inherit']; + if ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + GetInheritedFieldList(list, e.Value); + end; + end; + + function CheckEnum(o: ISuperObject; p: ISuperObject; name: SOString = ''): boolean; + var + enum: ISuperObject; + i: integer; + begin + Result := false; + enum := FindInheritedProperty('enum', p); + case ObjectGetType(enum) of + stArray: + for i := 0 to enum.AsArray.Length - 1 do + if (o.AsString = enum.AsArray[i].AsString) then + begin + Result := true; + exit; + end; + stNull: Result := true; + else + Result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + Exit; + end; + + if (not Result) and assigned(callback) then + callback(sender, veValueNotInEnum, name); + end; + + function CheckLength(len: integer; p: ISuperObject; const objpath: SOString): boolean; + var + length, o: ISuperObject; + begin + result := true; + length := FindInheritedProperty('length', p); + case ObjectGetType(length) of + stObject: + begin + o := length.AsObject.GetO('min'); + if (o <> nil) and (o.AsInteger > len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + o := length.AsObject.GetO('max'); + if (o <> nil) and (o.AsInteger < len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + o := length.AsObject.GetO('minex'); + if (o <> nil) and (o.AsInteger >= len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + o := length.AsObject.GetO('maxex'); + if (o <> nil) and (o.AsInteger <= len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + end; + stNull: ; + else + Result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + end; + end; + + function CheckRange(obj: ISuperObject; p: ISuperObject; const objpath: SOString): boolean; + var + length, o: ISuperObject; + begin + result := true; + length := FindInheritedProperty('range', p); + case ObjectGetType(length) of + stObject: + begin + o := length.AsObject.GetO('min'); + if (o <> nil) and (o.Compare(obj) = cpGreat) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + o := length.AsObject.GetO('max'); + if (o <> nil) and (o.Compare(obj) = cpLess) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + o := length.AsObject.GetO('minex'); + if (o <> nil) and (o.Compare(obj) in [cpGreat, cpEqu]) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + o := length.AsObject.GetO('maxex'); + if (o <> nil) and (o.Compare(obj) in [cpLess, cpEqu]) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + end; + stNull: ; + else + Result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + end; + end; + + + function process(o: ISuperObject; p: ISuperObject; objpath: SOString = ''): boolean; + var + ite: TSuperAvlIterator; + ent: TSuperAvlEntry; + p2, o2, sequence: ISuperObject; + s: SOString; + i: integer; + uniquelist, fieldlist: ISuperObject; + begin + Result := true; + if (o = nil) then + begin + if getInheritedBool('required', p) then + begin + if assigned(callback) then + callback(sender, veFieldIsRequired, objpath); + result := false; + end; + end else + case FindDataType(p) of + dtStr: + case ObjectGetType(o) of + stString: + begin + Result := Result and CheckLength(Length(o.AsString), p, objpath); + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtBool: + case ObjectGetType(o) of + stBoolean: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtInt: + case ObjectGetType(o) of + stInt: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtFloat: + case ObjectGetType(o) of + stDouble, stCurrency: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtMap: + case ObjectGetType(o) of + stObject: + begin + // all objects have and match a rule ? + ite := TSuperAvlIterator.Create(o.AsObject); + try + ite.First; + ent := ite.GetIter; + while ent <> nil do + begin + p2 := FindInheritedField(ent.Name, p); + if ObjectIsType(p2, stObject) then + result := process(ent.Value, p2, objpath + '.' + ent.Name) and result else + begin + if assigned(callback) then + callback(sender, veUnexpectedField, objpath + '.' + ent.Name); + result := false; // field have no rule + end; + ite.Next; + ent := ite.GetIter; + end; + finally + ite.Free; + end; + + // all expected field exists ? + Result := InheritedFieldExist(o, p, objpath) and Result; + end; + stNull: {nop}; + else + result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + end; + dtSeq: + case ObjectGetType(o) of + stArray: + begin + sequence := FindInheritedProperty('sequence', p); + if sequence <> nil then + case ObjectGetType(sequence) of + stObject: + begin + for i := 0 to o.AsArray.Length - 1 do + result := process(o.AsArray.GetO(i), sequence, objpath + '[' + IntToStr(i) + ']') and result; + if getInheritedBool('unique', sequence) then + begin + // type is unique ? + uniquelist := TSuperObject.Create(stObject); + try + for i := 0 to o.AsArray.Length - 1 do + begin + s := o.AsArray.GetO(i).AsString; + if (s <> '') then + begin + if uniquelist.AsObject.Search(s) = nil then + uniquelist[s] := nil else + begin + Result := False; + if Assigned(callback) then + callback(sender, veDuplicateEntry, objpath + '[' + IntToStr(i) + ']'); + end; + end; + end; + finally + uniquelist := nil; + end; + end; + + // field is unique ? + if (FindDataType(sequence) = dtMap) then + begin + fieldlist := TSuperObject.Create(stObject); + try + GetInheritedFieldList(fieldlist, sequence); + ite := TSuperAvlIterator.Create(fieldlist.AsObject); + try + ite.First; + ent := ite.GetIter; + while ent <> nil do + begin + if getInheritedBool('unique', ent.Value) then + begin + uniquelist := TSuperObject.Create(stObject); + try + for i := 0 to o.AsArray.Length - 1 do + begin + o2 := o.AsArray.GetO(i); + if o2 <> nil then + begin + s := o2.AsObject.GetO(ent.Name).AsString; + if (s <> '') then + if uniquelist.AsObject.Search(s) = nil then + uniquelist[s] := nil else + begin + Result := False; + if Assigned(callback) then + callback(sender, veDuplicateEntry, objpath + '[' + IntToStr(i) + '].' + ent.name); + end; + end; + end; + finally + uniquelist := nil; + end; + end; + ite.Next; + ent := ite.GetIter; + end; + finally + ite.Free; + end; + finally + fieldlist := nil; + end; + end; + + + end; + stNull: {nop}; + else + result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + end; + Result := Result and CheckLength(o.AsArray.Length, p, objpath); + + end; + else + result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + end; + dtNumber: + case ObjectGetType(o) of + stInt, + stDouble, stCurrency: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtText: + case ObjectGetType(o) of + stInt, + stDouble, + stCurrency, + stString: + begin + result := result and CheckLength(Length(o.AsString), p, objpath); + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtScalar: + case ObjectGetType(o) of + stBoolean, + stDouble, + stCurrency, + stInt, + stString: + begin + result := result and CheckLength(Length(o.AsString), p, objpath); + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtAny:; + else + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + result := false; + end; + Result := Result and CheckEnum(o, p, objpath) + + end; +var + j: integer; + +begin + Result := False; + datatypes := TSuperObject.Create(stObject); + names := TSuperObject.Create; + try + datatypes.I['str'] := ord(dtStr); + datatypes.I['int'] := ord(dtInt); + datatypes.I['float'] := ord(dtFloat); + datatypes.I['number'] := ord(dtNumber); + datatypes.I['text'] := ord(dtText); + datatypes.I['bool'] := ord(dtBool); + datatypes.I['map'] := ord(dtMap); + datatypes.I['seq'] := ord(dtSeq); + datatypes.I['scalar'] := ord(dtScalar); + datatypes.I['any'] := ord(dtAny); + + if ObjectIsType(defs, stArray) then + for j := 0 to defs.AsArray.Length - 1 do + if ObjectIsType(defs.AsArray[j], stObject) then + GetNames(defs.AsArray[j]) else + begin + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + Exit; + end; + + + if ObjectIsType(rules, stObject) then + GetNames(rules) else + begin + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + Exit; + end; + + Result := process(self, rules); + + finally + datatypes := nil; + names := nil; + end; +end; + +function TSuperObject._AddRef: Integer; stdcall; +begin + Result := InterlockedIncrement(FRefCount); +end; + +function TSuperObject._Release: Integer; stdcall; +begin + Result := InterlockedDecrement(FRefCount); + if Result = 0 then + Destroy; +end; + +function TSuperObject.Compare(const str: SOString): TSuperCompareResult; +begin + Result := Compare(TSuperObject.ParseString(PSOChar(str), False)); +end; + +function TSuperObject.Compare(const obj: ISuperObject): TSuperCompareResult; + function GetIntCompResult(const i: int64): TSuperCompareResult; + begin + if i < 0 then result := cpLess else + if i = 0 then result := cpEqu else + Result := cpGreat; + end; + + function GetDblCompResult(const d: double): TSuperCompareResult; + begin + if d < 0 then result := cpLess else + if d = 0 then result := cpEqu else + Result := cpGreat; + end; + +begin + case DataType of + stBoolean: + case ObjectGetType(obj) of + stBoolean: Result := GetIntCompResult(ord(FO.c_boolean) - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(ord(FO.c_boolean) - obj.AsDouble); + stCurrency:Result := GetDblCompResult(ord(FO.c_boolean) - obj.AsCurrency); + stInt: Result := GetIntCompResult(ord(FO.c_boolean) - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stDouble: + case ObjectGetType(obj) of + stBoolean: Result := GetDblCompResult(FO.c_double - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(FO.c_double - obj.AsDouble); + stCurrency:Result := GetDblCompResult(FO.c_double - obj.AsCurrency); + stInt: Result := GetDblCompResult(FO.c_double - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stCurrency: + case ObjectGetType(obj) of + stBoolean: Result := GetDblCompResult(FO.c_currency - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(FO.c_currency - obj.AsDouble); + stCurrency:Result := GetDblCompResult(FO.c_currency - obj.AsCurrency); + stInt: Result := GetDblCompResult(FO.c_currency - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stInt: + case ObjectGetType(obj) of + stBoolean: Result := GetIntCompResult(FO.c_int - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(FO.c_int - obj.AsDouble); + stCurrency:Result := GetDblCompResult(FO.c_int - obj.AsCurrency); + stInt: Result := GetIntCompResult(FO.c_int - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stString: + case ObjectGetType(obj) of + stBoolean, + stDouble, + stCurrency, + stInt, + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + else + Result := cpError; + end; +end; + +{$IFDEF SUPER_METHOD} +function TSuperObject.AsMethod: TSuperMethod; +begin + if FDataType = stMethod then + Result := FO.c_method else + Result := nil; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +constructor TSuperObject.Create(m: TSuperMethod); +begin + Create(stMethod); + FO.c_method := m; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperObject.GetM(const path: SOString): TSuperMethod; +var + v: ISuperObject; +begin + v := ParseString(PSOChar(path), False, True, Self); + if (v <> nil) and (ObjectGetType(v) = stMethod) then + Result := v.AsMethod else + Result := nil; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +procedure TSuperObject.PutM(const path: SOString; Value: TSuperMethod); +begin + ParseString(PSOChar(path), False, True, Self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperObject.call(const path: SOString; const param: ISuperObject): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self, [foCallMethod], param); +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperObject.call(const path, param: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self, [foCallMethod], TSuperObject.ParseString(PSOChar(param), False)); +end; +{$ENDIF} + +function TSuperObject.GetProcessing: boolean; +begin + Result := FProcessing; +end; + +procedure TSuperObject.SetDataPtr(const Value: Pointer); +begin + FDataPtr := Value; +end; + +procedure TSuperObject.SetProcessing(value: boolean); +begin + FProcessing := value; +end; + +{ TSuperArray } + +function TSuperArray.Add(const Data: ISuperObject): Integer; +begin + Result := FLength; + PutO(Result, data); +end; + +function TSuperArray.Delete(index: Integer): ISuperObject; +begin + if (Index >= 0) and (Index < FLength) then + begin + Result := FArray^[index]; + FArray^[index] := nil; + Dec(FLength); + if Index < FLength then + begin + Move(FArray^[index + 1], FArray^[index], + (FLength - index) * SizeOf(Pointer)); + Pointer(FArray^[FLength]) := nil; + end; + end; +end; + +procedure TSuperArray.Insert(index: Integer; const value: ISuperObject); +begin + if (Index >= 0) then + if (index < FLength) then + begin + if FLength = FSize then + Expand(index); + if Index < FLength then + Move(FArray^[index], FArray^[index + 1], + (FLength - index) * SizeOf(Pointer)); + Pointer(FArray^[index]) := nil; + FArray^[index] := value; + Inc(FLength); + end else + PutO(index, value); +end; + +procedure TSuperArray.Clear(all: boolean); +var + j: Integer; +begin + for j := 0 to FLength - 1 do + if FArray^[j] <> nil then + begin + if all then + FArray^[j].Clear(all); + FArray^[j] := nil; + end; + FLength := 0; +end; + +procedure TSuperArray.Pack(all: boolean); +var + PackedCount, StartIndex, EndIndex, j: Integer; +begin + if FLength > 0 then + begin + PackedCount := 0; + StartIndex := 0; + repeat + while (StartIndex < FLength) and (FArray^[StartIndex] = nil) do + Inc(StartIndex); + if StartIndex < FLength then + begin + EndIndex := StartIndex; + while (EndIndex < FLength) and (FArray^[EndIndex] <> nil) do + Inc(EndIndex); + + Dec(EndIndex); + + if StartIndex > PackedCount then + Move(FArray^[StartIndex], FArray^[PackedCount], (EndIndex - StartIndex + 1) * SizeOf(Pointer)); + + Inc(PackedCount, EndIndex - StartIndex + 1); + StartIndex := EndIndex + 1; + end; + until StartIndex >= FLength; + FillChar(FArray^[PackedCount], (FLength - PackedCount) * sizeof(Pointer), 0); + FLength := PackedCount; + if all then + for j := 0 to FLength - 1 do + FArray^[j].Pack(all); + end; +end; + +constructor TSuperArray.Create; +begin + inherited Create; + FSize := SUPER_ARRAY_LIST_DEFAULT_SIZE; + FLength := 0; + GetMem(FArray, sizeof(Pointer) * FSize); + FillChar(FArray^, sizeof(Pointer) * FSize, 0); +end; + +destructor TSuperArray.Destroy; +begin + Clear; + FreeMem(FArray); + inherited; +end; + +procedure TSuperArray.Expand(max: Integer); +var + new_size: Integer; +begin + if (max < FSize) then + Exit; + if max < (FSize shl 1) then + new_size := (FSize shl 1) else + new_size := max + 1; + ReallocMem(FArray, new_size * sizeof(Pointer)); + FillChar(FArray^[FSize], (new_size - FSize) * sizeof(Pointer), 0); + FSize := new_size; +end; + +function TSuperArray.GetO(const index: Integer): ISuperObject; +begin + if(index >= FLength) then + Result := nil else + Result := FArray^[index]; +end; + +function TSuperArray.GetB(const index: integer): Boolean; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsBoolean else + Result := false; +end; + +function TSuperArray.GetD(const index: integer): Double; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsDouble else + Result := 0.0; +end; + +function TSuperArray.GetI(const index: integer): SuperInt; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsInteger else + Result := 0; +end; + +function TSuperArray.GetS(const index: integer): SOString; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsString else + Result := ''; +end; + +procedure TSuperArray.PutO(const index: Integer; const Value: ISuperObject); +begin + Expand(index); + FArray^[index] := value; + if(FLength <= index) then FLength := index + 1; +end; + +function TSuperArray.GetN(const index: integer): ISuperObject; +begin + Result := GetO(index); + if Result = nil then + Result := TSuperObject.Create(stNull); +end; + +procedure TSuperArray.PutN(const index: integer; const Value: ISuperObject); +begin + if Value <> nil then + PutO(index, Value) else + PutO(index, TSuperObject.Create(stNull)); +end; + +procedure TSuperArray.PutB(const index: integer; Value: Boolean); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +procedure TSuperArray.PutD(const index: integer; Value: Double); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +function TSuperArray.GetC(const index: integer): Currency; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsCurrency else + Result := 0.0; +end; + +procedure TSuperArray.PutC(const index: integer; Value: Currency); +begin + PutO(index, TSuperObject.CreateCurrency(Value)); +end; + +procedure TSuperArray.PutI(const index: integer; Value: SuperInt); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +procedure TSuperArray.PutS(const index: integer; const Value: SOString); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +{$IFDEF SUPER_METHOD} +function TSuperArray.GetM(const index: integer): TSuperMethod; +var + v: ISuperObject; +begin + v := GetO(index); + if (ObjectGetType(v) = stMethod) then + Result := v.AsMethod else + Result := nil; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +procedure TSuperArray.PutM(const index: integer; Value: TSuperMethod); +begin + PutO(index, TSuperObject.Create(Value)); +end; +{$ENDIF} + +{ TSuperWriterString } + +function TSuperWriterString.Append(buf: PSOChar; Size: Integer): Integer; + function max(a, b: Integer): integer; begin if a > b then Result := a else Result := b end; +begin + Result := size; + if Size > 0 then + begin + if (FSize - FBPos <= size) then + begin + FSize := max(FSize * 2, FBPos + size + 8); + ReallocMem(FBuf, FSize * SizeOf(SOChar)); + end; + // fast move + case size of + 1: FBuf[FBPos] := buf^; + 2: PInteger(@FBuf[FBPos])^ := PInteger(buf)^; + 4: PInt64(@FBuf[FBPos])^ := PInt64(buf)^; + else + move(buf^, FBuf[FBPos], size * SizeOf(SOChar)); + end; + inc(FBPos, size); + FBuf[FBPos] := #0; + end; +end; + +function TSuperWriterString.Append(buf: PSOChar): Integer; +begin + Result := Append(buf, strlen(buf)); +end; + +constructor TSuperWriterString.Create; +begin + inherited; + FSize := 32; + FBPos := 0; + GetMem(FBuf, FSize * SizeOf(SOChar)); +end; + +destructor TSuperWriterString.Destroy; +begin + inherited; + if FBuf <> nil then + FreeMem(FBuf) +end; + +function TSuperWriterString.GetString: SOString; +begin + SetString(Result, FBuf, FBPos); +end; + +procedure TSuperWriterString.Reset; +begin + FBuf[0] := #0; + FBPos := 0; +end; + +procedure TSuperWriterString.TrimRight; +begin + while (FBPos > 0) and (FBuf[FBPos-1] < #256) and (AnsiChar(FBuf[FBPos-1]) in [#32, #13, #10]) do + begin + dec(FBPos); + FBuf[FBPos] := #0; + end; +end; + +{ TSuperWriterStream } + +function TSuperWriterStream.Append(buf: PSOChar): Integer; +begin + Result := Append(buf, StrLen(buf)); +end; + +constructor TSuperWriterStream.Create(AStream: TStream); +begin + inherited Create; + FStream := AStream; +end; + +procedure TSuperWriterStream.Reset; +begin + FStream.Size := 0; +end; + +{ TSuperWriterStream } + +function TSuperAnsiWriterStream.Append(buf: PSOChar; Size: Integer): Integer; +var + Buffer: array[0..1023] of AnsiChar; + pBuffer: PAnsiChar; + i: Integer; +begin + if Size = 1 then + Result := FStream.Write(buf^, Size) else + begin + if Size > SizeOf(Buffer) then + GetMem(pBuffer, Size) else + pBuffer := @Buffer; + try + for i := 0 to Size - 1 do + pBuffer[i] := AnsiChar(buf[i]); + Result := FStream.Write(pBuffer^, Size); + finally + if pBuffer <> @Buffer then + FreeMem(pBuffer); + end; + end; +end; + +{ TSuperUnicodeWriterStream } + +function TSuperUnicodeWriterStream.Append(buf: PSOChar; Size: Integer): Integer; +begin + Result := FStream.Write(buf^, Size * 2); +end; + +{ TSuperWriterFake } + +function TSuperWriterFake.Append(buf: PSOChar; Size: Integer): Integer; +begin + inc(FSize, Size); + Result := FSize; +end; + +function TSuperWriterFake.Append(buf: PSOChar): Integer; +begin + inc(FSize, Strlen(buf)); + Result := FSize; +end; + +constructor TSuperWriterFake.Create; +begin + inherited Create; + FSize := 0; +end; + +procedure TSuperWriterFake.Reset; +begin + FSize := 0; +end; + +{ TSuperWriterSock } + +function TSuperWriterSock.Append(buf: PSOChar; Size: Integer): Integer; +var + Buffer: array[0..1023] of AnsiChar; + pBuffer: PAnsiChar; + i: Integer; +begin + if Size = 1 then +{$IFDEF FPC} + Result := fpsend(FSocket, buf, size, 0) else +{$ELSE} + Result := send(FSocket, buf^, size, 0) else +{$ENDIF} + begin + if Size > SizeOf(Buffer) then + GetMem(pBuffer, Size) else + pBuffer := @Buffer; + try + for i := 0 to Size - 1 do + pBuffer[i] := AnsiChar(buf[i]); +{$IFDEF FPC} + Result := fpsend(FSocket, pBuffer, size, 0); +{$ELSE} + Result := send(FSocket, pBuffer^, size, 0); +{$ENDIF} + finally + if pBuffer <> @Buffer then + FreeMem(pBuffer); + end; + end; + inc(FSize, Result); +end; + +function TSuperWriterSock.Append(buf: PSOChar): Integer; +begin + Result := Append(buf, StrLen(buf)); +end; + +constructor TSuperWriterSock.Create(ASocket: Integer); +begin + inherited Create; + FSocket := ASocket; + FSize := 0; +end; + +procedure TSuperWriterSock.Reset; +begin + FSize := 0; +end; + +{ TSuperTokenizer } + +constructor TSuperTokenizer.Create; +begin + pb := TSuperWriterString.Create; + line := 1; + col := 0; + Reset; +end; + +destructor TSuperTokenizer.Destroy; +begin + Reset; + pb.Free; + inherited; +end; + +procedure TSuperTokenizer.Reset; +var + i: integer; +begin + for i := depth downto 0 do + ResetLevel(i); + depth := 0; + err := teSuccess; +end; + +procedure TSuperTokenizer.ResetLevel(adepth: integer); +begin + stack[adepth].state := tsEatws; + stack[adepth].saved_state := tsStart; + stack[adepth].current := nil; + stack[adepth].field_name := ''; + stack[adepth].obj := nil; + stack[adepth].parent := nil; + stack[adepth].gparent := nil; +end; + +{ TSuperAvlTree } + +constructor TSuperAvlTree.Create; +begin + FRoot := nil; + FCount := 0; +end; + +destructor TSuperAvlTree.Destroy; +begin + Clear; + inherited; +end; + +function TSuperAvlTree.IsEmpty: boolean; +begin + result := FRoot = nil; +end; + +function TSuperAvlTree.balance(bal: TSuperAvlEntry): TSuperAvlEntry; +var + deep, old: TSuperAvlEntry; + bf: integer; +begin + if (bal.FBf > 0) then + begin + deep := bal.FGt; + if (deep.FBf < 0) then + begin + old := bal; + bal := deep.FLt; + old.FGt := bal.FLt; + deep.FLt := bal.FGt; + bal.FLt := old; + bal.FGt := deep; + bf := bal.FBf; + if (bf <> 0) then + begin + if (bf > 0) then + begin + old.FBf := -1; + deep.FBf := 0; + end else + begin + deep.FBf := 1; + old.FBf := 0; + end; + bal.FBf := 0; + end else + begin + old.FBf := 0; + deep.FBf := 0; + end; + end else + begin + bal.FGt := deep.FLt; + deep.FLt := bal; + if (deep.FBf = 0) then + begin + deep.FBf := -1; + bal.FBf := 1; + end else + begin + deep.FBf := 0; + bal.FBf := 0; + end; + bal := deep; + end; + end else + begin + (* "Less than" subtree is deeper. *) + + deep := bal.FLt; + if (deep.FBf > 0) then + begin + old := bal; + bal := deep.FGt; + old.FLt := bal.FGt; + deep.FGt := bal.FLt; + bal.FGt := old; + bal.FLt := deep; + + bf := bal.FBf; + if (bf <> 0) then + begin + if (bf < 0) then + begin + old.FBf := 1; + deep.FBf := 0; + end else + begin + deep.FBf := -1; + old.FBf := 0; + end; + bal.FBf := 0; + end else + begin + old.FBf := 0; + deep.FBf := 0; + end; + end else + begin + bal.FLt := deep.FGt; + deep.FGt := bal; + if (deep.FBf = 0) then + begin + deep.FBf := 1; + bal.FBf := -1; + end else + begin + deep.FBf := 0; + bal.FBf := 0; + end; + bal := deep; + end; + end; + Result := bal; +end; + +function TSuperAvlTree.Insert(h: TSuperAvlEntry): TSuperAvlEntry; +var + unbal, parentunbal, hh, parent: TSuperAvlEntry; + depth, unbaldepth: longint; + cmp: integer; + unbalbf: integer; + branch: TSuperAvlBitArray; + p: Pointer; +begin + inc(FCount); + h.FLt := nil; + h.FGt := nil; + h.FBf := 0; + branch := []; + + if (FRoot = nil) then + FRoot := h + else + begin + unbal := nil; + parentunbal := nil; + depth := 0; + unbaldepth := 0; + hh := FRoot; + parent := nil; + repeat + if (hh.FBf <> 0) then + begin + unbal := hh; + parentunbal := parent; + unbaldepth := depth; + end; + if hh.FHash <> h.FHash then + begin + if hh.FHash < h.FHash then cmp := -1 else + if hh.FHash > h.FHash then cmp := 1 else + cmp := 0; + end else + cmp := CompareNodeNode(h, hh); + if (cmp = 0) then + begin + Result := hh; + //exchange data + p := hh.Ptr; + hh.FPtr := h.Ptr; + h.FPtr := p; + doDeleteEntry(h, false); + dec(FCount); + exit; + end; + parent := hh; + if (cmp > 0) then + begin + hh := hh.FGt; + include(branch, depth); + end else + begin + hh := hh.FLt; + exclude(branch, depth); + end; + inc(depth); + until (hh = nil); + + if (cmp < 0) then + parent.FLt := h else + parent.FGt := h; + + depth := unbaldepth; + + if (unbal = nil) then + hh := FRoot + else + begin + if depth in branch then + cmp := 1 else + cmp := -1; + inc(depth); + unbalbf := unbal.FBf; + if (cmp < 0) then + dec(unbalbf) else + inc(unbalbf); + if cmp < 0 then + hh := unbal.FLt else + hh := unbal.FGt; + if ((unbalbf <> -2) and (unbalbf <> 2)) then + begin + unbal.FBf := unbalbf; + unbal := nil; + end; + end; + + if (hh <> nil) then + while (h <> hh) do + begin + if depth in branch then + cmp := 1 else + cmp := -1; + inc(depth); + if (cmp < 0) then + begin + hh.FBf := -1; + hh := hh.FLt; + end else (* cmp > 0 *) + begin + hh.FBf := 1; + hh := hh.FGt; + end; + end; + + if (unbal <> nil) then + begin + unbal := balance(unbal); + if (parentunbal = nil) then + FRoot := unbal + else + begin + depth := unbaldepth - 1; + if depth in branch then + cmp := 1 else + cmp := -1; + if (cmp < 0) then + parentunbal.FLt := unbal else + parentunbal.FGt := unbal; + end; + end; + end; + result := h; +end; + +function TSuperAvlTree.Search(const k: SOString; st: TSuperAvlSearchTypes): TSuperAvlEntry; +var + cmp, target_cmp: integer; + match_h, h: TSuperAvlEntry; + ha: Cardinal; +begin + ha := TSuperAvlEntry.Hash(k); + + match_h := nil; + h := FRoot; + + if (stLess in st) then + target_cmp := 1 else + if (stGreater in st) then + target_cmp := -1 else + target_cmp := 0; + + while (h <> nil) do + begin + if h.FHash < ha then cmp := -1 else + if h.FHash > ha then cmp := 1 else + cmp := 0; + + if cmp = 0 then + cmp := CompareKeyNode(PSOChar(k), h); + if (cmp = 0) then + begin + if (stEqual in st) then + begin + match_h := h; + break; + end; + cmp := -target_cmp; + end + else + if (target_cmp <> 0) then + if ((cmp xor target_cmp) and SUPER_AVL_MASK_HIGH_BIT) = 0 then + match_h := h; + if cmp < 0 then + h := h.FLt else + h := h.FGt; + end; + result := match_h; +end; + +function TSuperAvlTree.Delete(const k: SOString): ISuperObject; +var + depth, rm_depth: longint; + branch: TSuperAvlBitArray; + h, parent, child, path, rm, parent_rm: TSuperAvlEntry; + cmp, cmp_shortened_sub_with_path, reduced_depth, bf: integer; + ha: Cardinal; +begin + ha := TSuperAvlEntry.Hash(k); + cmp_shortened_sub_with_path := 0; + branch := []; + + depth := 0; + h := FRoot; + parent := nil; + while true do + begin + if (h = nil) then + exit; + if h.FHash < ha then cmp := -1 else + if h.FHash > ha then cmp := 1 else + cmp := 0; + + if cmp = 0 then + cmp := CompareKeyNode(k, h); + if (cmp = 0) then + break; + parent := h; + if (cmp > 0) then + begin + h := h.FGt; + include(branch, depth) + end else + begin + h := h.FLt; + exclude(branch, depth) + end; + inc(depth); + cmp_shortened_sub_with_path := cmp; + end; + rm := h; + parent_rm := parent; + rm_depth := depth; + + if (h.FBf < 0) then + begin + child := h.FLt; + exclude(branch, depth); + cmp := -1; + end else + begin + child := h.FGt; + include(branch, depth); + cmp := 1; + end; + inc(depth); + + if (child <> nil) then + begin + cmp := -cmp; + repeat + parent := h; + h := child; + if (cmp < 0) then + begin + child := h.FLt; + exclude(branch, depth); + end else + begin + child := h.FGt; + include(branch, depth); + end; + inc(depth); + until (child = nil); + + if (parent = rm) then + cmp_shortened_sub_with_path := -cmp else + cmp_shortened_sub_with_path := cmp; + + if cmp > 0 then + child := h.FLt else + child := h.FGt; + end; + + if (parent = nil) then + FRoot := child else + if (cmp_shortened_sub_with_path < 0) then + parent.FLt := child else + parent.FGt := child; + + if parent = rm then + path := h else + path := parent; + + if (h <> rm) then + begin + h.FLt := rm.FLt; + h.FGt := rm.FGt; + h.FBf := rm.FBf; + if (parent_rm = nil) then + FRoot := h + else + begin + depth := rm_depth - 1; + if (depth in branch) then + parent_rm.FGt := h else + parent_rm.FLt := h; + end; + end; + + if (path <> nil) then + begin + h := FRoot; + parent := nil; + depth := 0; + while (h <> path) do + begin + if (depth in branch) then + begin + child := h.FGt; + h.FGt := parent; + end else + begin + child := h.FLt; + h.FLt := parent; + end; + inc(depth); + parent := h; + h := child; + end; + + reduced_depth := 1; + cmp := cmp_shortened_sub_with_path; + while true do + begin + if (reduced_depth <> 0) then + begin + bf := h.FBf; + if (cmp < 0) then + inc(bf) else + dec(bf); + if ((bf = -2) or (bf = 2)) then + begin + h := balance(h); + bf := h.FBf; + end else + h.FBf := bf; + reduced_depth := integer(bf = 0); + end; + if (parent = nil) then + break; + child := h; + h := parent; + dec(depth); + if depth in branch then + cmp := 1 else + cmp := -1; + if (cmp < 0) then + begin + parent := h.FLt; + h.FLt := child; + end else + begin + parent := h.FGt; + h.FGt := child; + end; + end; + FRoot := h; + end; + if rm <> nil then + begin + Result := rm.GetValue; + doDeleteEntry(rm, false); + dec(FCount); + end; +end; + +procedure TSuperAvlTree.Pack(all: boolean); +var + node1, node2: TSuperAvlEntry; + list: TList; + i: Integer; +begin + node1 := FRoot; + list := TList.Create; + while node1 <> nil do + begin + if (node1.FLt = nil) then + begin + node2 := node1.FGt; + if (node1.FPtr = nil) then + list.Add(node1) else + if all then + node1.Value.Pack(all); + end + else + begin + node2 := node1.FLt; + node1.FLt := node2.FGt; + node2.FGt := node1; + end; + node1 := node2; + end; + for i := 0 to list.Count - 1 do + Delete(TSuperAvlEntry(list[i]).FName); + list.Free; +end; + +procedure TSuperAvlTree.Clear(all: boolean); +var + node1, node2: TSuperAvlEntry; +begin + node1 := FRoot; + while node1 <> nil do + begin + if (node1.FLt = nil) then + begin + node2 := node1.FGt; + doDeleteEntry(node1, all); + end + else + begin + node2 := node1.FLt; + node1.FLt := node2.FGt; + node2.FGt := node1; + end; + node1 := node2; + end; + FRoot := nil; + FCount := 0; +end; + +function TSuperAvlTree.CompareKeyNode(const k: SOString; h: TSuperAvlEntry): integer; +begin + Result := StrComp(PSOChar(k), PSOChar(h.FName)); +end; + +function TSuperAvlTree.CompareNodeNode(node1, node2: TSuperAvlEntry): integer; +begin + Result := StrComp(PSOChar(node1.FName), PSOChar(node2.FName)); +end; + +{ TSuperAvlIterator } + +(* Initialize depth to invalid value, to indicate iterator is +** invalid. (Depth is zero-base.) It's not necessary to initialize +** iterators prior to passing them to the "start" function. +*) + +constructor TSuperAvlIterator.Create(tree: TSuperAvlTree); +begin + FDepth := not 0; + FTree := tree; +end; + +procedure TSuperAvlIterator.Search(const k: SOString; st: TSuperAvlSearchTypes); +var + h: TSuperAvlEntry; + d: longint; + cmp, target_cmp: integer; + ha: Cardinal; +begin + ha := TSuperAvlEntry.Hash(k); + h := FTree.FRoot; + d := 0; + FDepth := not 0; + if (h = nil) then + exit; + + if (stLess in st) then + target_cmp := 1 else + if (stGreater in st) then + target_cmp := -1 else + target_cmp := 0; + + while true do + begin + if h.FHash < ha then cmp := -1 else + if h.FHash > ha then cmp := 1 else + cmp := 0; + + if cmp = 0 then + cmp := FTree.CompareKeyNode(k, h); + if (cmp = 0) then + begin + if (stEqual in st) then + begin + FDepth := d; + break; + end; + cmp := -target_cmp; + end + else + if (target_cmp <> 0) then + if ((cmp xor target_cmp) and SUPER_AVL_MASK_HIGH_BIT) = 0 then + FDepth := d; + if cmp < 0 then + h := h.FLt else + h := h.FGt; + if (h = nil) then + break; + if (cmp > 0) then + include(FBranch, d) else + exclude(FBranch, d); + FPath[d] := h; + inc(d); + end; +end; + +procedure TSuperAvlIterator.First; +var + h: TSuperAvlEntry; +begin + h := FTree.FRoot; + FDepth := not 0; + FBranch := []; + while (h <> nil) do + begin + if (FDepth <> not 0) then + FPath[FDepth] := h; + inc(FDepth); + h := h.FLt; + end; +end; + +procedure TSuperAvlIterator.Last; +var + h: TSuperAvlEntry; +begin + h := FTree.FRoot; + FDepth := not 0; + FBranch := [0..SUPER_AVL_MAX_DEPTH - 1]; + while (h <> nil) do + begin + if (FDepth <> not 0) then + FPath[FDepth] := h; + inc(FDepth); + h := h.FGt; + end; +end; + +function TSuperAvlIterator.MoveNext: boolean; +begin + if FDepth = not 0 then + First else + Next; + Result := GetIter <> nil; +end; + +function TSuperAvlIterator.GetIter: TSuperAvlEntry; +begin + if (FDepth = not 0) then + begin + result := nil; + exit; + end; + if FDepth = 0 then + Result := FTree.FRoot else + Result := FPath[FDepth - 1]; +end; + +procedure TSuperAvlIterator.Next; +var + h: TSuperAvlEntry; +begin + if (FDepth <> not 0) then + begin + if FDepth = 0 then + h := FTree.FRoot.FGt else + h := FPath[FDepth - 1].FGt; + + if (h = nil) then + repeat + if (FDepth = 0) then + begin + FDepth := not 0; + break; + end; + dec(FDepth); + until (not (FDepth in FBranch)) + else + begin + include(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + while true do + begin + h := h.FLt; + if (h = nil) then + break; + exclude(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + end; + end; + end; +end; + +procedure TSuperAvlIterator.Prior; +var + h: TSuperAvlEntry; +begin + if (FDepth <> not 0) then + begin + if FDepth = 0 then + h := FTree.FRoot.FLt else + h := FPath[FDepth - 1].FLt; + if (h = nil) then + repeat + if (FDepth = 0) then + begin + FDepth := not 0; + break; + end; + dec(FDepth); + until (FDepth in FBranch) + else + begin + exclude(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + while true do + begin + h := h.FGt; + if (h = nil) then + break; + include(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + end; + end; + end; +end; + +procedure TSuperAvlTree.doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); +begin + Entry.Free; +end; + +function TSuperAvlTree.GetEnumerator: TSuperAvlIterator; +begin + Result := TSuperAvlIterator.Create(Self); +end; + +{ TSuperAvlEntry } + +constructor TSuperAvlEntry.Create(const AName: SOString; Obj: Pointer); +begin + FName := AName; + FPtr := Obj; + FHash := Hash(FName); +end; + +function TSuperAvlEntry.GetValue: ISuperObject; +begin + Result := ISuperObject(FPtr) +end; + +class function TSuperAvlEntry.Hash(const k: SOString): Cardinal; +var + h: cardinal; + i: Integer; +begin + h := 0; +{$Q-} + for i := 1 to Length(k) do + h := h*129 + ord(k[i]) + $9e370001; +{$Q+} + Result := h; +end; + +procedure TSuperAvlEntry.SetValue(const val: ISuperObject); +begin + ISuperObject(FPtr) := val; +end; + +{ TSuperTableString } + +function TSuperTableString.GetValues: ISuperObject; +var + ite: TSuperAvlIterator; + obj: TSuperAvlEntry; +begin + Result := TSuperObject.Create(stArray); + ite := TSuperAvlIterator.Create(Self); + try + ite.First; + obj := ite.GetIter; + while obj <> nil do + begin + Result.AsArray.Add(obj.Value); + ite.Next; + obj := ite.GetIter; + end; + finally + ite.Free; + end; +end; + +function TSuperTableString.GetNames: ISuperObject; +var + ite: TSuperAvlIterator; + obj: TSuperAvlEntry; +begin + Result := TSuperObject.Create(stArray); + ite := TSuperAvlIterator.Create(Self); + try + ite.First; + obj := ite.GetIter; + while obj <> nil do + begin + Result.AsArray.Add(TSuperObject.Create(obj.FName)); + ite.Next; + obj := ite.GetIter; + end; + finally + ite.Free; + end; +end; + +procedure TSuperTableString.doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); +begin + if Entry.Ptr <> nil then + begin + if all then Entry.Value.Clear(true); + Entry.Value := nil; + end; + inherited; +end; + +function TSuperTableString.GetO(const k: SOString): ISuperObject; +var + e: TSuperAvlEntry; +begin + e := Search(k); + if e <> nil then + Result := e.Value else + Result := nil +end; + +procedure TSuperTableString.PutO(const k: SOString; const value: ISuperObject); +var + entry: TSuperAvlEntry; +begin + entry := Insert(TSuperAvlEntry.Create(k, Pointer(value))); + if entry.FPtr <> nil then + ISuperObject(entry.FPtr)._AddRef; +end; + +procedure TSuperTableString.PutS(const k: SOString; const value: SOString); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +function TSuperTableString.GetS(const k: SOString): SOString; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsString else + Result := ''; +end; + +procedure TSuperTableString.PutI(const k: SOString; value: SuperInt); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +function TSuperTableString.GetI(const k: SOString): SuperInt; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsInteger else + Result := 0; +end; + +procedure TSuperTableString.PutD(const k: SOString; value: Double); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +procedure TSuperTableString.PutC(const k: SOString; value: Currency); +begin + PutO(k, TSuperObject.CreateCurrency(Value)); +end; + +function TSuperTableString.GetC(const k: SOString): Currency; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsCurrency else + Result := 0.0; +end; + +function TSuperTableString.GetD(const k: SOString): Double; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsDouble else + Result := 0.0; +end; + +procedure TSuperTableString.PutB(const k: SOString; value: Boolean); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +function TSuperTableString.GetB(const k: SOString): Boolean; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsBoolean else + Result := False; +end; + +{$IFDEF SUPER_METHOD} +procedure TSuperTableString.PutM(const k: SOString; value: TSuperMethod); +begin + PutO(k, TSuperObject.Create(Value)); +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperTableString.GetM(const k: SOString): TSuperMethod; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsMethod else + Result := nil; +end; +{$ENDIF} + +procedure TSuperTableString.PutN(const k: SOString; const value: ISuperObject); +begin + if value <> nil then + PutO(k, TSuperObject.Create(stNull)) else + PutO(k, value); +end; + +function TSuperTableString.GetN(const k: SOString): ISuperObject; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj else + Result := TSuperObject.Create(stNull); +end; + + +{$IFDEF VER210} + +{ TSuperAttribute } + +constructor TSuperAttribute.Create(const AName: string); +begin + FName := AName; +end; + +{ TSuperRttiContext } + +constructor TSuperRttiContext.Create; +begin + Context := TRttiContext.Create; + SerialFromJson := TDictionary.Create; + SerialToJson := TDictionary.Create; + + SerialFromJson.Add(TypeInfo(Boolean), serialfromboolean); + SerialFromJson.Add(TypeInfo(TDateTime), serialfromdatetime); + SerialFromJson.Add(TypeInfo(TGUID), serialfromguid); + SerialToJson.Add(TypeInfo(Boolean), serialtoboolean); + SerialToJson.Add(TypeInfo(TDateTime), serialtodatetime); + SerialToJson.Add(TypeInfo(TGUID), serialtoguid); +end; + +destructor TSuperRttiContext.Destroy; +begin + SerialFromJson.Free; + SerialToJson.Free; + Context.Free; +end; + +class function TSuperRttiContext.GetFieldName(r: TRttiField): string; +var + o: TCustomAttribute; +begin + for o in r.GetAttributes do + if o is SOName then + Exit(SOName(o).Name); + Result := r.Name; +end; + +class function TSuperRttiContext.GetFieldDefault(r: TRttiField; const obj: ISuperObject): ISuperObject; +var + o: TCustomAttribute; +begin + if not ObjectIsType(obj, stNull) then Exit(obj); + for o in r.GetAttributes do + if o is SODefault then + Exit(SO(SODefault(o).Name)); + Result := obj; +end; + +function TSuperRttiContext.AsType(const obj: ISuperObject): T; +var + ret: TValue; +begin + if FromJson(TypeInfo(T), obj, ret) then + Result := ret.AsType else + raise exception.Create('Marshalling error'); +end; + +function TSuperRttiContext.AsJson(const obj: T; const index: ISuperObject = nil): ISuperObject; +var + v: TValue; +begin + TValue.MakeWithoutCopy(@obj, TypeInfo(T), v); + if index <> nil then + Result := ToJson(v, index) else + Result := ToJson(v, so); +end; + +function TSuperRttiContext.FromJson(TypeInfo: PTypeInfo; const obj: ISuperObject; + var Value: TValue): Boolean; + + procedure FromChar; + begin + if ObjectIsType(obj, stString) and (Length(obj.AsString) = 1) then + begin + Value := string(AnsiString(obj.AsString)[1]); + Result := True; + end else + Result := False; + end; + + procedure FromWideChar; + begin + if ObjectIsType(obj, stString) and (Length(obj.AsString) = 1) then + begin + Value := obj.AsString[1]; + Result := True; + end else + Result := False; + end; + + procedure FromInt64; + var + i: Int64; + begin + case ObjectGetType(obj) of + stInt: + begin + TValue.Make(nil, TypeInfo, Value); + TValueData(Value).FAsSInt64 := obj.AsInteger; + Result := True; + end; + stString: + begin + if TryStrToInt64(obj.AsString, i) then + begin + TValue.Make(nil, TypeInfo, Value); + TValueData(Value).FAsSInt64 := i; + Result := True; + end else + Result := False; + end; + else + Result := False; + end; + end; + + procedure FromInt(const obj: ISuperObject); + var + TypeData: PTypeData; + i: Integer; + o: ISuperObject; + begin + case ObjectGetType(obj) of + stInt, stBoolean: + begin + i := obj.AsInteger; + TypeData := GetTypeData(TypeInfo); + Result := (i >= TypeData.MinValue) and (i <= TypeData.MaxValue); + if Result then + TValue.Make(@i, TypeInfo, Value); + end; + stString: + begin + o := SO(obj.AsString); + if not ObjectIsType(o, stString) then + FromInt(o) else + Result := False; + end; + else + Result := False; + end; + end; + + procedure fromSet; + begin + if ObjectIsType(obj, stInt) then + begin + TValue.Make(nil, TypeInfo, Value); + TValueData(Value).FAsSLong := obj.AsInteger; + Result := True; + end else + Result := False; + end; + + procedure FromFloat(const obj: ISuperObject); + var + o: ISuperObject; + begin + case ObjectGetType(obj) of + stInt, stDouble, stCurrency: + begin + TValue.Make(nil, TypeInfo, Value); + case GetTypeData(TypeInfo).FloatType of + ftSingle: TValueData(Value).FAsSingle := obj.AsDouble; + ftDouble: TValueData(Value).FAsDouble := obj.AsDouble; + ftExtended: TValueData(Value).FAsExtended := obj.AsDouble; + ftComp: TValueData(Value).FAsSInt64 := obj.AsInteger; + ftCurr: TValueData(Value).FAsCurr := obj.AsCurrency; + end; + Result := True; + end; + stString: + begin + o := SO(obj.AsString); + if not ObjectIsType(o, stString) then + FromFloat(o) else + Result := False; + end + else + Result := False; + end; + end; + + procedure FromString; + begin + case ObjectGetType(obj) of + stObject, stArray: + Result := False; + stnull: + begin + Value := ''; + Result := True; + end; + else + Value := obj.AsString; + Result := True; + end; + end; + + procedure FromClass; + var + f: TRttiField; + v: TValue; + begin + case ObjectGetType(obj) of + stObject: + begin + Result := True; + if Value.Kind <> tkClass then + Value := GetTypeData(TypeInfo).ClassType.Create; + for f in Context.GetType(Value.AsObject.ClassType).GetFields do + if f.FieldType <> nil then + begin + Result := FromJson(f.FieldType.Handle, GetFieldDefault(f, obj.AsObject[GetFieldName(f)]), v); + if Result then + f.SetValue(Value.AsObject, v) else + Exit; + end; + end; + stNull: + begin + Value := nil; + Result := True; + end + else + // error + Value := nil; + Result := False; + end; + end; + + procedure FromRecord; + var + f: TRttiField; + p: Pointer; + v: TValue; + begin + Result := True; + TValue.Make(nil, TypeInfo, Value); + for f in Context.GetType(TypeInfo).GetFields do + begin + if ObjectIsType(obj, stObject) and (f.FieldType <> nil) then + begin + p := IValueData(TValueData(Value).FHeapData).GetReferenceToRawData; + Result := FromJson(f.FieldType.Handle, GetFieldDefault(f, obj.AsObject[GetFieldName(f)]), v); + if Result then + f.SetValue(p, v) else + Exit; + end else + begin + Result := False; + Exit; + end; + end; + end; + + procedure FromDynArray; + var + i: Integer; + p: Pointer; + pb: PByte; + val: TValue; + typ: PTypeData; + el: PTypeInfo; + begin + case ObjectGetType(obj) of + stArray: + begin + i := obj.AsArray.Length; + p := nil; + DynArraySetLength(p, TypeInfo, 1, @i); + pb := p; + typ := GetTypeData(TypeInfo); + if typ.elType <> nil then + el := typ.elType^ else + el := typ.elType2^; + + Result := True; + for i := 0 to i - 1 do + begin + Result := FromJson(el, obj.AsArray[i], val); + if not Result then + Break; + val.ExtractRawData(pb); + val := TValue.Empty; + Inc(pb, typ.elSize); + end; + if Result then + TValue.MakeWithoutCopy(@p, TypeInfo, Value) else + DynArrayClear(p, TypeInfo); + end; + stNull: + begin + TValue.MakeWithoutCopy(nil, TypeInfo, Value); + Result := True; + end; + else + i := 1; + p := nil; + DynArraySetLength(p, TypeInfo, 1, @i); + pb := p; + typ := GetTypeData(TypeInfo); + if typ.elType <> nil then + el := typ.elType^ else + el := typ.elType2^; + + Result := FromJson(el, obj, val); + val.ExtractRawData(pb); + val := TValue.Empty; + + if Result then + TValue.MakeWithoutCopy(@p, TypeInfo, Value) else + DynArrayClear(p, TypeInfo); + end; + end; + + procedure FromArray; + var + ArrayData: PArrayTypeData; + idx: Integer; + function ProcessDim(dim: Byte; const o: ISuperobject): Boolean; + var + i: Integer; + v: TValue; + a: PTypeData; + begin + if ObjectIsType(o, stArray) and (ArrayData.Dims[dim-1] <> nil) then + begin + a := @GetTypeData(ArrayData.Dims[dim-1]^).ArrayData; + if (a.MaxValue - a.MinValue + 1) <> o.AsArray.Length then + begin + Result := False; + Exit; + end; + Result := True; + if dim = ArrayData.DimCount then + for i := a.MinValue to a.MaxValue do + begin + Result := FromJson(ArrayData.ElType^, o.AsArray[i], v); + if not Result then + Exit; + Value.SetArrayElement(idx, v); + inc(idx); + end + else + for i := a.MinValue to a.MaxValue do + begin + Result := ProcessDim(dim + 1, o.AsArray[i]); + if not Result then + Exit; + end; + end else + Result := False; + end; + var + i: Integer; + v: TValue; + begin + TValue.Make(nil, TypeInfo, Value); + ArrayData := @GetTypeData(TypeInfo).ArrayData; + idx := 0; + if ArrayData.DimCount = 1 then + begin + if ObjectIsType(obj, stArray) and (obj.AsArray.Length = ArrayData.ElCount) then + begin + Result := True; + for i := 0 to ArrayData.ElCount - 1 do + begin + Result := FromJson(ArrayData.ElType^, obj.AsArray[i], v); + if not Result then + Exit; + Value.SetArrayElement(idx, v); + v := TValue.Empty; + inc(idx); + end; + end else + Result := False; + end else + Result := ProcessDim(1, obj); + end; + + procedure FromClassRef; + var + r: TRttiType; + begin + if ObjectIsType(obj, stString) then + begin + r := Context.FindType(obj.AsString); + if r <> nil then + begin + Value := TRttiInstanceType(r).MetaclassType; + Result := True; + end else + Result := False; + end else + Result := False; + end; + + procedure FromUnknown; + begin + case ObjectGetType(obj) of + stBoolean: + begin + Value := obj.AsBoolean; + Result := True; + end; + stDouble: + begin + Value := obj.AsDouble; + Result := True; + end; + stCurrency: + begin + Value := obj.AsCurrency; + Result := True; + end; + stInt: + begin + Value := obj.AsInteger; + Result := True; + end; + stString: + begin + Value := obj.AsString; + Result := True; + end + else + Value := nil; + Result := False; + end; + end; + + procedure FromInterface; + const soguid: TGuid = '{4B86A9E3-E094-4E5A-954A-69048B7B6327}'; + var + o: ISuperObject; + begin + if CompareMem(@GetTypeData(TypeInfo).Guid, @soguid, SizeOf(TGUID)) then + begin + if obj <> nil then + TValue.Make(@obj, TypeInfo, Value) else + begin + o := TSuperObject.Create(stNull); + TValue.Make(@o, TypeInfo, Value); + end; + Result := True; + end else + Result := False; + end; +var + Serial: TSerialFromJson; +begin + if TypeInfo <> nil then + begin + if not SerialFromJson.TryGetValue(TypeInfo, Serial) then + case TypeInfo.Kind of + tkChar: FromChar; + tkInt64: FromInt64; + tkEnumeration, tkInteger: FromInt(obj); + tkSet: fromSet; + tkFloat: FromFloat(obj); + tkString, tkLString, tkUString, tkWString: FromString; + tkClass: FromClass; + tkMethod: ; + tkWChar: FromWideChar; + tkRecord: FromRecord; + tkPointer: ; + tkInterface: FromInterface; + tkArray: FromArray; + tkDynArray: FromDynArray; + tkClassRef: FromClassRef; + else + FromUnknown + end else + begin + TValue.Make(nil, TypeInfo, Value); + Result := Serial(Self, obj, Value); + end; + end else + Result := False; +end; + +function TSuperRttiContext.ToJson(var value: TValue; const index: ISuperObject): ISuperObject; + procedure ToInt64; + begin + Result := TSuperObject.Create(SuperInt(Value.AsInt64)); + end; + + procedure ToChar; + begin + Result := TSuperObject.Create(string(Value.AsType)); + end; + + procedure ToInteger; + begin + Result := TSuperObject.Create(TValueData(Value).FAsSLong); + end; + + procedure ToFloat; + begin + case Value.TypeData.FloatType of + ftSingle: Result := TSuperObject.Create(TValueData(Value).FAsSingle); + ftDouble: Result := TSuperObject.Create(TValueData(Value).FAsDouble); + ftExtended: Result := TSuperObject.Create(TValueData(Value).FAsExtended); + ftComp: Result := TSuperObject.Create(TValueData(Value).FAsSInt64); + ftCurr: Result := TSuperObject.CreateCurrency(TValueData(Value).FAsCurr); + end; + end; + + procedure ToString; + begin + Result := TSuperObject.Create(string(Value.AsType)); + end; + + procedure ToClass; + var + o: ISuperObject; + f: TRttiField; + v: TValue; + begin + if TValueData(Value).FAsObject <> nil then + begin + o := index[IntToStr(Integer(Value.AsObject))]; + if o = nil then + begin + Result := TSuperObject.Create(stObject); + index[IntToStr(Integer(Value.AsObject))] := Result; + for f in Context.GetType(Value.AsObject.ClassType).GetFields do + if f.FieldType <> nil then + begin + v := f.GetValue(Value.AsObject); + Result.AsObject[GetFieldName(f)] := ToJson(v, index); + end + end else + Result := o; + end else + Result := nil; + end; + + procedure ToWChar; + begin + Result := TSuperObject.Create(string(Value.AsType)); + end; + + procedure ToVariant; + begin + Result := SO(Value.AsVariant); + end; + + procedure ToRecord; + var + f: TRttiField; + v: TValue; + begin + Result := TSuperObject.Create(stObject); + for f in Context.GetType(Value.TypeInfo).GetFields do + begin + v := f.GetValue(IValueData(TValueData(Value).FHeapData).GetReferenceToRawData); + Result.AsObject[GetFieldName(f)] := ToJson(v, index); + end; + end; + + procedure ToArray; + var + idx: Integer; + ArrayData: PArrayTypeData; + + procedure ProcessDim(dim: Byte; const o: ISuperObject); + var + dt: PTypeData; + i: Integer; + o2: ISuperObject; + v: TValue; + begin + if ArrayData.Dims[dim-1] = nil then Exit; + dt := GetTypeData(ArrayData.Dims[dim-1]^); + if Dim = ArrayData.DimCount then + for i := dt.MinValue to dt.MaxValue do + begin + v := Value.GetArrayElement(idx); + o.AsArray.Add(toJSon(v, index)); + inc(idx); + end + else + for i := dt.MinValue to dt.MaxValue do + begin + o2 := TSuperObject.Create(stArray); + o.AsArray.Add(o2); + ProcessDim(dim + 1, o2); + end; + end; + var + i: Integer; + v: TValue; + begin + Result := TSuperObject.Create(stArray); + ArrayData := @Value.TypeData.ArrayData; + idx := 0; + if ArrayData.DimCount = 1 then + for i := 0 to ArrayData.ElCount - 1 do + begin + v := Value.GetArrayElement(i); + Result.AsArray.Add(toJSon(v, index)) + end + else + ProcessDim(1, Result); + end; + + procedure ToDynArray; + var + i: Integer; + v: TValue; + begin + Result := TSuperObject.Create(stArray); + for i := 0 to Value.GetArrayLength - 1 do + begin + v := Value.GetArrayElement(i); + Result.AsArray.Add(toJSon(v, index)); + end; + end; + + procedure ToClassRef; + begin + if TValueData(Value).FAsClass <> nil then + Result := TSuperObject.Create(string( + TValueData(Value).FAsClass.UnitName + '.' + + TValueData(Value).FAsClass.ClassName)) else + Result := nil; + end; + + procedure ToInterface; + begin + if TValueData(Value).FHeapData <> nil then + TValueData(Value).FHeapData.QueryInterface(ISuperObject, Result) else + Result := nil; + end; + +var + Serial: TSerialToJson; +begin + if not SerialToJson.TryGetValue(value.TypeInfo, Serial) then + case Value.Kind of + tkInt64: ToInt64; + tkChar: ToChar; + tkSet, tkInteger, tkEnumeration: ToInteger; + tkFloat: ToFloat; + tkString, tkLString, tkUString, tkWString: ToString; + tkClass: ToClass; + tkWChar: ToWChar; + tkVariant: ToVariant; + tkRecord: ToRecord; + tkArray: ToArray; + tkDynArray: ToDynArray; + tkClassRef: ToClassRef; + tkInterface: ToInterface; + else + result := nil; + end else + Result := Serial(Self, value, index); +end; + +{ TSuperObjectHelper } + +constructor TSuperObjectHelper.FromJson(const obj: ISuperObject; ctx: TSuperRttiContext = nil); +var + v: TValue; + ctxowned: Boolean; +begin + if ctx = nil then + begin + ctx := TSuperRttiContext.Create; + ctxowned := True; + end else + ctxowned := False; + try + v := Self; + if not ctx.FromJson(v.TypeInfo, obj, v) then + raise Exception.Create('Invalid object'); + finally + if ctxowned then + ctx.Free; + end; +end; + +constructor TSuperObjectHelper.FromJson(const str: string; ctx: TSuperRttiContext = nil); +begin + FromJson(SO(str), ctx); +end; + +function TSuperObjectHelper.ToJson(ctx: TSuperRttiContext = nil): ISuperObject; +var + v: TValue; + ctxowned: boolean; +begin + if ctx = nil then + begin + ctx := TSuperRttiContext.Create; + ctxowned := True; + end else + ctxowned := False; + try + v := Self; + Result := ctx.ToJson(v, SO); + finally + if ctxowned then + ctx.Free; + end; +end; + +{$ENDIF} + +{$IFDEF DEBUG} +initialization + +finalization + Assert(debugcount = 0, 'Memory leak'); +{$ENDIF} +end. + diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uJSON.pas" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uJSON.pas" new file mode 100644 index 0000000..4ccdf95 --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uJSON.pas" @@ -0,0 +1,4389 @@ +{ + Copyright (C) 2005 Fabio Almeida + fabiorecife@yahoo.com.br + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Autor : Jose Fabio Nascimento de Almeida + Data : 7/11/2005 + + +Change Logs: +2013-11-04 By yangyxd + parent, child֧֡int64֧ + +2009-11-22 By creation_zy + Can parse #10 #13 inside a string. + JSONObject.quote method can deal with special character smaller than space. + Value inside a _String object can Read/Write directly. + +2011-09-02 By creation_zy + Add _Object to store common Object. + +2011-09-20 By creation_zy + Add SafeFreeJObj. + Add "inline" directive. + +2011-12-15 By creation_zy + Add SpaceStr function to optimize toString3. +} +unit uJSON; + +interface + +uses + Windows,SysUtils, Classes, TypInfo; + +{$DEFINE J_OBJECT} // store common Object +{$IF COMPILERVERSION>=18}{$DEFINE INLINE_OPT}{$IFEND} +{$DEFINE BACK_OPT} +{$DEFINE NEXT_OPT} + + +Type + JSONArray = class ; + JSONBase = class; + JSONObject = class; + + TZAbstractObject = class (TObject) + class procedure WriteChar(avOut: TStream; const avData: Char); + class procedure WriteString(avOut: TStream; const avData: string); + class procedure WriteText(avOut: TStream; const avData: string; len: Integer); + + function Equals(const Value: TZAbstractObject): Boolean; virtual; + function Hash: LongInt; + function Clone: TZAbstractObject; virtual; + function toString: string; virtual; + function toJSONObject: JSONObject; + function toJSONArray: JSONArray; + function instanceOf(const Value: TZAbstractObject): Boolean; + procedure SaveToStream(stream: TStream); virtual; + class function getInt(o: TZAbstractObject; DefaultValue: Integer):Integer; + class function getInt64(o: TZAbstractObject; DefaultValue: Int64): Int64; + class function getDouble(o: TZAbstractObject; DefaultValue: Double):Double; + class function getBoolean(o: TZAbstractObject; DefaultValue: Boolean):Boolean; + procedure Free; overload; //2011-10-10 Call SafeFreeJObj + end; + + ClassCastException = class (Exception) end; + NoSuchElementException = class (Exception) end; + NumberFormatException = class (Exception) end; + NullPointerException = class (Exception) end; + NotImplmentedFeature = class (Exception) end; + _Number = class ; + _String = class; + _Double = class; + _NULL = class ; +{$IFDEF J_OBJECT} + _Object = class; //2011-08-09 +{$ENDIF} + + + ParseException = class (Exception) + constructor create (_message: string ; index: integer); + end; + JSONTokener = class (TZAbstractObject) + public + constructor create (const s: string); + procedure back();{$IFDEF INLINE_OPT}inline;{$ENDIF} + class function dehexchar(c: char) :integer; + function more :boolean;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function next(): char; overload ;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function next (c:char ): char; overload ;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function next (n:integer): string; overload ;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function nextClean (): char;//{$IFDEF INLINE_OPT}inline;{$ENDIF} + function nextString (quote: char): string;//{$IFDEF INLINE_OPT}inline;{$ENDIF} + function nextTo (d: char): string; overload ; + function nextTo (const delimiters: string): char; overload ; + function nextValue (parent: JSONBase): TZAbstractObject ;//{$IFDEF INLINE_OPT}inline;{$ENDIF} + procedure skipPast (const _to: string ) ; + function skipTo (_to: char ): char; + function syntaxError (const _message: string): ParseException; + function toString: string; override; + function unescape (const s: string): string; + private + myIndex, Len1: integer; + mySource: string; + end; + + // by yangyxd 2013.11.04 + JSONBase = class(TZAbstractObject) + private + FParent: JSONBase; + FName: string; + protected + function GetCount: Integer; virtual; + function GetChild(Index: Integer): TZAbstractObject; virtual; + procedure SetChild(Index: Integer; const Value: TZAbstractObject); virtual; + public + constructor Create; + function IndexOfObject(aobj: TObject): Integer; virtual; + property Count: Integer read GetCount; + property Parent: JSONBase read FParent write FParent; + property Name: string read FName write FName; + property Child[Index: Integer]: TZAbstractObject read GetChild write SetChild; + end; + + JSONObject = class (JSONBase) + private + myHashMap: TStringList; + function GetPropValues(const Key: String): String; + procedure SetPropValues(const Key: String; const Value: String); + procedure SetAsString(const Value: String); + function GetKeyByIndex(index: Integer): String; + procedure SetCascadeValueEx(const Value: String; const Keys: array of String; + StartIdx: Integer); + function GetValByIndex(index: Integer): String; + procedure UpdateByTokener(x: JSONTokener); + function GetValObjByIndex(index: Integer): TZAbstractObject; + protected + function GetCount: Integer; override; // by yangyxd + function GetChild(Index: Integer): TZAbstractObject; override; // by yangyxd + procedure SetChild(Index: Integer; const Value: TZAbstractObject); override; // by yangyxd + public + constructor Create; overload; + constructor Create (jo: JSONObject; sa: array of string); overload; + constructor Create (x: JSONTokener); overload; + constructor Create (map: TStringList); overload; + constructor Create (const s: string); overload; + constructor CreateInArray(Ay: JSONArray); + + function IndexOfObject(aobj: TObject): Integer; override; // by yangyxd + + procedure Clean; + function Clone: TZAbstractObject; override; + function Accumulate (const key: string; value: TZAbstractObject): JSONObject; + function Get (const key: string): TZAbstractObject; + function GetBoolean (const key: string): boolean; + function GetDouble (const key: string): double; + function GetInt (const key: string): integer; + function GetInt64 (const key: string): Int64; + function GetJSONArray (const key: string) :JSONArray; + function GetJSONObject (const key: string): JSONObject; + function GetString (const key: string): string; + function Has (const key: string): boolean; + function IsNull (const key: string): boolean; + function Keys: TStringList ; + function Length: integer; + function Names: JSONArray; + class function NumberToString (n: _Number): string; + class function ValueToString(value: TZAbstractObject): string; overload; + class procedure ValueToStream(value: TZAbstractObject; stream: TStream); overload; + class function ValueToString(value: TZAbstractObject; + indentFactor, indent: integer): string; overload; + + function NextSibling: JSONObject; + function UpSibling: JSONObject; + + function Opt (const key: string): TZAbstractObject; + function OptBoolean (const key: string): boolean; overload; + function OptBoolean (const key: string; defaultValue: boolean): boolean; overload; + function OptDouble (const key: string): double; overload; + function OptDouble (const key: string; defaultValue: double): double; overload; + function OptInt (const key: string): integer; overload; + function OptInt (const key: string; defaultValue: integer): integer; overload; + function OptInt64 (const key: string): int64; overload; + function OptInt64 (const key: string; defaultValue: int64): int64; overload; + function OptString (const key: string): string; overload; + function OptString (const key, defaultValue: string): string; overload; + + function OptJSONArray (const key: string): JSONArray; overload; + function OptJSONObject (const key: string): JSONObject; overload; + + function Put (const key: string; value: boolean): JSONObject; overload; + function Put (const key: string; value: double): JSONObject; overload; + function Put (const key: string; value: integer): JSONObject; overload; + function Put (const key: string; value: int64): JSONObject; overload; + function Put (const key: string; const value: string): JSONObject; overload; + function Put (const key: string; value: TZAbstractObject): JSONObject; overload; + + function PutOpt (const key: string; value: TZAbstractObject): JSONObject; + class function quote (const s: string): string; + class procedure quoteToStream (stream: TStream; const s: string); + function Remove (const key: string): TZAbstractObject; + procedure AssignTo(json: JSONObject); + + function ToJSONArray (names: JSONArray): JSONArray; + function toString (): string ; overload; override; + function toString2 (indentFactor: integer): string; overload; + function toString3 (indentFactor, indent: integer): string; overload; + + procedure SaveToStream(stream: TStream); override; + + //Add by creation_zy 2008-10-21 + property PropValues[const Key: String]:String read GetPropValues write SetPropValues; default; + property KeyByIndex[index: Integer]:String read GetKeyByIndex; + property ValByIndex[index: Integer]:String read GetValByIndex; + property ValObjByIndex[index: Integer]:TZAbstractObject read GetValObjByIndex; + property AsString:String read ToString write SetAsString; + procedure Assign(Source: JSONObject); + function Opt2(key, key2: string): TZAbstractObject; + function OptString2(key, key2: String; DefaultValue: String=''): String; + function OptInt2(key, key2: String; DefaultValue: Integer=0): Integer; + function GetCascadeValue(const Keys: array of String): String; + procedure SetCascadeValue(const Value: String; const Keys: array of String); + function GetCascadeValEx(const Keys: array of String): String; + function GetCascadeValObj(const Keys: array of String): TZAbstractObject; + function GetDiffFrom(Source: JSONObject; UseSrc: Boolean=true):JSONObject; + procedure Delete(index: Integer); + procedure RemoveByKeyHeader(const Header: String='~'); + function RemoveLastKey:TZAbstractObject; + procedure CleanKey(const Key: String); + function SetKey(idx: Integer; const Key: String):Boolean; + function PropCount:Integer; + function KeyByVal(const Value: String):String; + function PartExtract(KeyNames: TStrings; DoRemove: Boolean):JSONObject; + function ExtractAll:JSONObject; + function TryNewJSONArray(const Key: String):JSONArray; + function TryNewJSONObject(const Key: String):JSONObject; + //Add by creation_zy 2011-08-09 + {$IFDEF J_OBJECT} + function GetObject (const key: string): TObject; + function OptObject (const key: string): TObject; overload; + function OptObject (const key: string; defaultValue: TObject): TObject; overload; + function Put (const key: string; value: TObject): JSONObject; overload; + {$ENDIF} + + destructor Destroy;override; + class function NULL: _NULL; + end; + + JSONArray = class (JSONBase) + public + destructor Destroy ; override; + constructor Create ; overload; + constructor Create (collection: TList); overload; + constructor Create (x: JSONTokener); overload; + constructor Create (const s: string); overload; + + procedure Clean; //by creation_zy 2009-08-19 + function Clone: TZAbstractObject; override; //by creation_zy 2008-10-05 + function get (index: integer): TZAbstractObject; + function getBoolean (index: integer): boolean; + function getDouble (index: integer): double; + function getInt (index: integer): integer; + function getInt64 (index: integer): int64; + function getJSONArray (index: integer): JSONArray; + function getJSONObject (index: integer): JSONObject; + function getString (index: integer): string; + function isNull (index: integer): boolean; + function join (separator: string): string; + function length: integer; + function opt (index: integer): TZAbstractObject; + function optBoolean ( index: integer): boolean; overload; + function optBoolean ( index: integer; defaultValue: boolean): boolean; overload; + function optDouble (index: integer): double; overload; + function optDouble (index: integer; defaultValue :double ): double ; overload; + function optInt (index: integer): integer; overload; + function optInt (index: integer; defaultValue: integer): integer; overload; + function OptInt64 (index: integer): int64; overload; + function OptInt64 (index: integer; defaultValue: int64): int64; overload; + function optJSONArray (index: integer): JSONArray ; overload; + function optJSONObject (index: integer): JSONObject ; overload; + function optString (index: integer): string; overload; + function optString (index: integer; defaultValue: string): string; overload; + {$IFDEF J_OBJECT} + function optObject (index: integer): TObject; overload; + {$ENDIF} + function put ( value: boolean): JSONArray; overload ; + function put ( value: double ): JSONArray; overload ; + function put ( value: integer): JSONArray; overload ; + function put ( value: TZAbstractObject): JSONArray; overload ; + function put ( value: string): JSONArray; overload; + {$IFDEF J_OBJECT} + function put ( value: TObject): JSONArray; overload; + {$ENDIF} + function put ( index: integer ; value: boolean): JSONArray; overload ; + function put ( index: integer ; value: double): JSONArray; overload ; + function put ( index: integer ; value: integer): JSONArray; overload ; + function put ( index: integer ; value: int64): JSONArray; overload ; + function put ( index: integer ; value: TZAbstractObject): JSONArray; overload ; + function put ( index: integer; value: string): JSONArray; overload; + {$IFDEF J_OBJECT} + function put ( index: integer ; value: TObject): JSONArray; overload; + {$ENDIF} + function LastItem: TZAbstractObject; + function toJSONObject (names :JSONArray ): JSONObject ; overload ; + function toString: string; overload; override; + function toString2 (indentFactor: integer): string; overload; + function toString3 (indentFactor, indent: integer): string; overload; + function toList (): TList; + function appendJSONArray( value: JSONArray): Integer ; //2008-10-08 + procedure Assign( Source: JSONArray); + + function IndexOfObject(aobj: TObject): Integer; override; // by yangyxd + private + myArrayList: TList; + protected + function GetCount: Integer; override; // by yangyxd + function GetChild(Index: Integer): TZAbstractObject; override; // by yangyxd + procedure SetChild(Index: Integer; const Value: TZAbstractObject); override; // by yangyxd + end; + + + _Number = class (TZAbstractObject) + public + function doubleValue: double; virtual; abstract; + function intValue: integer; virtual; abstract; + function int64Value: Int64; virtual; abstract; // by yangyxd + end; + + _Boolean = class (TZAbstractObject) + public + class function _TRUE (): _Boolean; + class function _FALSE (): _Boolean; + class function valueOf (b: boolean): _Boolean; + constructor create (b: boolean); + function boolValue: Boolean; //By creation_zy 2008-10-06 + function toString (): string; override; + function Clone :TZAbstractObject; override; + private + fvalue: boolean; + end; + + _Double = class (_Number) + constructor create (const s: string); overload; + constructor create (s: _String); overload; + constructor create (d: double); overload; + function doubleValue: double; override; + function intValue: integer; override; + function int64Value: Int64; override; + function toString (): string ; override; + class function NaN: double; + function Clone :TZAbstractObject; override; + private + fvalue: double; + end; + + _Integer = class (_Number) + class function parseInt64 (const s: string): int64; overload; + class function parseInt64 (s: _String): int64; overload; + class function parseInt (const s: string; i: integer): integer; overload; + class function parseInt (s: _String): integer; overload; + class function toHexString (c: char): string; + constructor create (i: integer); overload; + constructor create (i: int64); overload; + constructor create (const s: string); overload; + function doubleValue: double; override; + function intValue: integer; override; + function int64Value: Int64; override; + function toString (): string; override; + function Clone :TZAbstractObject; override; + private + fvalue: int64; + end; + + _String = class (TZAbstractObject) + private + function GetAsString: String; + procedure SetAsString(const Value: String); + public + constructor create (const s: string); + function equalsIgnoreCase (const s: string): boolean; + function Equals(const Value: TZAbstractObject): Boolean; override; + function toString(): string; override; + function Clone :TZAbstractObject; override; + property AsString: String read GetAsString write SetAsString; //By creation_zy 2009-11-22 + private + fvalue: string; + end; + + _NULL = class (TZAbstractObject) + function Equals(const Value: TZAbstractObject): Boolean; override; + function toString(): string; override; + function Clone :TZAbstractObject; override; //By creation_zy 2009-12-11 + end; + +{$IFDEF J_OBJECT} + _Object = class (TZAbstractObject) + function Equals(const Value: TZAbstractObject): Boolean; override; + function toString(): string; override; + function Clone :TZAbstractObject; override; + private + fvalue: TObject; + constructor Create(value: TObject); + procedure SetAsObject(const Value: TObject); + public + property AsObject: TObject read fvalue write SetAsObject; + end; +{$ENDIF} + + TJObjTransFlag=(jtfDbQouteStr, jtfQouteStr, jtfOtherAsStr); + TJObjTransFlags=set of TJObjTransFlag; + +function HexToInt(const S: String):Integer; +function IsConstJSON(Z: TObject):Boolean; +procedure SafeFreeJObj(Z: TObject);{$IF COMPILERVERSION>=18}inline;{$IFEND} +function SpaceStr(ALen: Integer):String; +function StrToAbstractJObj(const Str: String; Flags: TJObjTransFlags=[jtfDbQouteStr, jtfQouteStr]):TZAbstractObject; + +// by yangyxd 2013.11.06 +function JsonGetAttribute(const JSON, Name: string): string; +function JsonGetAttributeAsInt(const JSON, Name: string): Integer; +function JsonGetAttributeAsDouble(const JSON, Name: string): double; + +var + gcLista: TList; + CNULL: _NULL; + //Set this var to ture to force unicode char (eg: Chinese...) output in the form of \uXXXX + UnicodeOutput: Boolean=false; + SimpleJSON: Boolean=false; //2012-08-03 + +implementation + +//{$D-} + +const + CROTINA_NAO_IMPLEMENTADA :string = 'Not imp'; +var + CONST_FALSE: _Boolean; + CONST_TRUE: _Boolean; + +//By creation_zy +function IsSimpString(const Str:String):Boolean; +var + i:Integer; +begin + Result:=true; + for i:=1 to Length(Str) do + begin + Result:=Str[i] in ['0'..'9','a'..'z','A'..'Z','_']; + if not Result then exit; + end; +end; + +//By creation_zy +function SingleHZToJSONCode(const HZ:String):String; +var + wstr:WideString; +begin + if HZ='' then + begin + Result:=''; + exit; + end; + wstr:=WideString(HZ); + Result:='\u'+IntToHex(PWord(@wstr[1])^,4); +end; + +//By creation_zy 2009-11-21 +function IsConstJSON(Z: TObject):Boolean; +begin + Result:=(Z=CNULL) or (Z=CONST_FALSE) or (Z=CONST_TRUE); +end; + +procedure SafeFreeJObj(Z: TObject); +begin + if not IsConstJSON(Z) then + Z.Free; +end; + +function SpaceStr(ALen: Integer): string; {$IFDEF INLINE_OPT}inline;{$ENDIF} +begin + if ALen > 0 then begin + SetLength(Result, ALen); + FillChar(Result[1], ALen, ' '); + end else Result := ''; +end; + +procedure newNotImplmentedFeature () ; +begin + raise NotImplmentedFeature.create (CROTINA_NAO_IMPLEMENTADA); +end; + +function getFormatSettings: TFormatSettings ; +var + f: TFormatSettings; +begin + {$IFDEF MSWINDOWS} + SysUtils.GetLocaleFormatSettings (Windows.GetThreadLocale,f); + {$ELSE} + newNotImplmentedFeature(); + {$ENDIF} + Result:=f; + Result.DecimalSeparator:='.'; + Result.ThousandSeparator:=','; +end; + + +function HexToInt(const S: String): Integer; +const HexMap:array [Char] of SmallInt = + ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ); +var + i, n, l: Integer; +begin + Result:=0; + l:=Length(S); + if l=0 then exit; + if S[1]='$' then + n:=2 + else if (l>=2) and (S[2] in ['x','X']) then + n:=3 + else + n:=1; + for i:=n to l do + Result:=Result*16+HexMap[S[i]]; +end; + +function StrToAbstractJObj(const Str: String; Flags: TJObjTransFlags):TZAbstractObject; +var + i:Integer; +begin + if Str<>'' then + begin + case Str[1] of + '{': + begin + try + Result:=JSONObject.Create(Str); + except + Result:=nil; + end; + exit; + end; + '[': + begin + try + Result:=JSONArray.Create(Str); + except + Result:=nil; + end; + exit; + end; + '0'..'9','.','-': + begin + try + i:=StrToInt(Str); + Result:=_Integer.Create(i); + except + Result:=_Double.Create(StrToFloatDef(Str,0)); + end; + exit; + end; + 'n': + begin + if Str='null' then + begin + Result:=CNull; + exit; + end; + end; + 't','T','F','f': + begin + if UpperCase(Str)='TRUE' then + begin + Result:=CONST_TRUE; + exit; + end + else if UpperCase(Str)='FALSE' then + begin + Result:=CONST_FALSE; + exit; + end; + end; + end; + end; + Result:=_String.create(Str); +end; + +function mLeftPos(const SrcStr: AnsiString; SubChar: Char; sPos: Integer): Integer; +var + i: Integer; +begin + for i := sPos to Length(SrcStr) do + if SrcStr[i] = SubChar then begin + Result := i; Exit; + end; + Result := -1; +end; + +function mRightPos(const SrcStr: AnsiString; SubChar: Char; sPos: Integer): Integer; +var + i: Integer; +begin + for i := sPos downto 1 do + if SrcStr[i] = SubChar then begin + Result := i; Exit; + end; + Result := -1; +end; + +function mMidStr(const SrcStr: AnsiString; sPos, sCount: Integer): AnsiString; +begin + Result := Copy(SrcStr, sPos, sCount); +end; + +function JsonGetAttribute(const JSON, Name: string): string; +var + i, j: Integer; +begin + i := Pos('"'+Name+'":"', JSON); + if i > 0 then begin + i := i + Length(Name) + 4; + j := mLeftPos(JSON, '"', i); + Result := Copy(JSON, i, j - i); + end; +end; + +function JsonGetAttributeAsInt(const JSON, Name: string): Integer; +var + i, j: Integer; +begin + Result := 0; + i := Pos('"'+Name+'":', JSON); + if i > 0 then begin + i := i + Length(Name) + 3; + j := mLeftPos(JSON, ',', i); + if (j < 0) then + j := mLeftPos(JSON, '}', i); + if (j > 0) then begin + if JSON[i] = '"' then i := i + 1; + if JSON[j-1] = '"' then j := j - 1; + Result := StrToIntDef(Copy(JSON, i, j - i), 0); + end; + end; +end; + +function JsonGetAttributeAsDouble(const JSON, Name: string): double; +var + i, j: Integer; +begin + Result := 0; + i := Pos('"'+Name+'":', JSON); + if i > 0 then begin + i := i + Length(Name) + 3; + j := mLeftPos(JSON, ',', i); + if (j < 0) then + j := mLeftPos(JSON, '}', i); + if (j > 0) then begin + if JSON[i] = '"' then i := i + 1; + if JSON[j-1] = '"' then j := j - 1; + Result := StrToFloatDef(Copy(JSON, i, j - i), 0); + end; + end; +end; + +{ JSONTokener } + +(** + * Construct a JSONTokener from a string. + * + * @param s A source string. + *) +constructor JSONTokener.create(const s: string); +begin + myIndex:=1; + mySource:=s; + Len1:=Length(mySource)+1; +end; + +(** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. +*) +procedure JSONTokener.back; +begin + if myIndex>1 then Dec(myIndex); +end; + +(** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + *) +class function JSONTokener.dehexchar(c: char): integer; +begin + if ((c >= '0') and (c <= '9')) then begin + Result:= (ord(c) - ord('0')); + exit; + end; + if ((c >= 'A') and (c <= 'F')) then begin + Result:= (ord(c) + 10 - ord('A')); + exit; + end; + if ((c >= 'a') and (c <= 'f')) then begin + Result:=ord(c) + 10 - ord('a'); + exit; + end; + Result:=-1; +end; + + +(** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. +*) +function JSONTokener.more: boolean; +begin + Result:=myIndex<=Len1{System.length(mySource)+1}; +end; + +function JSONTokener.next: char; +begin + if {$IFDEF BACK_OPT}myIndex<=Len1{$ELSE}more(){$ENDIF} then + begin + Result:=mySource[myIndex]; + Inc(myIndex); + end + else + Result:=chr(0); +end; + + + (** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws ParseException if the character does not match. + *) +function JSONTokener.next(c: char): char; +begin + Result:=next(); + if (Result <> c) then + raise syntaxError('Expected ' + c + ' and instead saw ' + Result + '.'); +end; + + +(** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @exception ParseException + * Substring bounds error if there are not + * n characters remaining in the source string. + *) +function JSONTokener.next(n: integer): string; +var + i,j: integer; +begin + i:=self.myIndex; + j:=i + n; + if (j > System.length(self.mySource)) then begin + raise syntaxError('Substring bounds error'); + end; + self.myIndex:=self.myIndex + n; + Result:=copy (self.mySource,i,n); //substring(i, j) +end; + + (** + * Get the next char in the string, skipping whitespace + * and comments (slashslash, slashstar, and hash). + * @throws ParseException + * @return A character, or 0 if there are no more characters. + *) +function JSONTokener.nextClean: char; +var + c: char; +begin + while true do + begin + {$IFDEF NEXT_OPT2} + if myIndex<=Len1 then + begin + Result:=mySource[myIndex]; + Inc(myIndex); + end + else begin + Result:=#0; + exit; + end; + {$ELSE} + Result:=next(); + {$ENDIF} + if (Result = '/') then + begin + case (next()) of + '/': begin + repeat + c:=next(); + until (not ((c <> #10) and (c <> #13) and (c <> #0))); + end ; + '*': + begin + while (true) do + begin + c:=next(); + if (c = #0) then + begin + raise syntaxError('Unclosed comment.'); + end; + if (c = '*') then + begin + if (next() = '/') then break; + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + end; + end; + end + else begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:='/'; + exit; + end; + end; + end + else if (Result = '#') then + begin + repeat + c:=next(); + until (not ((c <> #10) and (c <> #13) and (c <> #0))); + end + else if ((Result = #0) or (Result > ' ')) then + exit; + end; //while +end; + + +(** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @exception ParseException Unterminated string. + *) +function JSONTokener.nextString (quote: char): string; +var + c: char; + sb: string; + WCh:WideChar; +begin + sb:=''; + while (true) do + begin + c:=next(); + case (c) of + #0, #10, #13: + begin + //Ignore #10 and #13 inside a string. By creation_zy 2009-11-22 + if c=#0 then + raise syntaxError('Unterminated string') + else + continue; + end; + '\': + begin + c:=next(); + case (c) of + {'b': // ?o backspace = #8 + sb.append('\b'); + break;} + 'b': //By creation_zy 2009-08-20 + sb:=sb + #8; + 't': + sb:=sb + #9; + 'n': + sb:=sb + #10; + 'f': + sb:=sb + #12; + 'r': + sb:=sb + #13; + {case 'u': + sb.append((char)Integer.parseInt(next(4), 16)); + break; + case 'x': \cx The control character corresponding to x + sb.append((char) Integer.parseInt(next(2), 16)); + break;} + 'u': //By creation_zy 2009-08-20 + begin + PWord(@WCh)^:=Word(HexToInt(next(4))); + sb:=sb+WCh; + end; + else + sb:=sb + c + end; + end + else begin + if (c = quote) then + begin + Result:=sb; + exit; + end; + sb:=sb + c + end; + end; + end; +end; + +(** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param d A delimiter character. + * @return A string. + *) +function JSONTokener.nextTo(d: char): string; +var + sb: string; + c: char; +begin + //c:=#0; + sb:=''; + while (true) do + begin + c:=next(); + if ((c = d) or (c = #0) or (c = #10) or (c = #13)) then + begin + if (c <> #0) then + begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + end; + Result:=trim (sb); + exit; + end; + sb:=sb + c; + end; +end; + +(** + * Get the text up but not including one of the specified delimeter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. +*) +function JSONTokener.nextTo(const delimiters: string): char; +var + c: char; + sb: string; +begin + //c:=#0; + Result:=#0; //By creation_zy + sb:=''; + while (true) do + begin + c:=next(); + if ((pos (c,delimiters) > 0) or (c = #0) or + (c = #10) or (c = #13)) then + begin + if (c <> #0) then + begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + end; + sb:=trim(sb); + if (System.length(sb) > 0) then + Result:=sb[1]; + exit; + end; + sb:=sb + c; + end; +end; + +(** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, or String, or the JSONObject.NULL object. + * @exception ParseException The source does not conform to JSON syntax. + * + * @return An object. +*) +function JSONTokener.nextValue(parent: JSONBase): TZAbstractObject; // by yangyxd parent +var + c, b: char; + s , sb: string; + n:Integer; +begin + c:=nextClean(); + + case (c) of + '"', #39: begin + Result:=_String.create (nextString(c)); + exit; + end; + '{': begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:=JSONObject.create(self); + JSONObject(Result).Parent := parent; + exit; + end; + '[': begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:=JSONArray.create(self); + JSONArray(Result).Parent := parent; + exit; + end; + end; + + (* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + *) + + sb:=''; + b:=c; + while ((ord(c) >= ord(' ')) and (pos (c,',:]}/\\\"[{;=#') = 0)) do begin + sb:=sb + c; + c:=next(); + end; + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + + (* + * If it is true, false, or null, return the proper value. + *) + + s:=trim (sb); + n:=System.Length(s); + if n=0 then + raise syntaxError('Missing value.'); + if n in [4,5] then //2009-09-14 Length limit before AnsiLowerCase. By creation_zy + begin + sb:=AnsiLowerCase(s); + if (sb = 'true') then + begin + Result:= _Boolean._TRUE; + exit; + end; + + if (sb = 'false') then + begin + Result:=_Boolean._FALSE; + exit; + end; + if (sb = 'null') then + begin + Result:=JSONObject.NULL; + exit; + end; + end; + + (* + * If it might be a number, try converting it. We support the 0- and 0x- + * conventions. If a number cannot be produced, then the value will just + * be a string. Note that the 0-, 0x-, plus, and implied string + * conventions are non-standard. A JSON parser is free to accept + * non-JSON forms as long as it accepts all correct JSON forms. + *) + + if ( ((b >= '0') and (b <= '9')) or (b = '.') + or (b = '-') or (b = '+')) then + begin + if (b = '0') then begin + if ( (System.length(s) > 2) and + ((s[2] = 'x') or (s[2] = 'X') ) ) then + begin + try + Result:=_Integer.create(_Integer.parseInt(copy(s,3,System.length(s)),16)); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + end else begin + try + if (System.length(s) >= 2) and (s[2]='.') then //2009-09-14 By creation_zy + Result:=_Double.create(s) + else + Result:=_Integer.create(_Integer.parseInt(s,8)); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + end; + end; + if Pos('.',s)=0 then //2011-10-02 Bug fixed. By creation_zy + try + Result:=_Integer.create(s); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + + try + Result:=_Double.create(s); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + end; + Result:=_String.create(s); +end; + +(** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + *) +function JSONTokener.skipTo(_to: char): char; +var + c: char; + index: integer; +begin + index:=self.myIndex; + repeat + c:=next(); + if (c = #0) then + begin + self.myIndex:=index; + Result:=c; + exit; + end; + until c=_to; + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:=c; + exit; +end; + +(** + * Skip characters until past the requested string. + * If it is not found, we are left at the end of the source. + * @param to A string to skip past. + *) +procedure JSONTokener.skipPast(const _to: string); +begin + self.myIndex:=pos (_to, copy(mySource, self.myIndex, System.length(mySource))); + if (self.myIndex < 0) then begin + self.myIndex:=System.length(self.mySource)+1; + end else begin + self.myIndex:=self.myIndex + System.length(_to); + end; +end; + + + +(** + * Make a ParseException to signal a syntax error. + * + * @param message The error message. + * @return A ParseException object, suitable for throwing + *) +function JSONTokener.syntaxError(const _message: string): ParseException; +begin + Result:=ParseException.create (_message + toString()+' postion: ' //' prximo a: ' + + copy (toString(),self.myIndex,10), self.myIndex); +end; + +(** + * Make a printable string of this JSONTokener. + * + * @return " at character [this.myIndex] of [this.mySource]" + *) + + +function JSONTokener.toString: string; +begin + Result:=' at character ' + intToStr(myIndex) + ' of ' + mySource; +end; + + +(** + * Convert %hh sequences to single characters, and + * convert plus to space. + * @param s A string that may contain + * + (plus) and + * %hh sequences. + * @return The unescaped string. + *) +function JSONTokener.unescape(const s: string): string; +var + len, i,d,e: integer; + b: string; + c: char; +begin + len:=System.length(s); + b:=''; + i:=1; + while ( i <= len ) do begin + c:=s[i]; + if (c = '+') then begin + c:=' '; + end + else if ((c = '%') and ((i + 2) <= len)) then + begin + d:=dehexchar(s[i + 1]); + e:=dehexchar(s[i + 2]); + if ((d >= 0) and (e >= 0)) then + begin + c:=chr(d * 16 + e); + i:=i + 2; + end; + end; + b:=b + c; + i:=i + 1; + end; + Result:=b ; +end; + +{ JSONObject } + +(** +* Construct an empty JSONObject. +*) +constructor JSONObject.create; +begin + myHashMap:=TStringList.create; + inherited Create; +end; + + +(** + * Construct a JSONObject from a subset of another JSONObject. + * An array of strings is used to identify the keys that should be copied. + * Missing keys are ignored. + * @param jo A JSONObject. + * @param sa An array of strings. + *) +constructor JSONObject.create(jo: JSONObject; sa: array of string); +var + i: integer; +begin + create(); + for i:=low(sa) to high(sa) do + putOpt(sa[i], jo.opt(sa[i]).Clone); +end; + +(** + * Construct a JSONObject from a JSONTokener. + * @param x A JSONTokener object containing the source string. + * @throws ParseException if there is a syntax error in the source string. + *) +constructor JSONObject.create(x: JSONTokener); +begin + create ; + UpdateByTokener(x); +end; + +(** + * Construct a JSONObject from a Map. + * @param map A map object that can be used to initialize the contents of + * the JSONObject. + *) +constructor JSONObject.create(map: TStringList); +var + i: integer; +begin + myHashMap:=TStringlist.create; + for i:=0 to map.Count -1 do + myHashMap.AddObject(map[i],map.Objects[i]); +end; + +(** + * Construct a JSONObject from a string. + * This is the most commonly used JSONObject constructor. + * @param string A string beginning + * with { (left brace) and ending + * with } (right brace). + * @exception ParseException The string must be properly formatted. + *) +constructor JSONObject.create(const s: string); +var + token: JSOnTokener; +begin + if s='' then //Add by creation_zy 2008-10-21 + begin + create(); + exit; + end; + token:=JSONTokener.create(s); + try + create(token); + finally + token.free; + end; +end; + + +constructor JSONObject.CreateInArray(Ay: JSONArray); +begin + create; + if Ay<>nil then + Ay.put(Self); +end; + +(** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a + * JSONArray is stored under the key to hold all of the accumulated values. + * If there is already a JSONArray, then the new value is appended to it. + * In contrast, the put method replaces the previous value. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws NullPointerException if the key is null + *) +function JSONObject.accumulate(const key: string; value: TZAbstractObject): JSONObject; +var + a: JSONArray; + o: TZAbstractObject; +begin + o:=opt(key); + if (o = nil) then + put(key, value) + else if (o is JSONArray) then + begin + a:=JSONArray(o); + a.put(value); + end + else begin + a:=JSONArray.create; + a.put(o.Clone); + a.put(value); + put(key, a); + end; + Result:=self; +end; + + +(** + * Get the value object associated with a key. + * + * @param key A key string. + * @return The object associated with the key. + * @exception NoSuchElementException if the key is not found. + *) +function JSONObject.get(const key: string): TZAbstractObject; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o = nil) then + raise NoSuchElementException.create('JSONObject['+quote(key)+'] not found.'); + Result:=o; +end; + + +(** + * Get the boolean value associated with a key. + * + * @param key A key string. + * @return The truth. + * @exception NoSuchElementException if the key is not found. + * @exception ClassCastException + * if the value is not a Boolean or the String "true" or "false". + *) +function JSONObject.getBoolean(const key: string): boolean; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('false'))) then begin + Result:=false; + exit; + end + else if (o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('true'))) then begin + Result:=true; + exit; + end; + raise ClassCastException.create('JSONObject[' + + quote(key) + '] is not a Boolean.'); +end; + +function JSONObject.getDouble(const key: string): double; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is _Number) then + begin + Result:=_Number (o).doubleValue(); + exit; + end ; + if (o is _String) then + begin + Result:=StrToFloat (_String(o).toString(), getFormatSettings()); + exit; + end; + raise NumberFormatException.create('JSONObject['+quote(key)+'] is not a number.'); +end; + + +(** + * Get the int value associated with a key. + * + * @param key A key string. + * @return The integer value. + * @exception NoSuchElementException if the key is not found + * @exception NumberFormatException + * if the value cannot be converted to a number. + *) +function JSONObject.getInt(const key: string): integer; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is _Number) then + Result:= _Number(o).intValue() + else + Result:= Round(getDouble(key)); +end; + + +function JSONObject.GetInt64(const key: string): Int64; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is _Number) then + Result:= _Number(o).int64Value() + else + Result:= Round(getDouble(key)); +end; + +(** + * Get the JSONArray value associated with a key. + * + * @param key A key string. + * @return A JSONArray which is the value. + * @exception NoSuchElementException if the key is not found or + * if the value is not a JSONArray. + *) +function JSONObject.getJSONArray(const key: string): JSONArray; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is JSONArray) then + Result:=JSONArray(o) + else + raise NoSuchElementException.create('JSONObject[' + + quote(key) + '] is not a JSONArray.'); +end; + + +(** + * Get the JSONObject value associated with a key. + * + * @param key A key string. + * @return A JSONObject which is the value. + * @exception NoSuchElementException if the key is not found or + * if the value is not a JSONObject. + *) +function JSONObject.getJSONObject(const key: string): JSONObject; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is JSONObject) then + Result:=JSONObject(o) + else + raise NoSuchElementException.create('JSONObject[' + + quote(key) + '] is not a JSONObject.'); +end; + + +(** + * Get the string associated with a key. + * + * @param key A key string. + * @return A string which is the value. + * @exception NoSuchElementException if the key is not found. +*) +function JSONObject.getString(const key: string): string; +begin + Result:=get(key).toString(); +end; + + +(** + * Determine if the JSONObject contains a specific key. + * @param key A key string. + * @return true if the key exists in the JSONObject. + *) +function JSONObject.has(const key: string): boolean; +begin + Result:=myHashMap.IndexOf(key)>=0; +end; + +function JSONObject.IndexOfObject(aobj: TObject): Integer; +var + i: Integer; +begin + for i := 0 to myHashMap.Count - 1 do + if myHashMap.Objects[i] = aobj then begin + Result := I; + Exit; + end; + Result := -1; +end; + +(** + * Determine if the value associated with the key is null or if there is + * no value. + * @param key A key string. + * @return true if there is no value associated with the key or if + * the value is the JSONObject.NULL object. + *) +function JSONObject.isNull(const key: string): boolean; +begin + Result:=NULL.equals(opt(key)); +end; + +function JSONObject.keys: TStringList; +var + i: integer; +begin + Result:=TStringList.Create; + for i:=0 to myHashMap.Count -1 do + Result.add (myHashMap[i]); +end; + +function JSONObject.length: integer; +begin + Result:=myHashMap.Count; +end; + + +(** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + *) +function JSONObject.names: JSONArray; +var + i,c: integer; + k: TStringList; +begin + Result:=nil; + k:=keys; + try + c:=k.Count; + if c>0 then //2013-05-04 Fix memory leak bug found by K.o.s + begin + Result:=JSONArray.create; + for i:=0 to c-1 do + Result.put(_String.create(k[i])); + end; + finally + k.free; + end; +end; + +function JSONObject.NextSibling: JSONObject; +var + i: Integer; +begin + if not Assigned(Parent) then + Result := nil + else begin + i := Parent.IndexOfObject(Self) + 1; + if (i > 0) and (i < Parent.Count) then begin + if Parent.Child[i] is JSONObject then + Result := JSONObject(Parent.Child[i]) + else Result := nil; + end else + Result := nil; + end; +end; + +class function JSONObject.numberToString(n: _Number): string; +begin + if (n = nil) then + Result:='' + else if (n is _Integer) then + Result:=IntToStr(n.intValue) + else + Result:=FloatToStr(n.doubleValue, getFormatSettings()); +end; + + +(** + * Get an optional value associated with a key. + * @param key A key string. + * @return An object which is the value, or null if there is no value. + * @exception NullPointerException The key must not be null. + *) +function JSONObject.opt(const key: string): TZAbstractObject; +var + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then + Result:=nil + else + Result:=TZAbstractObject(myHashMap.Objects[i]); +end; + +function JSONObject.Opt2(key, key2: string): TZAbstractObject; +var + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then + i:=myHashMap.IndexOf(key2); + if i<0 then + Result:=nil + else + Result:=TZAbstractObject(myHashMap.Objects[i]); +end; + +(** + * Get an optional boolean associated with a key. + * It returns false if there is no such key, or if the value is not + * Boolean.TRUE or the String "true". + * + * @param key A key string. + * @return The truth. + *) +function JSONObject.optBoolean(const key: string): boolean; +begin + Result:=optBoolean(key, false); +end; + + +(** + * Get an optional boolean associated with a key. + * It returns the defaultValue if there is no such key, or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param key A key string. + * @param defaultValue The default. + * @return The truth. + *) +function JSONObject.optBoolean(const key: string; + defaultValue: boolean): boolean; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> nil) then + begin + if o.ClassType=_Boolean then //2009-03-06 By creation_zy + begin + Result:=_Boolean(o).fvalue; + exit; + end + else if //o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o).equalsIgnoreCase('false'))) then begin + Result:=false; + exit; + end + else if //o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o).equalsIgnoreCase('true'))) then begin + Result:=true; + exit; + end; + end; + Result:=defaultValue; +end; + + +(** + * Get an optional double associated with a key, + * or NaN if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A string which is the key. + * @return An object which is the value. + *) +function JSONObject.optDouble(const key: string): double; +begin + Result:=optDouble(key, _Double.NaN); +end; + + +(** + * Get an optional double associated with a key, or the + * defaultValue if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + *) +function JSONObject.optDouble(const key: string; defaultValue: double): double; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).doubleValue(); + exit; + end; + try + Result:=_Double.create(_String(o)).doubleValue(); + exit; + except + on e:Exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + +(** + * Get an optional int value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + *) +function JSONObject.optInt(const key: string): integer; +begin + Result:=optInt(key, 0); +end; + + +(** + * Get an optional int value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + *) +function JSONObject.optInt(const key: string; defaultValue: integer): integer; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> null) and ( o <> nil ) then //By creation_zy. Add compare to nil + begin + if (o is _Number) then + begin + Result:=(_Number(o)).intValue(); + exit; + end; + try + Result:=_Integer.parseInt(_String(o)); + except + on e:Exception do + begin + Result:=defaultValue; + end; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +function JSONObject.OptInt2(key, key2: String; DefaultValue: Integer): Integer; +var + o:TZAbstractObject; +begin + o:=Opt2(key,key2); + if o<>nil then + Result:=TZAbstractObject.getInt(o,DefaultValue) + else + Result:=DefaultValue; +end; + +function JSONObject.OptInt64(const key: string): int64; +begin + Result:=optInt64(key, 0); +end; + +function JSONObject.OptInt64(const key: string; defaultValue: int64): int64; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> null) and ( o <> nil ) then //By creation_zy. Add compare to nil + begin + if (o is _Number) then + begin + Result:=(_Number(o)).int64Value(); + exit; + end; + try + Result:=_Integer.parseInt64(_String(o)); + except + on e:Exception do + begin + Result:=defaultValue; + end; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +(** + * Get an optional JSONArray associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONArray. + * + * @param key A key string. + * @return A JSONArray which is the value. + *) +function JSONObject.optJSONArray(const key: string): JSONArray; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is JSONArray) then + Result:=JSONArray(o) + else + Result:=nil; +end; + + +(** + * Get an optional JSONObject associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONObject. + * + * @param key A key string. + * @return A JSONObject which is the value. + *) +function JSONObject.optJSONObject(const key: string): JSONObject; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is JSONObject) then + Result:=JSONObject(o) + else + Result:=nil; +end; + +{$IFDEF J_OBJECT} +function JSONObject.OptObject(const key: string; + defaultValue: TObject): TObject; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is _Object) then + Result:=_Object(o).AsObject + else + Result:=defaultValue; +end; + +function JSONObject.OptObject(const key: string): TObject; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is _Object) then + Result:=_Object(o).AsObject + else + Result:=nil; +end; +{$ENDIF} + +(** + * Get an optional string associated with a key. + * It returns an empty string if there is no such key. If the value is not + * a string and is not null, then it is coverted to a string. + * + * @param key A key string. + * @return A string which is the value. + *) +function JSONObject.optString(const key: string): string; +var + o: TZAbstractObject ; + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then + Result:='' + else begin + o:=TZAbstractObject(myHashMap.Objects[i]); + if (o <> nil) then + Result:=o.toString() + else + Result:=''; + end; +end; + +(** + * Get an optional string associated with a key. + * It returns the defaultValue if there is no such key. + * + * @param key A key string. + * @param defaultValue The default. + * @return A string which is the value. + *) +function JSONObject.optString(const key, defaultValue: string): string; +var + o: TZAbstractObject ; +begin + o:=Opt(key); + if (o <> nil) then + Result:=o.toString() + else + Result:=defaultValue; +end; + +function JSONObject.OptString2(key, key2: String; DefaultValue: String): String; +var + o:TZAbstractObject; +begin + o:=Opt2(key,key2); + if o<>nil then + Result:=o.toString() + else + Result:=DefaultValue; +end; + +(** + * Put a key/boolean pair in the JSONObject. + * + * @param key A key string. + * @param value A boolean which is the value. + * @return this. + *) +function JSONObject.put(const key: string; value: boolean): JSONObject; +begin + put(key, _Boolean.valueOf(value)); + Result:=self; +end; + +(** + * Put a key/double pair in the JSONObject. + * + * @param key A key string. + * @param value A double which is the value. + * @return this. + *) +function JSONObject.put(const key: string; value: double): JSONObject; +begin + put(key, _Double.create(value)); + Result:=self; +end; + + +(** + * Put a key/int pair in the JSONObject. + * + * @param key A key string. + * @param value An int which is the value. + * @return this. + *) +function JSONObject.put(const key: string; value: integer): JSONObject; +begin + put(key, _Integer.create(value)); + Result:=self; +end; + + +(** + * Put a key/value pair in the JSONObject. If the value is null, + * then the key will be removed from the JSONObject if it is present. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, String, or the + * JSONObject.NULL object. + * @return this. + * @exception NullPointerException The key must be non-null. + *) +function JSONObject.put(const key: string; value: TZAbstractObject): JSONObject; +var + temp: TObject; + i: integer; +begin + if (key = '') then + begin + raise NullPointerException.create('Null key.'); + end ; + if (value <> nil) then {$D+} + begin + i:=myHashMap.IndexOf(key); + if ( i >= 0) then + begin + temp:=myHashMap.Objects [i]; + myHashMap.Objects[i]:=value; + if (temp<>CNULL) and (temp<>CONST_FALSE) and (temp<>CONST_TRUE) then //Prevent to free const obj. By craetion_zy 2009-11-21 + temp.free; + end + else + myHashMap.AddObject(key, value); + end + else begin + temp:=remove(key); + if (temp<>nil) and (temp<>CNULL) and (temp<>CONST_FALSE) and (temp<>CONST_TRUE) then //Prevent to free const obj. By craetion_zy 2009-11-21 + temp.free; + end; + Result:=self; +end; + +function JSONObject.put(const key, value: string): JSONObject; +begin + put(key, _String.create(value)); + Result:=self; +end; + +{$IFDEF J_OBJECT} +function JSONObject.Put(const key: string; value: TObject): JSONObject; +begin + put(key, _Object.create(value)); + Result:=self; +end; +function JSONObject.Put(const key: string; value: int64): JSONObject; +begin + put(key, _Integer.create(value)); + Result:=self; +end; + +{$ENDIF} + +(** + * Put a key/value pair in the JSONObject, but only if the + * value is non-null. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, String, or the + * JSONObject.NULL object. + * @return this. + * @exception NullPointerException The key must be non-null. + *) +function JSONObject.putOpt(const key: string; value: TZAbstractObject): JSONObject; +begin + if (value <> nil) then + put(key, value); + Result:=self; +end; + + +(** + * Produce a string in double quotes with backslash sequences in all the + * right places. + * @param string A String + * @return A String correctly formatted for insertion in a JSON message. + *) +class function JSONObject.quote(const s: string): string; +var + b,c: char; + i, len: integer; + sb, t: string; +begin + if ((s = '') or (System.Length(s) = 0)) then + begin + Result:= '""'; + exit; + end; + + //b:=#0; + c:=#0; + len:=System.length(s); + //SetLength (s, len+4); + t:=''; + + sb:=sb +'"'; + i:=1; + while i<=len do + begin + b:=c; + c:=s[i]; + case (c) of + '\', '"': + begin + sb:=sb + '\'; + sb:=sb + c; + end; + '/': + begin + if (b = '<') then + begin + sb:=sb + '\'; + end; + sb:=sb + c; + end; + {#8, #9, #10, #12, #13: + begin + sb:=sb + c; + end;} + //Output special character smaller than space. By creation_zy 2009-11-22 + #0: sb:=sb + '\u0000'; + #1..#7: sb:=sb + '\u000'+Char(Byte('0')+Byte(c)); + #8: sb:=sb + '\b'; + #9: sb:=sb + '\t'; + #10: sb:=sb + '\n'; + #12: sb:=sb + '\f'; + #13: sb:=sb + '\r'; + else + begin + if (c < ' ') then + begin + t:='000' + _Integer.toHexString(c); + sb:=sb + '\u' + copy (t,System.length(t)-3,4); + end + else if UnicodeOutput and (c>#128) and (i#128) and (i + * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +function JSONObject.toString: string; +var + _keys: TStringList; + o, sb: string; + i :integer; +begin + _keys:=keys(); + try + sb:='{'; + + for i:=0 to _keys.count -1 do + begin + if (System.length(sb) > 1) then + begin + sb:= sb + ','; + end; + o:=_keys[i]; + if SimpleJSON and IsSimpString(o) then //By creation_zy + sb:=sb + o + else + sb:=sb + quote(o); + sb:=sb + ':'; + sb:= sb + valueToString(TZAbstractObject(myHashMap.Objects[myHashMap.IndexOf(o)])); + end; + sb:=sb + '}'; + Result:=sb; + finally + _keys.free; + end; +end; + + +(** + * Make a prettyprinted JSON external form string of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +procedure JSONObject.SaveToStream(stream: TStream); +var + _keys: TStringList; + o: string; + i, j :integer; +begin + _keys:=keys(); + try + WriteChar(stream, '{'); + j := 1; + for i:=0 to _keys.count -1 do + begin + if (j > 1) then + WriteChar(stream, ','); + o:=_keys[i]; + if SimpleJSON and IsSimpString(o) then //By creation_zy + WriteString(stream, o) + else + quoteToStream(stream, o); + WriteChar(stream, ':'); + ValueToStream(TZAbstractObject(myHashMap.Objects[myHashMap.IndexOf(o)]), stream); + Inc(j); + end; + WriteChar(stream, '}'); + finally + _keys.free; + end; +end; + +function JSONObject.toString2(indentFactor: integer): string; +begin + Result:=toString3(indentFactor, 0); +end; + +(** + * Make a prettyprinted JSON string of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +function JSONObject.toString3(indentFactor, indent: integer): string; +var + j , n , newindent: integer; + _keys: TStringList; + o, sb: string; +begin + //i:=0; + n:=length(); + if (n = 0) then begin + Result:='{}'; + exit; + end; + _keys:=keys(); + try + sb:=sb + '{'; + newindent:=indent + indentFactor; + if (n = 1) then + begin + o:=_keys[0]; + sb:= sb + quote(o); + sb:= sb + ': '; + sb:= sb + valueToString(TZAbstractObject(myHashMap + .Objects[myHashMap.IndexOf(o)]) + , indentFactor, indent); + end + else begin + for j:=0 to _keys.count -1 do + begin + o:=_keys[j]; + if (System.length(sb) > 1) then + begin + sb:=sb + ','+ #10; + end + else begin + sb:= sb + #10; + end; + sb:= sb + SpaceStr(newindent) + quote(o) + ': '; + sb:= sb + valueToString(TZAbstractObject(myHashMap.Objects[myHashMap.IndexOf(o)]) + , indentFactor, newindent); + end; + if (System.length(sb) > 1) then + begin + sb:=sb + #10; + sb:= sb + SpaceStr(indent); + end; + end; + sb:= sb + '}'; + Result:=sb; + finally + _keys.Free; //Memory leak fixed. By creation_zy 2009-08-03 + end; +end; + +class function JSONObject.NULL: _NULL; +begin + Result:=CNULL; +end; + +(** + * Make JSON string of an object value. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +class function JSONObject.valueToString(value: TZAbstractObject): string; +begin + if ((value = nil) or (value.equals(null))) then begin + Result:='null'; + exit; + end; + if (value is _Number) then begin + Result:=numberToString(_Number(value)); + exit; + end; + if ((value is _Boolean) or (value is JSONObject) or + (value is JSONArray)) then begin + Result:=value.toString(); + exit; + end; + Result:=quote(value.toString()); +end; + +class procedure JSONObject.ValueToStream(value: TZAbstractObject; + stream: TStream); +var + m: TStringStream; +begin + if ((value = nil) or (value.equals(null))) then begin + WriteString(stream, 'null'); + exit; + end; + if (value is _Number) then begin + WriteString(stream, numberToString(_Number(value))); + exit; + end; + if ((value is _Boolean) or (value is JSONObject) or + (value is JSONArray)) then begin + value.SaveToStream(stream); + exit; + end; + m := TStringStream.Create(''); + try + value.SaveToStream(m); + quoteToStream(stream, m.DataString); + //WriteString(stream, quote(m.DataString)); + finally + m.Free; + end; +end; + + + +(** + * Make a prettyprinted JSON string of an object value. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +class function JSONObject.valueToString(value: TZAbstractObject; + indentFactor, indent: integer): string; +begin + if ((value = nil) or (value.equals(nil))) then begin + Result:='null'; + exit; + end; + if (value is _Number) then begin + Result:=numberToString(_Number(value)); + exit; + end; + if (value is _Boolean) then begin + Result:= value.toString(); + exit; + end; + if (value is JSONObject) then begin + Result:=((JSONObject(value)).toString3(indentFactor, indent)); + exit; + end; + if (value is JSONArray) then begin + Result:=((JSONArray(value)).toString3(indentFactor, indent)); + exit; + end; + Result:=quote(value.toString()); +end; + +procedure JSONObject.clean; +var + i: integer; + MyObj:TObject; +begin + for i:=Pred(myHashMap.Count) downto 0 do + begin + MyObj:=myHashMap.Objects[i]; + if (MyObj <> CONST_FALSE) and (MyObj <> CONST_TRUE) and (MyObj <> CNULL) then + MyObj.Free; + end; + myHashMap.Clear; +end; + + +(** +* Assign the values to other json Object. +* @param JSONObject objeto to assign Values +*) +procedure JSONObject.assignTo (json: JSONObject) ; +var + _keys: TStringList; + i: integer; +begin + _keys:=keys; + try + for i:=0 to _keys.Count -1 do + begin + json.put (_keys[i],get(_keys[i]).Clone); + end; + finally + _keys.free; + end; +end; + +function JSONObject.Clone: TZAbstractObject; +begin + Result:=JSONObject.create(self.toString()); +end; + +function JSONObject.GetPropValues(const Key: String): String; +begin + Result:=OptString(Key); +end; + +procedure JSONObject.SetPropValues(const Key: String; const Value: String); +begin + Put(Key, Value); +end; + +function JSONObject.GetCascadeValue(const Keys: array of String): String; +var + i:Integer; + TmpProp:JSONObject; +begin + Result:=''; + TmpProp:=Self; + for i:=Low(Keys) to High(Keys) do + begin + if i=High(Keys) then + begin + Result:=TmpProp.PropValues[Keys[i]]; + exit; + end; + TmpProp:=TmpProp.OptJSONObject(Keys[i]); + if TmpProp=nil then exit; + end; +end; + +function JSONObject.GetChild(Index: Integer): TZAbstractObject; +begin + Result := JSONObject(myHashMap.Objects[index]); +end; + +function JSONObject.GetCount: Integer; +begin + Result := myHashMap.Count; +end; + +function JSONObject.GetCascadeValEx(const Keys: array of String): String; +var + i:Integer; + TmpProp,p:JSONObject; +begin + Result:=''; + TmpProp:=Self; + for i:=Low(Keys) to High(Keys) do + begin + if i=High(Keys) then + begin + Result:=TmpProp.PropValues[Keys[i]]; + exit; + end; + p:=TmpProp.OptJSONObject(Keys[i]); + if p=nil then + begin + Result:=TmpProp.OptString(Keys[i]); + exit; + end; + TmpProp:=p; + end; +end; + +function JSONObject.GetCascadeValObj( + const Keys: array of String): TZAbstractObject; +var + i:Integer; + TmpProp:JSONObject; +begin + Result:=nil; + TmpProp:=Self; + for i:=Low(Keys) to High(Keys) do + begin + if i=High(Keys) then + begin + Result:=TmpProp.Opt(Keys[i]); + exit; + end; + TmpProp:=TmpProp.OptJSONObject(Keys[i]); + if TmpProp=nil then exit; + end; +end; + +procedure JSONObject.SetAsString(const Value: String); +var + token:JSOnTokener; +begin + Clean; + if System.Length(Value)<=2 then exit; + token:=JSONTokener.create(Value); + try + UpdateByTokener(token); + finally + token.free; + end; +end; + +function JSONObject.GetDiffFrom(Source: JSONObject; UseSrc: Boolean): JSONObject; +var + sl:TStrings; + i:Integer; + mstr:String; + z,sz:TZAbstractObject; +begin + Result:=JSONObject.Create; + if UseSrc then + sl:=Source.Keys + else + sl:=Keys; + with sl do + begin + for i:=0 to Pred(Count) do + begin + mstr:=Strings[i]; + if UseSrc then + begin + z:=Self.Opt(mstr); + sz:=Source.ValObjByIndex[i]; + if z=nil then + begin + Result.Put(mstr,sz.Clone); + continue; + end; + end + else begin + sz:=Source.Opt(mstr); + z:=Self.ValObjByIndex[i]; + if sz=nil then + begin + Result.Put(mstr,z.Clone); + continue; + end; + end; + if sz.ClassType=z.ClassType then + begin + if sz.toString=z.toString then continue; + if sz.ClassType=JSONObject then + begin + Result.Put(mstr,JSONObject(z).GetDiffFrom(JSONObject(sz),UseSrc)); + continue; + end; + end; + if UseSrc then + Result.Put(mstr,sz.Clone) + else if z<>nil then //Ӧ... + Result.Put(mstr,z.Clone); + end; + Free; + end; +end; + +procedure JSONObject.Delete(index: Integer); +begin + SafeFreeJObj(myHashMap.Objects[index]); + self.myHashMap.delete(index); +end; + +procedure JSONObject.RemoveByKeyHeader(const Header: String); +var + i:Integer; +begin + with Keys do + begin + for i:=Pred(Count) downto 0 do + begin + if Pos(Header,Strings[i])=1 then + CleanKey(Strings[i]); + end; + Free; + end; +end; + +function JSONObject.RemoveLastKey: TZAbstractObject; +var + i:Integer; +begin + with myHashMap do + begin + i:=length-1; + if i<0 then + begin + Result:=nil; + exit; + end; + Result:=TZAbstractObject(Objects[i]); + delete(i); + end; +end; + +function JSONObject.PropCount: Integer; +begin + Result:=myHashMap.Count; +end; + +function JSONObject.KeyByVal(const Value: String): String; +var + i:Integer; +begin + for i:=0 to Pred(myHashMap.Count) do + begin + with TZAbstractObject(myHashMap.Objects[i]) do + begin + if toString=Value then + begin + Result:=myHashMap[i]; + exit; + end; + end; + end; + Result:=''; +end; + +function JSONObject.PartExtract(KeyNames: TStrings; + DoRemove: Boolean): JSONObject; +var + i:Integer; + KeyName:String; +begin + Result:=nil; + if KeyNames=nil then exit; + Result:=JSONObject.Create; + for i:=Pred(Length) downto 0 do + begin + KeyName:=KeyByIndex[i]; + if KeyNames.IndexOf(KeyName)<0 then continue; + if DoRemove then + Result.Put(KeyName,Remove(KeyByIndex[i])) + else + Result.Put(KeyName,ValObjByIndex[i].Clone); + end; +end; + +function JSONObject.ExtractAll: JSONObject; +var + i:Integer; + KeyName:String; +begin + Result:=JSONObject.Create; + for i:=Pred(Length) downto 0 do + begin + KeyName:=KeyByIndex[i]; + Result.Put(KeyName,Remove(KeyByIndex[i])) + end; +end; + +function JSONObject.TryNewJSONArray(const Key: String): JSONArray; +begin + Result:=OptJSONArray(Key); + if Result=nil then + begin + Result:=JSONArray.create; + Result.Parent := Self; + Put(Key,Result); + end; +end; + +function JSONObject.TryNewJSONObject(const Key: String): JSONObject; +begin + Result:=OptJSONObject(Key); + if Result=nil then + begin + Result:=JSONObject.create; + Result.Parent := Self; + Put(Key,Result); + end; +end; + +procedure JSONObject.Assign(Source: JSONObject); +begin + if Source=nil then + Clean + else begin + AsString:=Source.AsString; + end; +end; + +function JSONObject.GetKeyByIndex(index: Integer): String; +begin + Result:=myHashMap[index]; +end; + +function JSONObject.GetObject(const key: string): TObject; +begin + Result:=OptObject(Key); +end; + +procedure JSONObject.SetCascadeValue(const Value: String; + const Keys: array of String); +begin + SetCascadeValueEx(Value,Keys,0); +end; + +procedure JSONObject.SetCascadeValueEx(const Value: String; + const Keys: array of String; StartIdx: Integer); +var + JObj:JSONObject; +begin + if High(Keys)CNULL) and (temp<>CONST_FALSE) and (temp<>CONST_TRUE) and (temp <> nil) then //Prevent to free const obj. By craetion_zy 2009-11-21 + temp.free; +end; + +function JSONObject.SetKey(idx: Integer; const Key: String): Boolean; +begin + Result:=myHashMap.IndexOf(Key)<0; + if not Result or (idx<0) or (idx>=myHashMap.Count) then exit; + myHashMap.Strings[idx]:=Key; +end; + +function JSONObject.GetValByIndex(index: Integer): String; +begin + Result:=TZAbstractObject(myHashMap.Objects[index]).toString; +end; + +function JSONObject.GetValObjByIndex(index: Integer): TZAbstractObject; +begin + Result:=TZAbstractObject(myHashMap.Objects[index]); +end; + +procedure JSONObject.CleanKey(const Key: String); +var + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then exit; + SafeFreeJObj(myHashMap.Objects[i]); + myHashMap.delete(i); +end; + +procedure JSONObject.UpdateByTokener(x: JSONTokener); +var + c: char; +begin + FName:=''; // by yangyxd + + if (x.nextClean() <> '{') then + raise x.syntaxError('A JSONObject must begin with "{"'); + while (true) do + begin + c:=x.nextClean(); + case (c) of + #0: + raise x.syntaxError('A JSONObject must end with "}"'); + '}': begin + exit; + end + else begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + //key:=x.nextValue().toString(); + with x.nextValue(self) do + begin + FName:=toString(); // by yangyxd + Free; //Fix memory leak. By creation_zy 2008-08-07 + end; + end + end; //fim do case + + (* + * The key is followed by ':'. We will also tolerate '=' or '=>'. + *) + + c:=x.nextClean(); + if (c = '=') then begin + if (x.next() <> '>') then begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + end; + end else if (c <> ':') then begin + raise x.syntaxError('Expected a ":" after a key'); + end; + self.myHashMap.AddObject(FName, x.nextValue(self)); // by yangyxd + + (* + * Pairs are separated by ','. We will also tolerate ';'. + *) + + case (x.nextClean()) of + ';', ',': begin + if (x.nextClean() = '}') then begin + exit; + end; + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + end; + '}': begin + exit; + end + else begin + raise x.syntaxError('Expected a "," or "}"'); + end + end; + end; //while +end; + +function JSONObject.UpSibling: JSONObject; +var + i: Integer; +begin + if not Assigned(Parent) then + Result := nil + else begin + i := Parent.IndexOfObject(Self) - 1; + if (i > -1) and (i < Parent.Count - 1) and (Parent.Child[i] is JSONObject) then begin + Result := JSONObject(Parent.Child[i]) + end else + Result := nil; + end; +end; + +{ _Boolean } + +function _Boolean.boolValue: Boolean; +begin + Result:=fvalue; +end; + +function _Boolean.Clone: TZAbstractObject; +begin + Result:=_Boolean.create(Self.fvalue); +end; + +constructor _Boolean.create(b: boolean); +begin + fvalue:=b; +end; + +function _Boolean.toString: string; +begin + if fvalue then + Result:='true' + else + Result:='false'; +end; + +class function _Boolean.valueOf(b: boolean): _Boolean; +begin + if (b) then + Result:=_TRUE + else + Result:=_FALSE; +end; + +class function _Boolean._FALSE: _Boolean; +begin + Result:=CONST_FALSE; +end; + +class function _Boolean._TRUE: _Boolean; +begin + Result:=CONST_TRUE; +end; + +{ _String } + +function _String.Clone: TZAbstractObject; +begin + Result:=_String.create (self.fvalue); +end; + +constructor _String.create(const s: string); +begin + fvalue:=s; +end; + + +function _String.equals(const Value: TZAbstractObject): Boolean; +begin + Result:=(value is _String) and (_String (value).fvalue = fvalue); +end; + +function _String.equalsIgnoreCase(const s: string): boolean; +begin + Result:=AnsiLowerCase (s) = AnsiLowerCase (fvalue); +end; + +function _String.GetAsString: String; +begin + Result:=fvalue; +end; + +procedure _String.SetAsString(const Value: String); +begin + fvalue:=Value; +end; + +function _String.toString: string; +begin + Result:=fvalue; +end; + +{ ParseException } + +constructor ParseException.create(_message: string; index: integer); +begin + inherited createFmt(_message+#10#13' erro no caracter: %d',[index]); +end; + +{ _Integer } + +constructor _Integer.create(i: integer); +begin + fvalue:=i; +end; + +function _Integer.Clone: TZAbstractObject; +begin + Result:=_Integer.create(self.fvalue); +end; + +constructor _Integer.create(const s: string); +begin + fvalue:=strToInt64(s); +end; + +constructor _Integer.create(i: int64); +begin + fvalue := i; +end; + +function _Integer.doubleValue: double; +begin + Result:=fvalue; +end; + +function _Integer.int64Value: Int64; +begin + Result := fvalue; +end; + +function _Integer.intValue: integer; +begin + Result:=fvalue; +end; + + + +class function _Integer.parseInt(const s: string; i: integer): integer; +begin + Result:=0; //By creation_zy + case i of + 10: Result:=strToInt(s); + 16: Result:=hexToInt(s); + 8: + begin + if s='0' then exit; //By creation_zy + newNotImplmentedFeature() ; + end; + else newNotImplmentedFeature() ; //By creation_zy + end; +end; + +class function _Integer.parseInt(s: _String): integer; +begin + Result:=_Integer.parseInt(s.toString, 10); +end; + +class function _Integer.parseInt64(s: _String): int64; +begin + Result:=_Integer.parseInt64(s.toString); +end; + +class function _Integer.parseInt64(const s: string): int64; +begin + Result := strToInt64(s); +end; + +class function _Integer.toHexString(c: char): string; +begin + Result:=IntToHex(ord(c),2); +end; + +function _Integer.toString: string; +begin + Result:=intToStr(fvalue); +end; + + +{ _Double } + +constructor _Double.create(const s: string); +begin + fvalue:=StrToFloat(s, getFormatSettings); +end; + +constructor _Double.create(s: _String); +begin + create (s.toString); +end; + + +function _Double.Clone: TZAbstractObject; +begin + Result:=_Double.create(Self.fvalue); +end; + +constructor _Double.create(d: double); +begin + fvalue:=d; +end; + +function _Double.doubleValue: double; +begin + Result:=fvalue; +end; + +function _Double.int64Value: Int64; +begin + Result := Trunc(fvalue); +end; + +function _Double.intValue: integer; +begin + Result:=trunc(fvalue); +end; + +class function _Double.NaN: double; +begin + Result:=3.6e-4951; +end; + +function _Double.toString: string; +begin + Result:=floatToStr(fvalue, getFormatSettings); +end; + +{ JSONArray } + +(** + * Construct a JSONArray from a JSONTokener. + * @param x A JSONTokener + * @exception ParseException A JSONArray must start with '[' + * @exception ParseException Expected a ',' or ']' + *) +constructor JSONArray.create(x: JSONTokener); +var + Ch:Char; +begin + create; + if (x.nextClean() <> '[') then + raise x.syntaxError('A JSONArray must start with "["'); + //if (x.nextClean() = ']') then exit; + //{$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + Ch:=x.nextClean(); + if Ch=']' then exit; + while true do + begin + if (Ch = ',') then begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + myArrayList.add(nil); + end + else begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + myArrayList.add(x.nextValue(self)); + end; + case x.nextClean() of + ';',',': + begin + if (x.nextClean() = ']') then exit; + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + end; + ']': exit; + else raise x.syntaxError('Expected a "," or "]"'); + end; + Ch:=x.nextClean(); + end; +end; + +destructor JSONObject.destroy; +var + i :integer; + MyObj:TObject; +begin + for i:=Pred(myHashMap.Count) downto 0 do + begin + MyObj:=myHashMap.Objects[i]; + SafeFreeJObj(MyObj); + end; + myHashMap.Free; + inherited; +end; + +(** + * Construct a JSONArray from a Collection. + * @param collection A Collection. + *) +constructor JSONArray.create(collection: TList); +var + i: integer; +begin + inherited Create; + myArrayList:=TList.create (); + for i:=0 to collection.count -1 do begin + myArrayList.add (collection[i]); + end; +end; + +(** + * Construct an empty JSONArray. +*) +constructor JSONArray.create; +begin + inherited Create; + myArrayList:=TList.create; +end; + + +(** + * Construct a JSONArray from a source string. + * @param string A string that begins with + * [ (left bracket) + * and ends with ] (right bracket). + * @exception ParseException The string must conform to JSON syntax. + *) +constructor JSONArray.create(const s: string); +var + token:JSOnTokener; +begin + token:=JSONTokener.create(s); + try + create(token); + finally + token.free; + end; +end; + +destructor JSONArray.destroy; +var + i: integer; +begin + for i:=Pred(myArrayList.Count) downto 0 do + SafeFreeJObj(myArrayList[i]); + myArrayList.Free; + inherited; +end; + +procedure JSONArray.Assign(Source: JSONArray); +begin + Clean; + appendJSONArray(Source); +end; + +procedure JSONArray.Clean; +var + i: integer; +begin + for i:=Pred(myArrayList.Count) downto 0 do + SafeFreeJObj(myArrayList[i]); + myArrayList.Clear; //2009-12-10 By creation_zy +end; + +function JSONArray.Clone: TZAbstractObject; +begin + Result:=JSONArray.create(Self.toString); +end; + +function JSONArray.appendJSONArray(value: JSONArray): Integer; +var + i:Integer; +begin + if value=nil then + begin + Result:=0; + exit; + end; + Result:=value.length; + for i:=0 to Pred(Result) do + put(value.get(i).Clone); +end; + +(** + * Get the object value associated with an index. + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @exception NoSuchElementException + *) +function JSONArray.get(index: integer): TZAbstractObject; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o = nil) then + raise NoSuchElementException.create('JSONArray[' + intToStr(index) + + '] not found.'); + Result:=o; +end; + + +(** + * Get the boolean value associated with an index. + * The string values "true" and "false" are converted to boolean. + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + * @exception NoSuchElementException if the index is not found + * @exception ClassCastException + *) +function JSONArray.getBoolean(index: integer): boolean; +var + o: TZAbstractObject; +begin + o:=get(index); + if ((o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('false')))) then begin + Result:=false; + exit; + end else if ((o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('true')))) then begin + Result:=true; + exit; + end; + raise ClassCastException.create('JSONArray[' + intToStr(index) + + '] not a Boolean.'); +end; + +function JSONArray.GetChild(Index: Integer): TZAbstractObject; +begin + Result:=opt(index); +end; + +function JSONArray.GetCount: Integer; +begin + Result := myArrayList.Count; +end; + +(** + * Get the double value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @exception NoSuchElementException if the key is not found + * @exception NumberFormatException + * if the value cannot be converted to a number. + *) +function JSONArray.getDouble(index: integer): double; +var + o: TZAbstractObject; + d: _Double; +begin + o:=get(index); + if (o is _Number) then + begin + Result:=(_Number(o)).doubleValue(); + exit; + end; + if (o is _String) then + begin + d:= _Double.create(_String(o)); + try + Result:=d.doubleValue(); + exit; + finally + d.Free; + end; + end; + raise NumberFormatException.create('JSONObject[' + + intToStr(index) + '] is not a number.'); +end; + + +(** + * Get the int value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @exception NoSuchElementException if the key is not found + * @exception NumberFormatException + * if the value cannot be converted to a number. + *) +function JSONArray.getInt(index: integer): integer; +var + o: TZAbstractObject; +begin + o:=get(index); + if (o is _Number) then + Result:=_Number(o).intValue() + else + Result:=trunc(getDouble(index)); +end; + + +function JSONArray.getInt64(index: integer): int64; +var + o: TZAbstractObject; +begin + o:=get(index); + if (o is _Number) then + Result:=_Number(o).int64Value() + else + Result:=trunc(getDouble(index)); +end; + +(** + * Get the JSONArray associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @exception NoSuchElementException if the index is not found or if the + * value is not a JSONArray + *) +function JSONArray.getJSONArray(index: integer): JSONArray; +var + o: TZAbstractObject; +begin + o:=get(index); + if (o is JSONArray) then + begin + Result:=JSONArray(o); + exit; + end; + raise NoSuchElementException.create('JSONArray[' + intToStr(index) + + '] is not a JSONArray.'); +end; + + +(** + * Get the JSONObject associated with an index. + * @param index subscript + * @return A JSONObject value. + * @exception NoSuchElementException if the index is not found or if the + * value is not a JSONObject + *) +function JSONArray.getJSONObject(index: integer): JSONObject; +var + o: TZAbstractObject; + s: string; +begin + o:=get(index); + if (o is JSONObject) then + Result:=JSONObject(o) + else begin + if o <> nil then + s:=o.ClassName + else + s:='nil'; + raise NoSuchElementException.create('JSONArray[' + intToStr(index) + + '] is not a JSONObject is ' + s); + end; +end; + +(** + * Get the string associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A string value. + * @exception NoSuchElementException + *) +function JSONArray.getString(index: integer): string; +begin + Result:=get(index).toString(); +end; + +function JSONArray.IndexOfObject(aobj: TObject): Integer; +var i: Integer; +begin + for i := 0 to myArrayList.Count - 1 do + if opt(i) = aobj then begin + Result := I; + Exit; + end; + Result := -1; +end; + +(** + * Determine if the value is null. + * @param index The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + *) + +function JSONArray.isNull(index: integer): boolean; +var + o: TZAbstractObject; +begin + o:=opt(index); + Result:=(o = nil) or (o.equals(nil)); +end; + +(** + * Make a string from the contents of this JSONArray. The separator string + * is inserted between each element. + * Warning: This method assumes that the data structure is acyclical. + * @param separator A string that will be inserted between the elements. + * @return a string. + *) +function JSONArray.join(separator: string): string; +var + len, i: integer; + sb: string ; +begin + len:=length(); + sb:=''; + for i:=0 to len -1 do + begin + if (i > 0) then + sb:=sb + separator; + sb:= sb + JSONObject.valueToString(TZAbstractObject(myArrayList[i])); + end; + Result:=sb; +end; + +function JSONArray.LastItem: TZAbstractObject; +var + Len:Integer; +begin + Len:=length(); + if Len=0 then + Result:=nil + else + Result:=TZAbstractObject(TZAbstractObject(myArrayList[Len-1])); +end; + +(** + * Get the length of the JSONArray. + * + * @return The length (or size). + *) +function JSONArray.length: integer; +begin + Result:=myArrayList.Count; +end; + + (** + * Get the optional object value associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return An object value, or null if there is no + * object at that index. + *) +function JSONArray.opt(index: integer): TZAbstractObject; +begin + if ((index < 0) or (index >= length()) ) then + Result:=nil + else + Result:=TZAbstractObject (myArrayList[index]); +end; + +(** + * Get the optional boolean value associated with an index. + * It returns false if there is no value at that index, + * or if the value is not Boolean.TRUE or the String "true". + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + *) +function JSONArray.optBoolean(index: integer): boolean; +begin + Result:=optBoolean(index, false); +end; + +(** + * Get the optional boolean value associated with an index. + * It returns the defaultValue if there is no value at that index or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. + * @return The truth. + *) +function JSONArray.optBoolean(index: integer; + defaultValue: boolean): boolean; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + begin + if ((o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('false')))) then begin + Result:=false; + exit; + end + else if ((o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('true')))) then begin + Result:=true; + exit; + end; + end; + Result:=defaultValue; +end; + + +(** + * Get the optional double value associated with an index. + * NaN is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + *) +function JSONArray.optDouble(index: integer): double; +begin + Result:=optDouble(index, _Double.NaN); +end; + +(** + * Get the optional double value associated with an index. + * The defaultValue is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * + * @param index subscript + * @param defaultValue The default value. + * @return The value. + *) +function JSONArray.optDouble(index: integer; defaultValue :double): double; +var + o: TZAbstractObject; + d: _Double; +begin + o:=opt(index); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).doubleValue(); + exit; + end; + try + d:=_Double.create (_String (o)); + Result:=d.doubleValue ; + d.Free; + exit; + except + on e:Exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + +(** + * Get the optional int value associated with an index. + * Zero is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + *) +function JSONArray.optInt(index: integer): integer; +begin + Result:=optInt(index, 0); +end; + + +(** + * Get the optional int value associated with an index. + * The defaultValue is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + *) +function JSONArray.optInt(index, defaultValue: integer): integer; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).intValue(); + exit; //By creation_zy + end; + try + Result:=_Integer.parseInt(_String(o)); + exit; + except + on e: exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + + +function JSONArray.OptInt64(index: integer): int64; +begin + Result := OptInt64(index, 0); +end; + +function JSONArray.OptInt64(index: integer; defaultValue: int64): int64; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).int64Value(); + exit; //By creation_zy + end; + try + Result:=_Integer.parseInt64(_String(o)); + exit; + except + on e: exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + +(** + * Get the optional JSONArray associated with an index. + * @param index subscript + * @return A JSONArray value, or null if the index has no value, + * or if the value is not a JSONArray. + *) +function JSONArray.optJSONArray(index: integer): JSONArray; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o is JSONArray) then + Result:=JSONArray(o) + else + Result:=nil; +end; + +(** + * Get the optional JSONObject associated with an index. + * Null is returned if the key is not found, or null if the index has + * no value, or if the value is not a JSONObject. + * + * @param index The index must be between 0 and length() - 1. + * @return A JSONObject value. + *) +function JSONArray.optJSONObject(index: integer): JSONObject; +var + o: TZAbstractObject; +begin + o:=opt(index); + if o is JSONObject then + Result:=JSONObject(o) + else + Result:=nil; +end; + +{$IFDEF J_OBJECT} +function JSONArray.optObject(index: integer): TObject; +var + o: TZAbstractObject; +begin + o:=opt(index); + if o is _Object then + Result:=_Object(o).fvalue + else + Result:=nil; +end; +{$ENDIF} + +(** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value + * is not a string and is not null, then it is coverted to a string. + * + * @param index The index must be between 0 and length() - 1. + * @return A String value. + *) +function JSONArray.optString(index: integer): string; +begin + Result:=optString(index, ''); +end; + +(** + * Get the optional string associated with an index. + * The defaultValue is returned if the key is not found. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return A String value. + *) +function JSONArray.optString(index: integer; defaultValue: string): string; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + Result:=o.toString() + else + Result:=defaultValue; +end; + +(** + * Append a boolean value. + * + * @param value A boolean value. + * @return this. + *) +function JSONArray.put(value: boolean): JSONArray; +begin + put(_Boolean.valueOf(value)); + Result:= self; +end; + +(** + * Append a double value. + * + * @param value A double value. + * @return this. + *) +function JSONArray.put(value: double): JSONArray; +begin + put(_Double.create(value)); + Result:=self; +end; + +(** + * Append an int value. + * + * @param value An int value. + * @return this. + *) +function JSONArray.put(value: integer): JSONArray; +begin + put(_Integer.create(value)); + Result:=self; +end; + + +function JSONArray.put(value: string): JSONArray; +begin + put (_String.create (value)); + Result:=self; +end; + +{$IFDEF J_OBJECT} +function JSONArray.put ( value: TObject): JSONArray; +begin + put (_Object.create (value)); + Result:=self; +end; +{$ENDIF} + +(** + * Append an object value. + * @param value An object value. The value should be a + * Boolean, Double, Integer, JSONArray, JSObject, or String, or the + * JSONObject.NULL object. + * @return this. + *) +function JSONArray.put(value: TZAbstractObject): JSONArray; +begin + myArrayList.add(value); + Result:=self; +end; + +(** + * Put or replace a boolean value in the JSONArray. + * @param index subscript The subscript. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param value A boolean value. + * @return this. + * @exception NoSuchElementException The index must not be negative. + *) +function JSONArray.put(index: integer; value: boolean): JSONArray; +begin + put(index, _Boolean.valueOf(value)); + Result:=self; +end; + +function JSONArray.put(index, value: integer): JSONArray; +begin + put(index, _Integer.create(value)); + Result:=self; +end; + + +function JSONArray.put(index: integer; value: double): JSONArray; +begin + put(index, _Double.create(value)); + Result:=self; +end; + +function JSONArray.put(index: integer; value: string): JSONArray; +begin + put (index,_String.create (value)); + Result:=self; +end; + +(** + * Put or replace an object value in the JSONArray. + * @param index The subscript. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param value An object value. + * @return this. + * @exception NoSuchElementException The index must not be negative. + * @exception NullPointerException The index must not be null. + *) +function JSONArray.put(index: integer; value: TZAbstractObject): JSONArray; +begin + if (index < 0) then + raise NoSuchElementException.create('JSONArray['+intToStr(index)+'] not found.') + else if (value = nil) then + raise NullPointerException.create('') + else if (index < length()) then + myArrayList[index]:=value + else begin + while (index<>length()) do put(nil); + put(value); + end; + Result:=self; +end; + +{$IFDEF J_OBJECT} +function JSONArray.put(index: integer; value: TObject): JSONArray; +begin + put (index,_Object.create(value)); + Result:=self; +end; + +function JSONArray.put(index: integer; value: int64): JSONArray; +begin + put(index, _Integer.create(value)); + Result:=self; +end; + +procedure JSONArray.SetChild(Index: Integer; const Value: TZAbstractObject); +begin + put(index, Value); +end; + +{$ENDIF} + +(** + * Produce a JSONObject by combining a JSONArray of names with the values + * of this JSONArray. + * @param names A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + *) +function JSONArray.toJSONObject(names :JSONArray): JSONObject; +var + i: integer; +begin + if ((names = nil) or (names.length() = 0) or (length() = 0)) then + begin + Result:=nil; + exit; //By creation_zy + end; + Result:=JSONObject.create(); + for i:=0 to names.length() do + Result.put(names.getString(i), self.opt(i)); +end; + + +(** + * Make an JSON external form string of this JSONArray. For compactness, no + * unnecessary whitespace is added. + * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable + * representation of the array. + *) +function JSONArray.toString: string; +begin + Result:='[' + join(',') + ']'; +end; + +(** + * Make a prettyprinted JSON string of this JSONArray. + * Warning: This method assumes that the data structure is non-cyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with [ (left bracket) and ending + * with ] (right bracket). + *) +function JSONArray.toString2(indentFactor: integer): string; +begin + Result:=toString3(indentFactor, 0); +end; + +(** + * Make a prettyprinted string of this JSONArray. + * Warning: This method assumes that the data structure is non-cyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indention of the top level. + * @return a printable, displayable, transmittable + * representation of the array. + *) +function JSONArray.toList: TList; +begin + Result:=TList.create ; + Result.Assign(myArrayList,laCopy); +end; + +function JSONArray.toString3(indentFactor, indent: integer): string; +var + len, i, newindent: integer; + sb: string; +begin + len:=length(); + if (len = 0) then + begin + Result:='[]'; + exit; + end; + sb:='['; + if (len = 1) then + begin + sb:=sb + JSONObject + .valueToString(TZAbstractObject( myArrayList[0]),indentFactor, indent); + end + else begin + newindent:=indent + indentFactor; + sb:=sb + #10 ; + for i:=0 to len -1 do + begin + if i > 0 then + sb:=sb +',' + #10; + sb:=sb + SpaceStr(newindent) + (JSONObject.valueToString(TZAbstractObject(myArrayList[i]), + indentFactor, newindent)); + end; + sb:=sb + #10 + SpaceStr(indent); + end; + sb:=sb + ']'; + Result:=sb; +end; + + +{ _NULL } + +function _NULL.Clone: TZAbstractObject; +begin + Result:=CNULL; +end; + +function _NULL.Equals(const Value: TZAbstractObject): Boolean; +begin + if (value = nil) then + Result:=true + else + Result:=(value is _NULL); +end; + +function _NULL.toString: string; +begin + Result:='null'; +end; + + +{ TZAbstractObject } + +class procedure TZAbstractObject.WriteChar(avOut: TStream; const avData: Char); +begin + avOut.WriteBuffer(avData, SizeOf(Char)); +end; + +class procedure TZAbstractObject.WriteString(avOut: TStream; const avData: string); +var + l: Cardinal; +begin + l := Length(avData); + if l > 0 then + avOut.WriteBuffer(avData[1], l); +end; + +class procedure TZAbstractObject.WriteText(avOut: TStream; const avData: string; + len: Integer); +begin + if len > 0 then + avOut.WriteBuffer(avData[1], len); +end; + +function TZAbstractObject.Clone: TZAbstractObject; +begin + Result:=nil; + newNotImplmentedFeature(); +end; + +function TZAbstractObject.Equals(const Value: TZAbstractObject): Boolean; +begin + Result:=(value <> nil) and (value = self); +end; + +procedure TZAbstractObject.Free; +begin + SafeFreeJObj(Self); +end; + +class function TZAbstractObject.getBoolean(o: TZAbstractObject; DefaultValue: Boolean): Boolean; +begin + if (o<>CNULL) and (o<>nil) then + begin + if o.ClassType=_Boolean then //2009-03-06 By creation_zy + begin + Result:=_Boolean(o).fvalue; + exit; + end + else if ((o is _String) and (_String(o).equalsIgnoreCase('false'))) then + begin + Result:=false; + exit; + end + else if ((o is _String) and (_String(o).equalsIgnoreCase('true'))) then + begin + Result:=true; + exit; + end; + end; + Result:=DefaultValue; +end; + +class function TZAbstractObject.getDouble(o: TZAbstractObject; DefaultValue: Double): Double; +begin + if (o<>CNULL) and ( o <> nil ) then + begin + if (o is _Number) then + begin + Result:= _Number(o).doubleValue(); + exit; + end; + if o.ClassType=_String then + Result:=StrToFloatDef(o.toString,DefaultValue) + else + Result:=defaultValue; + end + else //By creation_zy + Result:=defaultValue; +end; + +class function TZAbstractObject.getInt(o: TZAbstractObject; DefaultValue: Integer): Integer; +begin + if (o<>CNULL) and ( o <> nil ) then + begin + if (o is _Number) then + begin + Result:=_Number(o).intValue(); + exit; + end; + if o.ClassType<>_String then + Result:=defaultValue + else + try + Result:=_Integer.parseInt(_String(o)); + except + Result:=defaultValue; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +class function TZAbstractObject.getInt64(o: TZAbstractObject; + DefaultValue: Int64): Int64; +begin + if (o<>CNULL) and ( o <> nil ) then + begin + if (o is _Number) then + begin + Result:=_Number(o).int64Value(); + exit; + end; + if o.ClassType<>_String then + Result:=defaultValue + else + try + Result:=_Integer.parseInt64(_String(o)); + except + Result:=defaultValue; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +function TZAbstractObject.Hash: LongInt; +begin + Result:=integer(addr(self)); +end; + +function TZAbstractObject.InstanceOf( + const Value: TZAbstractObject): Boolean; +begin + Result:=value is TZAbstractObject; +end; + +procedure TZAbstractObject.SaveToStream(stream: TStream); +begin + WriteString(stream, Format('%s <%p>', [ClassName, addr(Self)])); +end; + +function TZAbstractObject.toJSONArray: JSONArray; +begin + if Self is JSONArray then + Result := JSONArray(Self) + else + Result := nil; +end; + +function TZAbstractObject.toJSONObject: JSONObject; +begin + if Self is JSONObject then + Result := JSONObject(Self) + else + Result := nil; +end; + +function TZAbstractObject.ToString: string; +begin + Result:=Format('%s <%p>', [ClassName, addr(Self)]); +end; + +{$IFDEF J_OBJECT} +{ _Object } + +function _Object.Clone: TZAbstractObject; +begin + Result:=_Object.Create(fvalue); +end; + +constructor _Object.Create(value: TObject); +begin + fvalue:=value; +end; + +function _Object.Equals(const Value: TZAbstractObject): Boolean; +begin + Result:=(Value is _Object) and (_Object(Value).AsObject=AsObject); +end; + +procedure _Object.SetAsObject(const Value: TObject); +begin + fvalue:=Value; +end; + +function _Object.toString: string; +begin + if fvalue=nil then + Result:='' + else + Result:=fvalue.ClassName+'::'+IntToHex(Integer(fvalue),8); +end; +{$ENDIF} + +{ JSONBase } + +constructor JSONBase.Create; +begin + FParent := nil; +end; + +function JSONBase.GetChild(Index: Integer): TZAbstractObject; +begin + Result := nil; +end; + +function JSONBase.GetCount: Integer; +begin + Result := 0; +end; + +function JSONBase.IndexOfObject(aobj: TObject): Integer; +begin + Result := -1; +end; + +procedure JSONBase.SetChild(Index: Integer; const Value: TZAbstractObject); +begin +end; + +initialization + CONST_FALSE:=_Boolean.Create(false); + CONST_TRUE:=_Boolean.Create(true); + CNULL:=_NULL.Create; + +finalization + TObject(CONST_FALSE).Free; + TObject(CONST_TRUE).Free; + TObject(CNULL).Free; + +end. diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uLkJSON.pas" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uLkJSON.pas" new file mode 100644 index 0000000..05928f6 --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/uLkJSON.pas" @@ -0,0 +1,2626 @@ +{ + LkJSON v1.07 + + 06 november 2009 + +* Copyright (c) 2006,2007,2008,2009 Leonid Koninin +* leon_kon@users.sourceforge.net +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY Leonid Koninin ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Leonid Koninin BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + changes: + + v1.07 06/11/2009 * fixed a bug in js_string - thanks to Andrew G. Khodotov + * fixed error with double-slashes - thanks to anonymous user + * fixed a BOM bug in parser, thanks to jasper_dale + v1.06 13/03/2009 * fixed a bug in string parsing routine + * looked routine from the Adrian M. Jones, and get some + ideas from it; thanks a lot, Adrian! + * checked error reported by phpop and fix it in the string + routine; also, thanks for advice. + v1.05 26/01/2009 + added port to D2009 by Daniele Teti, thanx a lot! really, + i haven't the 2009 version, so i can't play with it. I was + add USE_D2009 directive below, disabled by default + * fixed two small bugs in parsing object: errors with empty + object and list; thanx to RSDN's delphi forum members + * fixed "[2229135] Value deletion is broken" tracker + issue, thanx to anonymous sender provided code for + tree version + * fixed js_string according to "[1917047] (much) faster + js_string Parse" tracker issue by Joao Inacio; a lot of + thanx, great speedup! + + v1.04 05/04/2008 + a declaration of Field property moved from TlkJSONobject + to TlkJSONbase; thanx for idea to Andrey Lukyanov; this + improve objects use, look the bottom of SAMPLE2.DPR + * fixed field name in TlkJSONobject to WideString + v1.03 14/03/2008 + added a code for generating readable JSON text, sended to + me by Kusnassriyanto Saiful Bahri, thanx to him! + * from this version, library distributed with BSD + license, more pleasure for commercial programmers :) + * was rewritten internal storing of objects, repacing + hash tables with balanced trees (AA tree, by classic + author's variant). On mine machine, with enabled fastmm, + tree variant is about 30% slower in from-zero creation, + but about 50% faster in parsing; also deletion of + objects will be much faster than a hash-one. + Hashes (old-style) can be switched on by enabling + USE_HASH directive below + v1.02 14/09/2007 * fix mistypes in diffrent places; thanx for reports + to Aleksandr Fedorov and Tobias Wrede + v1.01 18/05/2007 * fix small bug in new text generation routine, check + library for leaks by fastmm4; thanx for idea and comments + for Glynn Owen + v1.00 12/05/2007 * some fixes in new code (mistypes, mistypes...) + * also many fixes by ideas of Henri Gourvest - big thanx + for him again; he send me code for thread-safe initializing + of hash table, some FPC-compatible issues (not tested by + myself) and better code for localization in latest + delphi versions; very, very big thanx! + * rewritten procedure of json text generating, with wich + work of it speeds up 4-5 times (on test) its good for + a large objects + * started a large work for making source code self-doc + (not autodoc!) + v0.99 10/05/2007 + add functions to list and object: + function getInt(idx: Integer): Integer; + function getString(idx: Integer): String; + function getWideString(idx: Integer):WideString; + function getDouble(idx: Integer): Double; + function getBoolean(idx: Integer): Boolean; + + add overloaded functions to object: + function getDouble(nm: String): Double; overload; + function getInt(nm: String): Integer; overload; + function getString(nm: String): String; overload; + function getWideString(nm: String): WideString; overload; + function getBoolean(nm: String): Boolean; overload; + * changed storing mech of TlkJSONcustomlist descendants from + dynamic array to TList; this gives us great speedup with + lesser changes; thanx for idea to Henri Gourvest + * also reworked hashtable to work with TList, so it also + increase speed of work + v0.98 09/05/2007 * fix small bug in work with WideStrings(UTF8), thanx to + IVO GELOV to description and sources + v0.97 10/04/2007 + add capabilities to work with KOL delphi projects; for + this will define KOL variable in begin of text; of course, + in this case object TlkJSONstreamed is not compiled. + v0.96 03/30/2007 + add TlkJSONFuncEnum and method ForEach in all + TlkJSONcustomlist descendants + + add property UseHash(r/o) to TlkJSONobject, and parameter + UseHash:Boolean to object constructors; set it to false + allow to disable using of hash-table, what can increase + speed of work in case of objects with low number of + methods(fields); [by default it is true] + + added conditional compile directive DOTNET for use in .Net + based delphi versions; remove dot in declaration below + (thanx for idea and sample code to Tim Radford) + + added property HashOf to TlkHashTable to allow use of + users hash functions; on enter is widestring, on exit is + cardinal (32 bit unsigned). Original HashOf renamed to + DefaultHashOf + * hash table object of TlkJSONobject wrapped by property called + HashTable + * fixed some minor bugs + v0.95 03/29/2007 + add object TlkJSONstreamed what descendant of TlkJSON and + able to load/save JSON objects from/to streams/files. + * fixed small bug in generating of unicode strings representation + v0.94 03/27/2007 + add properties NameOf and FieldByIndex to TlkJSONobject + * fix small error in parsing unicode chars + * small changes in hashing code (try to speed up) + v0.93 03/05/2007 + add overloaded functions to list and object + + add enum type TlkJSONtypes + + add functions: SelfType:TlkJSONtypes and + SelfTypeName: String to every TlkJSONbase child + * fix mistype 'IndefOfName' to 'IndexOfName' + * fix mistype 'IndefOfObject' to 'IndexOfObject' + v0.92 03/02/2007 + add some fix to TlkJSON.ParseText to fix bug with parsing + objects - object methods not always added properly + to hash array (thanx to Chris Matheson) + ... +} + +unit uLkJSON; + +{$IFDEF fpc} + {$MODE objfpc} + {$H+} + {.$DEFINE HAVE_FORMATSETTING} +{$ELSE} + {$IF RTLVersion > 14.00} + {$DEFINE HAVE_FORMATSETTING} + {$IF RTLVersion > 19.00} + {$DEFINE USE_D2009} + {$IFEND} + {$IFEND} +{$ENDIF} + +interface + +{.$DEFINE USE_D2009} +{.$DEFINE KOL} +{.$define DOTNET} +{$DEFINE THREADSAFE} +{$DEFINE NEW_STYLE_GENERATE} +{.$DEFINE USE_HASH} +{.$DEFINE TCB_EXT} + +uses windows, + SysUtils, +{$IFNDEF KOL} + classes, +{$ELSE} + kol, +{$ENDIF} + variants; + +type + TlkJSONtypes = (jsBase, jsNumber, jsString, jsBoolean, jsNull, + jsList, jsObject); + +{$IFDEF DOTNET} + + TlkJSONdotnetclass = class + public + constructor Create; + destructor Destroy; override; + procedure AfterConstruction; virtual; + procedure BeforeDestruction; virtual; + end; + +{$ENDIF DOTNET} + + TlkJSONbase = class{$IFDEF DOTNET}(TlkJSONdotnetclass){$ENDIF} + protected + function GetValue: variant; virtual; + procedure SetValue(const AValue: variant); virtual; + function GetChild(idx: Integer): TlkJSONbase; virtual; + procedure SetChild(idx: Integer; const AValue: TlkJSONbase); + virtual; + function GetCount: Integer; virtual; + function GetField(AName: Variant):TlkJSONbase; virtual; + public + property Field[AName: Variant]: TlkJSONbase read GetField; + property Count: Integer read GetCount; + property Child[idx: Integer]: TlkJSONbase read GetChild write SetChild; + property Value: variant read GetValue write SetValue; + class function SelfType: TlkJSONtypes; virtual; + class function SelfTypeName: string; virtual; + end; + + TlkJSONnumber = class(TlkJSONbase) + protected + FValue: extended; + function GetValue: Variant; override; + procedure SetValue(const AValue: Variant); override; + public + procedure AfterConstruction; override; + class function Generate(AValue: extended = 0): TlkJSONnumber; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONstring = class(TlkJSONbase) + protected + FValue: WideString; + function GetValue: Variant; override; + procedure SetValue(const AValue: Variant); override; + public + procedure AfterConstruction; override; + class function Generate(const wsValue: WideString = ''): + TlkJSONstring; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONboolean = class(TlkJSONbase) + protected + FValue: Boolean; + function GetValue: Variant; override; + procedure SetValue(const AValue: Variant); override; + public + procedure AfterConstruction; override; + class function Generate(AValue: Boolean = true): TlkJSONboolean; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONnull = class(TlkJSONbase) + protected + function GetValue: Variant; override; + function Generate: TlkJSONnull; + public + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONFuncEnum = procedure(ElName: string; Elem: TlkJSONbase; + data: pointer; var Continue: Boolean) of object; + + TlkJSONcustomlist = class(TlkJSONbase) + protected +// FValue: array of TlkJSONbase; + fList: TList; + function GetCount: Integer; override; + function GetChild(idx: Integer): TlkJSONbase; override; + procedure SetChild(idx: Integer; const AValue: TlkJSONbase); + override; + function ForEachElement(idx: Integer; var nm: string): + TlkJSONbase; virtual; + + function GetField(AName: Variant):TlkJSONbase; override; + + function _Add(obj: TlkJSONbase): Integer; virtual; + procedure _Delete(iIndex: Integer); virtual; + function _IndexOf(obj: TlkJSONbase): Integer; virtual; + public + procedure ForEach(fnCallBack: TlkJSONFuncEnum; pUserData: + pointer); + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + + function getInt(idx: Integer): Integer; virtual; + function getString(idx: Integer): string; virtual; + function getWideString(idx: Integer): WideString; virtual; + function getDouble(idx: Integer): Double; virtual; + function getBoolean(idx: Integer): Boolean; virtual; + end; + + TlkJSONlist = class(TlkJSONcustomlist) + protected + public + function Add(obj: TlkJSONbase): Integer; overload; + + function Add(aboolean: Boolean): Integer; overload; + function Add(nmb: double): Integer; overload; + function Add(s: string): Integer; overload; + function Add(const ws: WideString): Integer; overload; + function Add(inmb: Integer): Integer; overload; + + procedure Delete(idx: Integer); + function IndexOf(obj: TlkJSONbase): Integer; + class function Generate: TlkJSONlist; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONobjectmethod = class(TlkJSONbase) + protected + FValue: TlkJSONbase; + FName: WideString; + procedure SetName(const AValue: WideString); + public + property ObjValue: TlkJSONbase read FValue; + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + property Name: WideString read FName write SetName; + class function Generate(const aname: WideString; aobj: TlkJSONbase): + TlkJSONobjectmethod; + end; + +{$IFDEF USE_HASH} + PlkHashItem = ^TlkHashItem; + TlkHashItem = packed record + hash: cardinal; + index: Integer; + end; + + TlkHashFunction = function(const ws: WideString): cardinal of + object; + + TlkHashTable = class + private + FParent: TObject; // TCB:parent for check chaining op. + FHashFunction: TlkHashFunction; + procedure SetHashFunction(const AValue: TlkHashFunction); + protected + a_x: array[0..255] of TList; + procedure hswap(j, k, l: Integer); + function InTable(const ws: WideString; var i, j, k: cardinal): + Boolean; + public + function counters: string; + + function DefaultHashOf(const ws: WideString): cardinal; + function SimpleHashOf(const ws: WideString): cardinal; + + property HashOf: TlkHashFunction read FHashFunction write + SetHashFunction; + + function IndexOf(const ws: WideString): Integer; + + procedure AddPair(const ws: WideString; idx: Integer); + procedure Delete(const ws: WideString); + + constructor Create; + destructor Destroy; override; + end; + +{$ELSE} + +// implementation based on "Arne Andersson, Balanced Search Trees Made Simpler" + + PlkBalNode = ^TlkBalNode; + TlkBalNode = packed record + left,right: PlkBalNode; + level: byte; + key: Integer; + nm: WideString; + end; + + TlkBalTree = class + protected + fdeleted,flast,fbottom,froot: PlkBalNode; + procedure skew(var t:PlkBalNode); + procedure split(var t:PlkBalNode); + public + function counters: string; + + procedure Clear; + + function Insert(const ws: WideString; x: Integer): Boolean; + function Delete(const ws: WideString): Boolean; + + function IndexOf(const ws: WideString): Integer; + + constructor Create; + destructor Destroy; override; + end; +{$ENDIF USE_HASH} + + TlkJSONobject = class(TlkJSONcustomlist) + protected +{$IFDEF USE_HASH} + ht: TlkHashTable; +{$ELSE} + ht: TlkBalTree; +{$ENDIF USE_HASH} + FUseHash: Boolean; + function GetFieldByIndex(idx: Integer): TlkJSONbase; + function GetNameOf(idx: Integer): WideString; + procedure SetFieldByIndex(idx: Integer; const AValue: TlkJSONbase); +{$IFDEF USE_HASH} + function GetHashTable: TlkHashTable; +{$ELSE} + function GetHashTable: TlkBalTree; +{$ENDIF USE_HASH} + function ForEachElement(idx: Integer; var nm: string): TlkJSONbase; + override; + function GetField(AName: Variant):TlkJSONbase; override; + public + property UseHash: Boolean read FUseHash; +{$IFDEF USE_HASH} + property HashTable: TlkHashTable read GetHashTable; +{$ELSE} + property HashTable: TlkBalTree read GetHashTable; +{$ENDIF USE_HASH} + + function Add(const aname: WideString; aobj: TlkJSONbase): Integer; + overload; + + function OldGetField(nm: WideString): TlkJSONbase; + procedure OldSetField(nm: WideString; const AValue: TlkJSONbase); + + function Add(const aname: WideString; aboolean: Boolean): Integer; overload; + function Add(const aname: WideString; nmb: double): Integer; overload; + function Add(const aname: WideString; s: string): Integer; overload; + function Add(const aname: WideString; const ws: WideString): Integer; + overload; + function Add(const aname: WideString; inmb: Integer): Integer; overload; + + procedure Delete(idx: Integer); + function IndexOfName(const aname: WideString): Integer; + function IndexOfObject(aobj: TlkJSONbase): Integer; + property Field[nm: WideString]: TlkJSONbase read OldGetField + write OldSetField; default; + + constructor Create(bUseHash: Boolean = true); + destructor Destroy; override; + + class function Generate(AUseHash: Boolean = true): TlkJSONobject; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + + property FieldByIndex[idx: Integer]: TlkJSONbase read GetFieldByIndex + write SetFieldByIndex; + property NameOf[idx: Integer]: WideString read GetNameOf; + + function getDouble(idx: Integer): Double; overload; override; + function getInt(idx: Integer): Integer; overload; override; + function getString(idx: Integer): string; overload; override; + function getWideString(idx: Integer): WideString; overload; override; + function getBoolean(idx: Integer): Boolean; overload; override; + + function {$ifdef TCB_EXT}getDoubleFromName{$else}getDouble{$endif} + (nm: string): Double; overload; + function {$ifdef TCB_EXT}getIntFromName{$else}getInt{$endif} + (nm: string): Integer; overload; + function {$ifdef TCB_EXT}getStringFromName{$else}getString{$endif} + (nm: string): string; overload; + function {$ifdef TCB_EXT}getWideStringFromName{$else}getWideString{$endif} + (nm: string): WideString; overload; + function {$ifdef TCB_EXT}getBooleanFromName{$else}getBoolean{$endif} + (nm: string): Boolean; overload; + end; + + TlkJSON = class + public + class function ParseText(const txt: string): TlkJSONbase; + class function GenerateText(obj: TlkJSONbase): string; + end; + +{$IFNDEF KOL} + TlkJSONstreamed = class(TlkJSON) + class function LoadFromStream(src: TStream): TlkJSONbase; + class procedure SaveToStream(obj: TlkJSONbase; dst: TStream); + class function LoadFromFile(srcname: string): TlkJSONbase; + class procedure SaveToFile(obj: TlkJSONbase; dstname: string); + end; +{$ENDIF} + +function GenerateReadableText(vObj: TlkJSONbase; var vLevel: + Integer): string; + +implementation + +uses math,strutils; + +type + ElkIntException = class(Exception) + public + idx: Integer; + constructor Create(idx: Integer; msg: string); + end; + +// author of next two functions is Kusnassriyanto Saiful Bahri + +function Indent(vTab: Integer): string; +begin + result := DupeString(' ', vTab); +end; + +function GenerateReadableText(vObj: TlkJSONbase; var vLevel: + Integer): string; +var + i: Integer; + vStr: string; + xs: TlkJSONstring; +begin + vLevel := vLevel + 1; + if vObj is TlkJSONObject then + begin + vStr := ''; + for i := 0 to TlkJSONobject(vObj).Count - 1 do + begin + if vStr <> '' then + begin + vStr := vStr + ','#13#10; + end; + vStr := vStr + Indent(vLevel) + + GenerateReadableText(TlkJSONobject(vObj).Child[i], vLevel); + end; + if vStr <> '' then + begin + vStr := '{'#13#10 + vStr + #13#10 + Indent(vLevel - 1) + '}'; + end + else + begin + vStr := '{}'; + end; + result := vStr; + end + else if vObj is TlkJSONList then + begin + vStr := ''; + for i := 0 to TlkJSONList(vObj).Count - 1 do + begin + if vStr <> '' then + begin + vStr := vStr + ','#13#10; + end; + vStr := vStr + Indent(vLevel) + + GenerateReadableText(TlkJSONList(vObj).Child[i], vLevel); + end; + if vStr <> '' then + begin + vStr := '['#13#10 + vStr + #13#10 + Indent(vLevel - 1) + ']'; + end + else + begin + vStr := '[]'; + end; + result := vStr; + end + else if vObj is TlkJSONobjectmethod then + begin + vStr := ''; + xs := TlkJSONstring.Create; + try + xs.Value := TlkJSONobjectMethod(vObj).Name; + vStr := GenerateReadableText(xs, vLevel); + vLevel := vLevel - 1; + vStr := vStr + ':' + GenerateReadableText(TlkJSONbase( + TlkJSONobjectmethod(vObj).ObjValue), vLevel); + //vStr := vStr + ':' + GenerateReadableText(TlkJSONbase(vObj), vLevel); + vLevel := vLevel + 1; + result := vStr; + finally + xs.Free; + end; + end + else + begin + if vObj is TlkJSONobjectmethod then + begin + if TlkJSONobjectMethod(vObj).Name <> '' then + begin + end; + end; + result := TlkJSON.GenerateText(vObj); + end; + vLevel := vLevel - 1; +end; + +// author of this routine is IVO GELOV + +function code2utf(iNumber: Integer): UTF8String; +begin + if iNumber < 128 then Result := chr(iNumber) + else if iNumber < 2048 then + Result := chr((iNumber shr 6) + 192) + chr((iNumber and 63) + 128) + else if iNumber < 65536 then + Result := chr((iNumber shr 12) + 224) + chr(((iNumber shr 6) and + 63) + 128) + chr((iNumber and 63) + 128) + else if iNumber < 2097152 then + Result := chr((iNumber shr 18) + 240) + chr(((iNumber shr 12) and + 63) + 128) + chr(((iNumber shr 6) and 63) + 128) + + chr((iNumber and 63) + 128); +end; + +{ TlkJSONbase } + +function TlkJSONbase.GetChild(idx: Integer): TlkJSONbase; +begin + result := nil; +end; + +function TlkJSONbase.GetCount: Integer; +begin + result := 0; +end; + +function TlkJSONbase.GetField(AName: Variant):TlkJSONbase; +begin + result := self; +end; + +function TlkJSONbase.GetValue: variant; +begin + result := variants.Null; +end; + +class function TlkJSONbase.SelfType: TlkJSONtypes; +begin + result := jsBase; +end; + +class function TlkJSONbase.SelfTypeName: string; +begin + result := 'jsBase'; +end; + +procedure TlkJSONbase.SetChild(idx: Integer; const AValue: + TlkJSONbase); +begin + +end; + +procedure TlkJSONbase.SetValue(const AValue: variant); +begin + +end; + +{ TlkJSONnumber } + +procedure TlkJSONnumber.AfterConstruction; +begin + inherited; + FValue := 0; +end; + +class function TlkJSONnumber.Generate(AValue: extended): + TlkJSONnumber; +begin + result := TlkJSONnumber.Create; + result.FValue := AValue; +end; + +function TlkJSONnumber.GetValue: Variant; +begin + result := FValue; +end; + +class function TlkJSONnumber.SelfType: TlkJSONtypes; +begin + result := jsNumber; +end; + +class function TlkJSONnumber.SelfTypeName: string; +begin + result := 'jsNumber'; +end; + +procedure TlkJSONnumber.SetValue(const AValue: Variant); +begin + FValue := VarAsType(AValue, varDouble); +end; + +{ TlkJSONstring } + +procedure TlkJSONstring.AfterConstruction; +begin + inherited; + FValue := ''; +end; + +class function TlkJSONstring.Generate(const wsValue: WideString): + TlkJSONstring; +begin + result := TlkJSONstring.Create; + result.FValue := wsValue; +end; + +function TlkJSONstring.GetValue: Variant; +begin + result := FValue; +end; + +class function TlkJSONstring.SelfType: TlkJSONtypes; +begin + result := jsString; +end; + +class function TlkJSONstring.SelfTypeName: string; +begin + result := 'jsString'; +end; + +procedure TlkJSONstring.SetValue(const AValue: Variant); +begin + FValue := VarToWideStr(AValue); +end; + +{ TlkJSONboolean } + +procedure TlkJSONboolean.AfterConstruction; +begin + FValue := false; +end; + +class function TlkJSONboolean.Generate(AValue: Boolean): + TlkJSONboolean; +begin + result := TlkJSONboolean.Create; + result.Value := AValue; +end; + +function TlkJSONboolean.GetValue: Variant; +begin + result := FValue; +end; + +class function TlkJSONboolean.SelfType: TlkJSONtypes; +begin + Result := jsBoolean; +end; + +class function TlkJSONboolean.SelfTypeName: string; +begin + Result := 'jsBoolean'; +end; + +procedure TlkJSONboolean.SetValue(const AValue: Variant); +begin + FValue := boolean(AValue); +end; + +{ TlkJSONnull } + +function TlkJSONnull.Generate: TlkJSONnull; +begin + result := TlkJSONnull.Create; +end; + +function TlkJSONnull.GetValue: Variant; +begin + result := variants.Null; +end; + +class function TlkJSONnull.SelfType: TlkJSONtypes; +begin + result := jsNull; +end; + +class function TlkJSONnull.SelfTypeName: string; +begin + result := 'jsNull'; +end; + +{ TlkJSONcustomlist } + +function TlkJSONcustomlist._Add(obj: TlkJSONbase): Integer; +begin + if not Assigned(obj) then + begin + result := -1; + exit; + end; + result := fList.Add(obj); +end; + +procedure TlkJSONcustomlist.AfterConstruction; +begin + inherited; + fList := TList.Create; +end; + +procedure TlkJSONcustomlist.BeforeDestruction; +var + i: Integer; +begin + for i := (Count - 1) downto 0 do _Delete(i); + fList.Free; + inherited; +end; + +// renamed + +procedure TlkJSONcustomlist._Delete(iIndex: Integer); +var + idx: Integer; +begin + if not ((iIndex < 0) or (iIndex >= Count)) then + begin + if fList.Items[iIndex] <> nil then + TlkJSONbase(fList.Items[iIndex]).Free; + idx := pred(fList.Count); + if iIndex= Count) then + begin + result := nil; + end + else + begin + result := fList.Items[idx]; + end; +end; + +function TlkJSONcustomlist.GetCount: Integer; +begin + result := fList.Count; +end; + +function TlkJSONcustomlist._IndexOf(obj: TlkJSONbase): Integer; +begin + result := fList.IndexOf(obj); +end; + +procedure TlkJSONcustomlist.SetChild(idx: Integer; const AValue: + TlkJSONbase); +begin + if not ((idx < 0) or (idx >= Count)) then + begin + if fList.Items[idx] <> nil then + TlkJSONbase(fList.Items[idx]).Free; + fList.Items[idx] := AValue; + end; +end; + +procedure TlkJSONcustomlist.ForEach(fnCallBack: TlkJSONFuncEnum; + pUserData: + pointer); +var + iCount: Integer; + IsContinue: Boolean; + anJSON: TlkJSONbase; + wsObject: string; +begin + if not assigned(fnCallBack) then exit; + IsContinue := true; + for iCount := 0 to GetCount - 1 do + begin + anJSON := ForEachElement(iCount, wsObject); + if assigned(anJSON) then + fnCallBack(wsObject, anJSON, pUserData, IsContinue); + if not IsContinue then break; + end; +end; + +///---- renamed to here + +function TlkJSONcustomlist.GetField(AName: Variant):TlkJSONbase; +var + index: Integer; +begin + if VarIsNumeric(AName) then + begin + index := integer(AName); + result := GetChild(index); + end + else + begin + result := inherited GetField(AName); + end; +end; + +function TlkJSONcustomlist.ForEachElement(idx: Integer; var nm: + string): TlkJSONbase; +begin + nm := inttostr(idx); + result := GetChild(idx); +end; + +function TlkJSONcustomlist.getDouble(idx: Integer): Double; +var + jn: TlkJSONnumber; +begin + jn := Child[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := jn.Value; +end; + +function TlkJSONcustomlist.getInt(idx: Integer): Integer; +var + jn: TlkJSONnumber; +begin + jn := Child[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := round(int(jn.Value)); +end; + +function TlkJSONcustomlist.getString(idx: Integer): string; +var + js: TlkJSONstring; +begin + js := Child[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := VarToStr(js.Value); +end; + +function TlkJSONcustomlist.getWideString(idx: Integer): WideString; +var + js: TlkJSONstring; +begin + js := Child[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := VarToWideStr(js.Value); +end; + +function TlkJSONcustomlist.getBoolean(idx: Integer): Boolean; +var + jb: TlkJSONboolean; +begin + jb := Child[idx] as TlkJSONboolean; + if not assigned(jb) then result := false + else result := jb.Value; +end; + +{ TlkJSONobjectmethod } + +procedure TlkJSONobjectmethod.AfterConstruction; +begin + inherited; + FValue := nil; + FName := ''; +end; + +procedure TlkJSONobjectmethod.BeforeDestruction; +begin + FName := ''; + if FValue <> nil then + begin + FValue.Free; + FValue := nil; + end; + inherited; +end; + +class function TlkJSONobjectmethod.Generate(const aname: WideString; + aobj: TlkJSONbase): TlkJSONobjectmethod; +begin + result := TlkJSONobjectmethod.Create; + result.FName := aname; + result.FValue := aobj; +end; + +procedure TlkJSONobjectmethod.SetName(const AValue: WideString); +begin + FName := AValue; +end; + +{ TlkJSONlist } + +function TlkJSONlist.Add(obj: TlkJSONbase): Integer; +begin + result := _Add(obj); +end; + +function TlkJSONlist.Add(nmb: double): Integer; +begin + Result := self.Add(TlkJSONnumber.Generate(nmb)); +end; + +function TlkJSONlist.Add(aboolean: Boolean): Integer; +begin + Result := self.Add(TlkJSONboolean.Generate(aboolean)); +end; + +function TlkJSONlist.Add(inmb: Integer): Integer; +begin + Result := self.Add(TlkJSONnumber.Generate(inmb)); +end; + +function TlkJSONlist.Add(const ws: WideString): Integer; +begin + Result := self.Add(TlkJSONstring.Generate(ws)); +end; + +function TlkJSONlist.Add(s: string): Integer; +begin + Result := self.Add(TlkJSONstring.Generate(s)); +end; + +procedure TlkJSONlist.Delete(idx: Integer); +begin + _Delete(idx); +end; + +class function TlkJSONlist.Generate: TlkJSONlist; +begin + result := TlkJSONlist.Create; +end; + +function TlkJSONlist.IndexOf(obj: TlkJSONbase): Integer; +begin + result := _IndexOf(obj); +end; + +class function TlkJSONlist.SelfType: TlkJSONtypes; +begin + result := jsList; +end; + +class function TlkJSONlist.SelfTypeName: string; +begin + result := 'jsList'; +end; + +{ TlkJSONobject } + +function TlkJSONobject.Add(const aname: WideString; aobj: + TlkJSONbase): + Integer; +var + mth: TlkJSONobjectmethod; +begin + if not assigned(aobj) then + begin + result := -1; + exit; + end; + mth := TlkJSONobjectmethod.Create; + mth.FName := aname; + mth.FValue := aobj; + result := self._Add(mth); + if FUseHash then +{$IFDEF USE_HASH} + ht.AddPair(aname, result); +{$ELSE} + ht.Insert(aname, result); +{$ENDIF USE_HASH} +end; + +procedure TlkJSONobject.Delete(idx: Integer); +var + i,j,k:cardinal; + mth: TlkJSONobjectmethod; +begin + if (idx >= 0) and (idx < Count) then + begin +// mth := FValue[idx] as TlkJSONobjectmethod; + mth := TlkJSONobjectmethod(fList.Items[idx]); + if FUseHash then + begin + ht.Delete(mth.FName); + end; + end; + _Delete(idx); +{$ifdef USE_HASH} + if (idx -1 then + begin +// mth := TlkJSONobjectmethod(FValue[i]); + mth := TlkJSONobjectmethod(fList.Items[i]); + mth.FValue := AValue; + end; +end; + +function TlkJSONobject.Add(const aname: WideString; nmb: double): + Integer; +begin + Result := self.Add(aname, TlkJSONnumber.Generate(nmb)); +end; + +function TlkJSONobject.Add(const aname: WideString; aboolean: Boolean): + Integer; +begin + Result := self.Add(aname, TlkJSONboolean.Generate(aboolean)); +end; + +function TlkJSONobject.Add(const aname: WideString; s: string): + Integer; +begin + Result := self.Add(aname, TlkJSONstring.Generate(s)); +end; + +function TlkJSONobject.Add(const aname: WideString; inmb: Integer): + Integer; +begin + Result := self.Add(aname, TlkJSONnumber.Generate(inmb)); +end; + +function TlkJSONobject.Add(const aname, ws: WideString): Integer; +begin + Result := self.Add(aname, TlkJSONstring.Generate(ws)); +end; + +class function TlkJSONobject.SelfType: TlkJSONtypes; +begin + Result := jsObject; +end; + +class function TlkJSONobject.SelfTypeName: string; +begin + Result := 'jsObject'; +end; + +function TlkJSONobject.GetFieldByIndex(idx: Integer): TlkJSONbase; +var + nm: WideString; +begin + nm := GetNameOf(idx); + if nm <> '' then + begin + result := Field[nm]; + end + else + begin + result := nil; + end; +end; + +function TlkJSONobject.GetNameOf(idx: Integer): WideString; +var + mth: TlkJSONobjectmethod; +begin + if (idx < 0) or (idx >= Count) then + begin + result := ''; + end + else + begin + mth := Child[idx] as TlkJSONobjectmethod; + result := mth.Name; + end; +end; + +procedure TlkJSONobject.SetFieldByIndex(idx: Integer; + const AValue: TlkJSONbase); +var + nm: WideString; +begin + nm := GetNameOf(idx); + if nm <> '' then + begin + Field[nm] := AValue; + end; +end; + +function TlkJSONobject.ForEachElement(idx: Integer; + var nm: string): TlkJSONbase; +begin + nm := GetNameOf(idx); + result := GetFieldByIndex(idx); +end; + +function TlkJSONobject.GetField(AName: Variant):TlkJSONbase; +begin + if VarIsStr(AName) then + result := OldGetField(VarToWideStr(AName)) + else + result := inherited GetField(AName); +end; + +{$IFDEF USE_HASH} +function TlkJSONobject.GetHashTable: TlkHashTable; +{$ELSE} +function TlkJSONobject.GetHashTable: TlkBalTree; +{$ENDIF USE_HASH} +begin + result := ht; +end; + +constructor TlkJSONobject.Create(bUseHash: Boolean); +begin + inherited Create; + FUseHash := bUseHash; +{$IFDEF USE_HASH} + ht := TlkHashTable.Create; + ht.FParent := self; +{$ELSE} + ht := TlkBalTree.Create; +{$ENDIF} +end; + +destructor TlkJSONobject.Destroy; +begin + if assigned(ht) then FreeAndNil(ht); + inherited; +end; + +function TlkJSONobject.getDouble(idx: Integer): Double; +var + jn: TlkJSONnumber; +begin + jn := FieldByIndex[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := jn.Value; +end; + +function TlkJSONobject.getInt(idx: Integer): Integer; +var + jn: TlkJSONnumber; +begin + jn := FieldByIndex[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := round(int(jn.Value)); +end; + +function TlkJSONobject.getString(idx: Integer): string; +var + js: TlkJSONstring; +begin + js := FieldByIndex[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := vartostr(js.Value); +end; + +function TlkJSONobject.getWideString(idx: Integer): WideString; +var + js: TlkJSONstring; +begin + js := FieldByIndex[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := VarToWideStr(js.Value); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getDoubleFromName(nm: string): Double; +{$else} +function TlkJSONobject.getDouble(nm: string): Double; +{$endif} +begin + result := getDouble(IndexOfName(nm)); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getIntFromName(nm: string): Integer; +{$else} +function TlkJSONobject.getInt(nm: string): Integer; +{$endif} +begin + result := getInt(IndexOfName(nm)); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getStringFromName(nm: string): string; +{$else} +function TlkJSONobject.getString(nm: string): string; +{$endif} +begin + result := getString(IndexOfName(nm)); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getWideStringFromName(nm: string): WideString; +{$else} +function TlkJSONobject.getWideString(nm: string): WideString; +{$endif} +begin + result := getWideString(IndexOfName(nm)); +end; + +function TlkJSONobject.getBoolean(idx: Integer): Boolean; +var + jb: TlkJSONboolean; +begin + jb := FieldByIndex[idx] as TlkJSONboolean; + if not assigned(jb) then result := false + else result := jb.Value; +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getBooleanFromName(nm: string): Boolean; +{$else} +function TlkJSONobject.getBoolean(nm: string): Boolean; +{$endif} +begin + result := getBoolean(IndexOfName(nm)); +end; + +{ TlkJSON } + +class function TlkJSON.GenerateText(obj: TlkJSONbase): string; +var +{$IFDEF HAVE_FORMATSETTING} + fs: TFormatSettings; +{$ENDIF} + pt1, pt0, pt2: PChar; + ptsz: cardinal; + +{$IFNDEF NEW_STYLE_GENERATE} + + function gn_base(obj: TlkJSONbase): string; + var + ws: string; + i, j: Integer; + xs: TlkJSONstring; + begin + result := ''; + if not assigned(obj) then exit; + if obj is TlkJSONnumber then + begin +{$IFDEF HAVE_FORMATSETTING} + result := FloatToStr(TlkJSONnumber(obj).FValue, fs); +{$ELSE} + result := FloatToStr(TlkJSONnumber(obj).FValue); + i := pos(DecimalSeparator, result); + if (DecimalSeparator <> '.') and (i > 0) then + result[i] := '.'; +{$ENDIF} + end + else if obj is TlkJSONstring then + begin + ws := UTF8Encode(TlkJSONstring(obj).FValue); + i := 1; + result := '"'; + while i <= length(ws) do + begin + case ws[i] of + '/', '\', '"': result := result + '\' + ws[i]; + #8: result := result + '\b'; + #9: result := result + '\t'; + #10: result := result + '\n'; + #13: result := result + '\r'; + #12: result := result + '\f'; + else + if ord(ws[i]) < 32 then + result := result + '\u' + inttohex(ord(ws[i]), 4) + else + result := result + ws[i]; + end; + inc(i); + end; + result := result + '"'; + end + else if obj is TlkJSONboolean then + begin + if TlkJSONboolean(obj).FValue then + result := 'true' + else + result := 'false'; + end + else if obj is TlkJSONnull then + begin + result := 'null'; + end + else if obj is TlkJSONlist then + begin + result := '['; + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then result := result + ','; + result := result + gn_base(TlkJSONlist(obj).Child[i]); + end; + result := result + ']'; + end + else if obj is TlkJSONobjectmethod then + begin + try + xs := TlkJSONstring.Create; + xs.FValue := TlkJSONobjectmethod(obj).FName; + result := gn_base(TlkJSONbase(xs)) + ':'; + result := result + + gn_base(TlkJSONbase(TlkJSONobjectmethod(obj).FValue)); + finally + if assigned(xs) then FreeAndNil(xs); + end; + end + else if obj is TlkJSONobject then + begin + result := '{'; + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then result := result + ','; + result := result + gn_base(TlkJSONobject(obj).Child[i]); + end; + result := result + '}'; + end; + end; +{$ELSE} + + procedure get_more_memory; + var + delta: cardinal; + begin + delta := 50000; + if pt0 = nil then + begin + pt0 := AllocMem(delta); + ptsz := 0; + pt1 := pt0; + end + else + begin + ReallocMem(pt0, ptsz + delta); + pt1 := pointer(cardinal(pt0) + ptsz); + end; + ptsz := ptsz + delta; + pt2 := pointer(cardinal(pt1) + delta); + end; + + procedure mem_ch(ch: char); + begin + if pt1 >= pt2 then get_more_memory; + pt1^ := ch; + inc(pt1); + end; + + procedure mem_write(rs: string); + var + i: Integer; + begin + for i := 1 to length(rs) do + begin + if pt1 >= pt2 then get_more_memory; + pt1^ := rs[i]; + inc(pt1); + end; + end; + + procedure gn_base(obj: TlkJSONbase); + var + ws: string; + i, j: Integer; + xs: TlkJSONstring; + begin + if not assigned(obj) then exit; + if obj is TlkJSONnumber then + begin +{$IFDEF HAVE_FORMATSETTING} + mem_write(FloatToStr(TlkJSONnumber(obj).FValue, fs)); +{$ELSE} + ws := FloatToStr(TlkJSONnumber(obj).FValue); + i := pos(DecimalSeparator, ws); + if (DecimalSeparator <> '.') and (i > 0) then ws[i] := '.'; + mem_write(ws); +{$ENDIF} + end + else if obj is TlkJSONstring then + begin + ws := UTF8Encode(TlkJSONstring(obj).FValue); + i := 1; + mem_ch('"'); + while i <= length(ws) do + begin + case ws[i] of + '/', '\', '"': + begin + mem_ch('\'); + mem_ch(ws[i]); + end; + #8: mem_write('\b'); + #9: mem_write('\t'); + #10: mem_write('\n'); + #13: mem_write('\r'); + #12: mem_write('\f'); + else + if ord(ws[i]) < 32 then + mem_write('\u' + inttohex(ord(ws[i]), 4)) + else + mem_ch(ws[i]); + end; + inc(i); + end; + mem_ch('"'); + end + else if obj is TlkJSONboolean then + begin + if TlkJSONboolean(obj).FValue then + mem_write('true') + else + mem_write('false'); + end + else if obj is TlkJSONnull then + begin + mem_write('null'); + end + else if obj is TlkJSONlist then + begin + mem_ch('['); + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then mem_ch(','); + gn_base(TlkJSONlist(obj).Child[i]); + end; + mem_ch(']'); + end + else if obj is TlkJSONobjectmethod then + begin + try + xs := TlkJSONstring.Create; + xs.FValue := TlkJSONobjectmethod(obj).FName; + gn_base(TlkJSONbase(xs)); + mem_ch(':'); + gn_base(TlkJSONbase(TlkJSONobjectmethod(obj).FValue)); + finally + if assigned(xs) then FreeAndNil(xs); + end; + end + else if obj is TlkJSONobject then + begin + mem_ch('{'); + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then mem_ch(','); + gn_base(TlkJSONobject(obj).Child[i]); + end; + mem_ch('}'); + end; + end; +{$ENDIF NEW_STYLE_GENERATE} + +begin +{$IFDEF HAVE_FORMATSETTING} + GetLocaleFormatSettings(GetThreadLocale, fs); + fs.DecimalSeparator := '.'; +{$ENDIF} +{$IFDEF NEW_STYLE_GENERATE} + pt0 := nil; + get_more_memory; + gn_base(obj); + mem_ch(#0); + result := string(pt0); + freemem(pt0); +{$ELSE} + result := gn_base(obj); +{$ENDIF} +end; + +class function TlkJSON.ParseText(const txt: string): TlkJSONbase; +{$IFDEF HAVE_FORMATSETTING} +var + fs: TFormatSettings; +{$ENDIF} + + function js_base(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; forward; + + function xe(idx: Integer): Boolean; + {$IFDEF FPC}inline; + {$ENDIF} + begin + result := idx <= length(txt); + end; + + procedure skip_spc(var idx: Integer); + {$IFDEF FPC}inline; + {$ENDIF} + begin + while (xe(idx)) and (ord(txt[idx]) < 33) do + inc(idx); + end; + + procedure add_child(var o, c: TlkJSONbase); + var + i: Integer; + begin + if o = nil then + begin + o := c; + end + else + begin + if o is TlkJSONobjectmethod then + begin + TlkJSONobjectmethod(o).FValue := c; + end + else if o is TlkJSONlist then + begin + TlkJSONlist(o)._Add(c); + end + else if o is TlkJSONobject then + begin + i := TlkJSONobject(o)._Add(c); + if TlkJSONobject(o).UseHash then +{$IFDEF USE_HASH} + TlkJSONobject(o).ht.AddPair(TlkJSONobjectmethod(c).Name, i); +{$ELSE} + TlkJSONobject(o).ht.Insert(TlkJSONobjectmethod(c).Name, i); +{$ENDIF USE_HASH} + end; + end; + end; + + function js_boolean(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONboolean; + begin + skip_spc(idx); + if copy(txt, idx, 4) = 'true' then + begin + result := true; + ridx := idx + 4; + js := TlkJSONboolean.Create; + js.FValue := true; + add_child(o, TlkJSONbase(js)); + end + else if copy(txt, idx, 5) = 'false' then + begin + result := true; + ridx := idx + 5; + js := TlkJSONboolean.Create; + js.FValue := false; + add_child(o, TlkJSONbase(js)); + end + else + begin + result := false; + end; + end; + + function js_null(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONnull; + begin + skip_spc(idx); + if copy(txt, idx, 4) = 'null' then + begin + result := true; + ridx := idx + 4; + js := TlkJSONnull.Create; + add_child(o, TlkJSONbase(js)); + end + else + begin + result := false; + end; + end; + + function js_integer(idx: Integer; var ridx: Integer): Boolean; + begin + result := false; + while (xe(idx)) and (txt[idx] in ['0'..'9']) do + begin + result := true; + inc(idx); + end; + if result then ridx := idx; + end; + + function js_number(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONnumber; + ws: string; + {$IFNDEF HAVE_FORMATSETTING} + i: Integer; + {$ENDIF} + begin + skip_spc(idx); + result := xe(idx); + if not result then exit; + if txt[idx] in ['+', '-'] then + begin + inc(idx); + result := xe(idx); + end; + if not result then exit; + result := js_integer(idx, idx); + if not result then exit; + if (xe(idx)) and (txt[idx] = '.') then + begin + inc(idx); + result := js_integer(idx, idx); + if not result then exit; + end; + if (xe(idx)) and (txt[idx] in ['e', 'E']) then + begin + inc(idx); + if (xe(idx)) and (txt[idx] in ['+', '-']) then inc(idx); + result := js_integer(idx, idx); + if not result then exit; + end; + if not result then exit; + js := TlkJSONnumber.Create; + ws := copy(txt, ridx, idx - ridx); +{$IFDEF HAVE_FORMATSETTING} + js.FValue := StrToFloat(ws, fs); +{$ELSE} + i := pos('.', ws); + if (DecimalSeparator <> '.') and (i > 0) then + ws[pos('.', ws)] := DecimalSeparator; + js.FValue := StrToFloat(ws); +{$ENDIF} + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + +{ + +} + function js_string(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + + function strSpecialChars(const s: string): string; + var + i, j : integer; + begin + i := Pos('\', s); + if (i = 0) then + Result := s + else + begin + Result := Copy(s, 1, i-1); + j := i; + repeat + if (s[j] = '\') then + begin + inc(j); + case s[j] of + '\': Result := Result + '\'; + '"': Result := Result + '"'; + '''': Result := Result + ''''; + '/': Result := Result + '/'; + 'b': Result := Result + #8; + 'f': Result := Result + #12; + 'n': Result := Result + #10; + 'r': Result := Result + #13; + 't': Result := Result + #9; + 'u': + begin + Result := Result + code2utf(strtoint('$' + copy(s, j + 1, 4))); + inc(j, 4); + end; + end; + end + else + Result := Result + s[j]; + inc(j); + until j > length(s); + end; + end; + + var + js: TlkJSONstring; + fin: Boolean; + ws: String; + i,j,widx: Integer; + begin + skip_spc(idx); + + result := xe(idx) and (txt[idx] = '"'); + if not result then exit; + + inc(idx); + widx := idx; + + fin:=false; + REPEAT + i := 0; + j := 0; + while (widx<=length(txt)) and (j=0) do + begin + if (i=0) and (txt[widx]='\') then i:=widx; + if (j=0) and (txt[widx]='"') then j:=widx; + inc(widx); + end; +// incorrect string!!! + if j=0 then + begin + result := false; + exit; + end; +// if we have no slashed chars in string + if (i=0) or (j0 and j>=i - skip slashed char + else + begin + widx:=i+2; + end; + UNTIL fin; + + ws := strSpecialChars(ws); + inc(idx); + + js := TlkJSONstring.Create; +{$ifdef USE_D2009} + js.FValue := UTF8ToString(ws); +{$else} + js.FValue := UTF8Decode(ws); +{$endif} + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + + function js_list(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONlist; + begin + result := false; + try + js := TlkJSONlist.Create; + skip_spc(idx); + result := xe(idx); + if not result then exit; + result := txt[idx] = '['; + if not result then exit; + inc(idx); + while js_base(idx, idx, TlkJSONbase(js)) do + begin + skip_spc(idx); + if (xe(idx)) and (txt[idx] = ',') then inc(idx); + end; + skip_spc(idx); + result := (xe(idx)) and (txt[idx] = ']'); + if not result then exit; + inc(idx); + finally + if not result then + begin + js.Free; + end + else + begin + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + end; + end; + + function js_method(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + mth: TlkJSONobjectmethod; + ws: TlkJSONstring; + begin + result := false; + try + ws := nil; + mth := TlkJSONobjectmethod.Create; + skip_spc(idx); + result := xe(idx); + if not result then exit; + result := js_string(idx, idx, TlkJSONbase(ws)); + if not result then exit; + skip_spc(idx); + result := xe(idx) and (txt[idx] = ':'); + if not result then exit; + inc(idx); + mth.FName := ws.FValue; + result := js_base(idx, idx, TlkJSONbase(mth)); + finally + if ws <> nil then ws.Free; + if result then + begin + add_child(o, TlkJSONbase(mth)); + ridx := idx; + end + else + begin + mth.Free; + end; + end; + end; + + function js_object(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONobject; + begin + result := false; + try + js := TlkJSONobject.Create; + skip_spc(idx); + result := xe(idx); + if not result then exit; + result := txt[idx] = '{'; + if not result then exit; + inc(idx); + while js_method(idx, idx, TlkJSONbase(js)) do + begin + skip_spc(idx); + if (xe(idx)) and (txt[idx] = ',') then inc(idx); + end; + skip_spc(idx); + result := (xe(idx)) and (txt[idx] = '}'); + if not result then exit; + inc(idx); + finally + if not result then + begin + js.Free; + end + else + begin + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + end; + end; + + function js_base(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + begin + skip_spc(idx); + result := js_boolean(idx, idx, o); + if not result then result := js_null(idx, idx, o); + if not result then result := js_number(idx, idx, o); + if not result then result := js_string(idx, idx, o); + if not result then result := js_list(idx, idx, o); + if not result then result := js_object(idx, idx, o); + if result then ridx := idx; + end; + +var + idx: Integer; +begin +{$IFDEF HAVE_FORMATSETTING} + GetLocaleFormatSettings(GetThreadLocale, fs); + fs.DecimalSeparator := '.'; +{$ENDIF} + + result := nil; + if txt = '' then exit; + try + idx := 1; + // skip a BOM utf8 marker + if copy(txt,idx,3)=#239#187#191 then + begin + inc(idx,3); + // if there are only a BOM - exit; + if idx>length(txt) then exit; + end; + if not js_base(idx, idx, result) then FreeAndNil(result); + except + if assigned(result) then FreeAndNil(result); + end; +end; + +{ ElkIntException } + +constructor ElkIntException.Create(idx: Integer; msg: string); +begin + self.idx := idx; + inherited Create(msg); +end; + +{ TlkHashTable } + +{$IFDEF USE_HASH} +procedure TlkHashTable.AddPair(const ws: WideString; idx: Integer); +var + i, j, k: cardinal; + p: PlkHashItem; + find: boolean; +begin + find := false; + if InTable(ws, i, j, k) then + begin +// if string is already in table, changing index + if TlkJSONobject(FParent).GetNameOf(PlkHashItem(a_x[j].Items[k])^.index) = ws then + begin + PlkHashItem(a_x[j].Items[k])^.index := idx; + find := true; + end; + end; + if find = false then + begin + GetMem(p,sizeof(TlkHashItem)); + k := a_x[j].Add(p); + p^.hash := i; + p^.index := idx; + while (k>0) and (PlkHashItem(a_x[j].Items[k])^.hash < PlkHashItem(a_x[j].Items[k-1])^.hash) do + begin + a_x[j].Exchange(k,k-1); + dec(k); + end; + end; +end; + +function TlkHashTable.counters: string; +var + i, j: Integer; + ws: string; +begin + ws := ''; + for i := 0 to 15 do + begin + for j := 0 to 15 do +// ws := ws + format('%.3d ', [length(a_h[i * 16 + j])]); + ws := ws + format('%.3d ', [a_x[i * 16 + j].Count]); + ws := ws + #13#10; + end; + result := ws; +end; + +procedure TlkHashTable.Delete(const ws: WideString); +var + i, j, k: cardinal; +begin + if InTable(ws, i, j, k) then + begin +// while k < high(a_h[j]) do +// begin +// hswap(j, k, k + 1); +// inc(k); +// end; +// SetLength(a_h[j], k); + FreeMem(a_x[j].Items[k]); + a_x[j].Delete(k); + end; +end; + +{$IFDEF THREADSAFE} +const + rnd_table: array[0..255] of byte = + (216, 191, 234, 201, 12, 163, 190, 205, 128, 199, 210, 17, 52, 43, + 38, 149, 40, 207, 186, 89, 92, 179, 142, 93, 208, 215, 162, + 161, 132, 59, 246, 37, 120, 223, 138, 233, 172, 195, 94, 237, 32, + 231, 114, 49, 212, 75, 198, 181, 200, 239, 90, 121, 252, 211, + 46, 125, 112, 247, 66, 193, 36, 91, 150, 69, 24, 255, 42, 9, 76, + 227, 254, 13, 192, 7, 18, 81, 116, 107, 102, 213, 104, 15, 250, + 153, 156, 243, 206, 157, 16, 23, 226, 225, 196, 123, 54, 101, + 184, 31, 202, 41, 236, 3, 158, 45, 96, 39, 178, 113, 20, 139, 6, + 245, 8, 47, 154, 185, 60, 19, 110, 189, 176, 55, 130, 1, 100, + 155, 214, 133, 88, 63, 106, 73, 140, 35, 62, 77, 0, 71, 82, 145, + 180, + 171, 166, 21, 168, 79, 58, 217, 220, 51, 14, 221, 80, 87, 34, 33, + 4, 187, 118, 165, 248, 95, 10, 105, 44, 67, 222, 109, 160, 103, + 242, 177, 84, 203, 70, 53, 72, 111, 218, 249, 124, 83, 174, 253, + 240, 119, 194, 65, 164, 219, 22, 197, 152, 127, 170, 137, 204, + 99, 126, 141, 64, 135, 146, 209, 244, 235, 230, 85, 232, 143, + 122, 25, 28, 115, 78, 29, 144, 151, 98, 97, 68, 251, 182, 229, + 56, + 159, 74, 169, 108, 131, 30, 173, 224, 167, 50, 241, 148, 11, 134, + 117, 136, 175, 26, 57, 188, 147, 238, 61, 48, 183, 2, 129, + 228, 27, 86, 5); +{$ELSE} +var + rnd_table: array[0..255] of byte; +{$ENDIF} + +function TlkHashTable.DefaultHashOf(const ws: WideString): cardinal; +{$IFDEF DOTNET} +var + i, j: Integer; + x1, x2, x3, x4: byte; +begin + result := 0; +// result := 0; + x1 := 0; + x2 := 1; + for i := 1 to length(ws) do + begin + j := ord(ws[i]); +// first version of hashing + x1 := (x1 + j) {and $FF}; + x2 := (x2 + 1 + (j shr 8)) {and $FF}; + x3 := rnd_table[x1]; + x4 := rnd_table[x3]; + result := ((x1 * x4) + (x2 * x3)) xor result; + end; +end; +{$ELSE} +var + x1, x2, x3, x4: byte; + p: PWideChar; +begin + result := 0; + x1 := 0; + x2 := 1; + p := PWideChar(ws); + while p^ <> #0 do + begin + inc(x1, ord(p^)) {and $FF}; + inc(x2, 1 + (ord(p^) shr 8)) {and $FF}; + x3 := rnd_table[x1]; + x4 := rnd_table[x3]; + result := ((x1 * x4) + (x2 * x3)) xor result; + inc(p); + end; +end; +{$ENDIF} + +procedure TlkHashTable.hswap(j, k, l: Integer); +//var +// h: TlkHashItem; +begin +// h := a_h[j, k]; +// a_h[j, k] := a_h[j, l]; +// a_h[j, l] := h; + a_x[j].Exchange(k, l); +end; + +function TlkHashTable.IndexOf(const ws: WideString): Integer; +var + i, j, k: Cardinal; +begin + if not InTable(ws, i, j, k) then + begin + result := -1; + end + else + begin +// result := a_h[j, k].index; + result := PlkHashItem(a_x[j].Items[k])^.index; + end; +end; + +function TlkHashTable.InTable(const ws: WideString; var i, j, k: + cardinal): + Boolean; +var + l, wu, wl: Integer; + x: Cardinal; + fin: Boolean; +begin + i := HashOf(ws); + j := i and $FF; + result := false; +{using "binary" search always, because array is sorted} + if a_x[j].Count-1 >= 0 then + begin + wl := 0; + wu := a_x[j].Count-1; + repeat + fin := true; + if PlkHashItem(a_x[j].Items[wl])^.hash = i then + begin + k := wl; + result := true; + end + else if PlkHashItem(a_x[j].Items[wu])^.hash = i then + begin + k := wu; + result := true; + end + else if (wu - wl) > 1 then + begin + fin := false; + x := (wl + wu) shr 1; + if PlkHashItem(a_x[j].Items[x])^.hash > i then + begin + wu := x; + end + else + begin + wl := x; + end; + end; + until fin; + end; + +// verify k index in chain + if result = true then + begin + while (k > 0) and (PlkHashItem(a_x[j].Items[k])^.hash = PlkHashItem(a_x[j].Items[k-1])^.hash) do dec(k); + repeat + fin := true; + if TlkJSONobject(FParent).GetNameOf(PlkHashItem(a_x[j].Items[k])^.index) <> ws then + begin + if k < a_x[j].Count-1 then + begin + inc(k); + fin := false; + end + else + begin + result := false; + end; + end + else + begin + result := true; + end; + until fin; + end; +end; + +{$IFNDEF THREADSAFE} + +procedure init_rnd; +var + x0: Integer; + i: Integer; +begin + x0 := 5; + for i := 0 to 255 do + begin + x0 := (x0 * 29 + 71) and $FF; + rnd_table[i] := x0; + end; +end; +{$ENDIF} + +procedure TlkHashTable.SetHashFunction(const AValue: + TlkHashFunction); +begin + FHashFunction := AValue; +end; + +constructor TlkHashTable.Create; +var + i: Integer; +begin + inherited; +// for i := 0 to 255 do SetLength(a_h[i], 0); + for i := 0 to 255 do a_x[i] := TList.Create; + HashOf := {$IFDEF FPC}@{$ENDIF}DefaultHashOf; +end; + +destructor TlkHashTable.Destroy; +var + i, j: Integer; +begin +// for i := 0 to 255 do SetLength(a_h[i], 0); + for i := 0 to 255 do + begin + for j := 0 to a_x[i].Count - 1 do Freemem(a_x[i].Items[j]); + a_x[i].Free; + end; + inherited; +end; + +function TlkHashTable.SimpleHashOf(const ws: WideString): cardinal; +var + i: Integer; +begin + result := length(ws); + for i := 1 to length(ws) do result := result + ord(ws[i]); +end; +{$ENDIF USE_HASH} + +{ TlkJSONstreamed } +{$IFNDEF KOL} + +class function TlkJSONstreamed.LoadFromFile(srcname: string): + TlkJSONbase; +var + fs: TFileStream; +begin + result := nil; + if not FileExists(srcname) then exit; + try + fs := TFileStream.Create(srcname, fmOpenRead); + result := LoadFromStream(fs); + finally + if Assigned(fs) then FreeAndNil(fs); + end; +end; + +class function TlkJSONstreamed.LoadFromStream(src: TStream): + TlkJSONbase; +var + ws: string; + len: int64; +begin + result := nil; + if not assigned(src) then exit; + len := src.Size - src.Position; + SetLength(ws, len); + src.Read(pchar(ws)^, len); + result := ParseText(ws); +end; + +class procedure TlkJSONstreamed.SaveToFile(obj: TlkJSONbase; + dstname: string); +var + fs: TFileStream; +begin + if not assigned(obj) then exit; + try + fs := TFileStream.Create(dstname, fmCreate); + SaveToStream(obj, fs); + finally + if Assigned(fs) then FreeAndNil(fs); + end; +end; + +class procedure TlkJSONstreamed.SaveToStream(obj: TlkJSONbase; + dst: TStream); +var + ws: string; +begin + if not assigned(obj) then exit; + if not assigned(dst) then exit; + ws := GenerateText(obj); + dst.Write(pchar(ws)^, length(ws)); +end; + +{$ENDIF} + +{ TlkJSONdotnetclass } + +{$IFDEF DOTNET} + +procedure TlkJSONdotnetclass.AfterConstruction; +begin + +end; + +procedure TlkJSONdotnetclass.BeforeDestruction; +begin + +end; + +constructor TlkJSONdotnetclass.Create; +begin + inherited; + AfterConstruction; +end; + +destructor TlkJSONdotnetclass.Destroy; +begin + BeforeDestruction; + inherited; +end; +{$ENDIF DOTNET} + +{ TlkBalTree } + +{$IFNDEF USE_HASH} +procedure TlkBalTree.Clear; + + procedure rec(t: PlkBalNode); + begin + if t.left<>fbottom then rec(t.left); + if t.right<>fbottom then rec(t.right); + t.nm := ''; + dispose(t); + end; + +begin + if froot<>fbottom then rec(froot); + froot := fbottom; + fdeleted := fbottom; +end; + +function TlkBalTree.counters: string; +begin + result := format('Balanced tree root node level is %d',[froot.level]); +end; + +constructor TlkBalTree.Create; +begin + inherited Create; + new(fbottom); + fbottom.left := fbottom; + fbottom.right := fbottom; + fbottom.level := 0; + fdeleted := fbottom; + froot := fbottom; +end; + +function TlkBalTree.Delete(const ws: WideString): Boolean; + + procedure UpdateKeys(t: PlkBalNode; idx: integer); + begin + if t <> fbottom then begin + if t.key > idx then + t.key := t.key - 1; + UpdateKeys(t.left, idx); + UpdateKeys(t.right, idx); + end; + end; + + function del(var t: PlkBalNode): Boolean; + begin + result := false; + if t<>fbottom then begin + flast := t; + if ws fbottom) and (ws = fdeleted.nm) then begin + UpdateKeys(froot, fdeleted.key); + fdeleted.key := t.key; + fdeleted.nm := t.nm; + t := t.right; + flast.nm := ''; + dispose(flast); + result := true; + end + else if (t.left.level < (t.level - 1)) or (t.right.level < (t.level - 1)) then begin + t.level := t.level - 1; + if t.right.level > t.level then + t.right.level := t.level; + skew(t); + skew(t.right); + skew(t.right.right); + split(t); + split(t.right); + end; + end; + end; + +{ +// mine version, buggy, see tracker message +// [ 2229135 ] Value deletion is broken by "Nobody/Anonymous - nobody" + + function del(var t: PlkBalNode): Boolean; + begin + result := false; + if t<>fbottom then + begin + flast := t; + if wsfbottom) and (ws = t.nm) then + begin + fdeleted.key := t.key; + fdeleted.nm := t.nm; + t := t.right; + flast.nm := ''; + dispose(flast); + result := true; + end + else if (t.left.level<(t.level-1)) or (t.right.level<(t.level-1)) then + begin + t.level := t.level-1; + if t.right.level>t.level then t.right.level := t.level; + skew(t); + skew(t.right); + skew(t.right.right); + split(t); + split(t.right); + end; + end; + end; +} + +begin + result := del(froot); +end; + +destructor TlkBalTree.Destroy; +begin + Clear; + dispose(fbottom); + inherited; +end; + +function TlkBalTree.IndexOf(const ws: WideString): Integer; +var + tk: PlkBalNode; +begin + result := -1; + tk := froot; + while (result=-1) and (tk<>fbottom) do + begin + if tk.nm = ws then result := tk.key + else if ws t.nm then + result := ins(t.right) + else result := false; + skew(t); + split(t); + end; + end; + +begin + result := ins(froot); +end; + +procedure TlkBalTree.skew(var t: PlkBalNode); +var + temp: PlkBalNode; +begin + if t.left.level = t.level then + begin + temp := t; + t := t.left; + temp.left := t.right; + t.right := temp; + end; +end; + +procedure TlkBalTree.split(var t: PlkBalNode); +var + temp: PlkBalNode; +begin + if t.right.right.level = t.level then + begin + temp := t; + t := t.right; + temp.right := t.left; + t.left := temp; + t.level := t.level+1; + end; +end; +{$ENDIF USE_HASH} + +initialization +{$IFNDEF THREADSAFE} +{$IFDEF USE_HASH} + init_rnd; +{$ENDIF USE_HASH} +{$ENDIF THREADSAFE} +end. + diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dpr" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dpr" new file mode 100644 index 0000000..6388703 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dpr" @@ -0,0 +1,16 @@ +program JSON_Test_D2007; + +uses + Forms, + uLkJSON in 'uLkJSON.pas', + main in 'main.pas' {Form1}; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dproj" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dproj" new file mode 100644 index 0000000..01486c9 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.dproj" @@ -0,0 +1,146 @@ + + + {b8d02dd2-c5bc-47d3-b6b8-d58a9a2f0956} + JSONTest.dpr + Debug + AnyCPU + DCC32 + ..\..\..\bin\JSONTest.exe + + + 7.0 + False + False + 0 + RELEASE + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + + + 7.0 + DEBUG + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + + + Delphi.Personality + VCLApplication + + + False + True + False + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + JSONTest.dpr + + + + + + + MainSource + + +

Form1
+ + + + \ No newline at end of file diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.res" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.res" new file mode 100644 index 0000000000000000000000000000000000000000..b3baee1a2734fd54948c5a3b4773af3e0d0378da GIT binary patch literal 96112 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%0D`}-x#Ef!JGE}DDK*DWak1CbnlyS; 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 E|DQoSApigX literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_Icon2.ico" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_Icon2.ico" new file mode 100644 index 0000000000000000000000000000000000000000..9917d72b6fc189e3dd24cd85ce07c7d8c59f1642 GIT binary patch literal 94198 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 S9KcU3_)-!mPvG?N`Tie;#=;2z literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dpr" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dpr" new file mode 100644 index 0000000..bf68606 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dpr" @@ -0,0 +1,18 @@ +program JSONTest; + +uses + Forms, + Unit2 in 'Unit2.pas' {Form2}, + uJSON in 'uJSON.pas', + uLkJSON in 'uLkJSON.pas', + YxdJson in 'YxdJson.pas'; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm2, Form2); + Application.Run; +end. diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dproj" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dproj" new file mode 100644 index 0000000..e25dcb3 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007.dproj" @@ -0,0 +1,101 @@ + + + {B3E2FA67-2D5C-40C1-A563-16FA8B349EFC} + JSONTest_d2007.dpr + True + Debug + 1 + Application + VCL + 15.4 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + false + JSONTest_d2007 + false + false + false + 00400000 + Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + 2052 + false + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + + + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + true + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + $(BDS)\bin\default_app.manifest + + + RELEASE;$(DCC_Define) + 0 + 0 + false + + + DEBUG;$(DCC_Define) + true + false + + + + MainSource + + +
Form2
+
+ + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + JSONTest_d2007.dpr + + + + True + False + + + 12 + + + +
diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007_Icon.ico" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest_d2007_Icon.ico" new file mode 100644 index 0000000000000000000000000000000000000000..2bc1f2cefb4d929e2a4afe85fddcc7c91e392723 GIT binary patch literal 94198 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 + + + {2778da2e-1090-4b05-b789-1366e08ab8a3} + Release + AnyCPU + DCC32 + ..\..\..\bin\JSONTest.exe + JSONTest.dpr + + + 7.0 + False + False + 0 + RELEASE + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + + + 7.0 + DEBUG + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + + + Delphi.Personality + + +FalseTrueFalseTrueFalse1000FalseFalseFalseFalseFalse103312521.0.0.01.0.0.0JSONTest.dpr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + + MainSource + + +
Form1
+
+ + +
+
\ No newline at end of file diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_D2007.res" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_D2007.res" new file mode 100644 index 0000000000000000000000000000000000000000..b3baee1a2734fd54948c5a3b4773af3e0d0378da GIT binary patch literal 96112 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%0D`}-x#Ef!JGE}DDK*DWak1CbnlyS; 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 E|DQoSApigX literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dpr" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dpr" new file mode 100644 index 0000000..9c61ee9 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dpr" @@ -0,0 +1,16 @@ +program JSON_Test_XE; + +uses + Forms, + uLkJSON in 'uLkJSON.pas', + main in 'main.pas' {Form1}; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dproj" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dproj" new file mode 100644 index 0000000..f10a728 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.dproj" @@ -0,0 +1,183 @@ + + + {2778da2e-1090-4b05-b789-1366e08ab8a3} + Debug + DCC32 + bin\JSONTest.exe + JSON_Test_XE.dpr + True + Release + 3 + Application + VCL + 15.4 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + JSON_Test_XE + Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + 2052 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + $(BDS)\bin\default_app.manifest + true + JSONTest_Icon2.ico + 1033 + + + 1033 + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + $(BDS)\bin\default_app.manifest + JSONTest_Icon2.ico + + + 7.0 + 0 + False + 0 + RELEASE;$(DCC_Define) + + + ..\..\..\bin + ..\..\..\bin + true + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + 1033 + ..\..\..\dcu + + + 7.0 + DEBUG;$(DCC_Define) + .\bin + .\dcu + .\dcu + .\dcu + .\bin + .\dcu + + + ..\..\..\dcu + ..\..\..\dcu + true + ..\..\..\bin + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + 1033 + ..\..\..\bin + + + Delphi.Personality.12 + + + + + False + True + False + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 2052 + 936 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + JSON_Test_XE.dpr + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + + + 12 + + + + + MainSource + + + +
Form1
+
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + +
diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.res" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSON_Test_XE.res" new file mode 100644 index 0000000000000000000000000000000000000000..b60aad9b3fa46d9529921c15f67a5577121f65d9 GIT binary patch literal 95832 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%0D2)Y{S(;+*aDWv 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

tQeNf>rv1gxQS8^hyo@lO#`gOuc_}bL{i0t# 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 McEZzy|C@gP5Bt_HWdHyG literal 0 HcmV?d00001 diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.dfm" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.dfm" new file mode 100644 index 0000000..e9b02f9 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.dfm" @@ -0,0 +1,92 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'QJson Compare' + ClientHeight = 425 + ClientWidth = 772 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object Panel1: TPanel + Left = 0 + Top = 0 + Width = 772 + Height = 49 + Align = alTop + BevelOuter = bvNone + TabOrder = 0 + object Button10: TButton + Left = 672 + Top = 12 + Width = 75 + Height = 25 + Caption = #24320#22987'(&S)' + TabOrder = 0 + OnClick = Button10Click + end + object chkCreate: TCheckBox + Left = 16 + Top = 16 + Width = 97 + Height = 17 + Caption = #21019#24314#32467#28857 + Checked = True + State = cbChecked + TabOrder = 1 + end + object chkLoad: TCheckBox + Left = 123 + Top = 16 + Width = 97 + Height = 17 + Caption = #21152#36733#25991#20214 + Checked = True + State = cbChecked + TabOrder = 2 + end + object chkSave: TCheckBox + Left = 231 + Top = 16 + Width = 97 + Height = 17 + Caption = #20445#23384#25991#20214 + Checked = True + State = cbChecked + TabOrder = 3 + end + object chkTypes: TCheckBox + Left = 339 + Top = 16 + Width = 129 + Height = 17 + Caption = #35299#26512#19981#21516#31867#22411#25968#25454 + Checked = True + State = cbChecked + TabOrder = 4 + end + end + object mmResult: TMemo + Left = 0 + Top = 49 + Width = 772 + Height = 376 + Align = alClient + ImeName = #20013#25991' - QQ'#20116#31508#36755#20837#27861 + TabOrder = 1 + end + object OpenDialog1: TOpenDialog + Left = 48 + Top = 104 + end + object SaveDialog1: TSaveDialog + Left = 16 + Top = 96 + end +end diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.pas" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.pas" new file mode 100644 index 0000000..50138ad --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/main.pas" @@ -0,0 +1,739 @@ +unit main; + +interface + +uses + Windows, Messages, SysUtils, Variants, Classes, Graphics, + Controls, Forms, Dialogs, StdCtrls, ExtCtrls, qstring, qjson; + +type + TForm1 = class(TForm) + Panel1: TPanel; + mmResult: TMemo; + OpenDialog1: TOpenDialog; + SaveDialog1: TSaveDialog; + Button10: TButton; + chkCreate: TCheckBox; + chkLoad: TCheckBox; + chkSave: TCheckBox; + chkTypes: TCheckBox; + procedure Button1Click(Sender: TObject); + procedure Button3Click(Sender: TObject); + procedure Button4Click(Sender: TObject); + procedure Button5Click(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure Button7Click(Sender: TObject); + procedure Button8Click(Sender: TObject); + procedure Button10Click(Sender: TObject); + private + { Private declarations } + procedure CreateTest; + procedure LoadTest; + procedure SaveTest; + procedure IOTest; + procedure TypeTest; + public + { Public declarations } + end; + +var + Form1: TForm1; + +implementation +{$R *.dfm} +uses uLkJSON,superobject,yxdjson; + +function GetFileSize(AFileName:String):Int64; +var + sr:TSearchRec; + AHandle:Integer; +begin +AHandle:=FindFirst(AFileName,faAnyFile,sr); +if AHandle=0 then + begin + Result:=sr.Size; + FindClose(sr); + end +else + Result:=0; +end; + +procedure TForm1.Button10Click(Sender: TObject); +var + T:Cardinal; +begin +Button10.Enabled:=False; +T:=GetTickCount; +mmResult.Lines.Add('Կʼ'); +Caption:='Դ10ٶ...'; +Update; +if chkCreate.Checked then + CreateTest; +Caption:='Լٶ...'; +Update; +if chkLoad.Checked then + LoadTest; +Caption:='Աٶ...'; +Update; +if chkSave.Checked then + SaveTest; +Caption:='Բͬͽٶ...'; +Update; +if chkTypes.Checked then + TypeTest; +Caption:='JSON Compare'; +mmResult.Lines.Add('Խʱ'+RollupTime((GetTickCount-T) div 1000)); +Button10.Enabled:=True; +end; + +procedure TForm1.Button1Click(Sender: TObject); +var + AJson:TQJson; + I:Integer; + T:Cardinal; +begin +AJson:=TQJson.Create; +try + T:=GetTickCount; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),Now); + T:=GetTickCount-T; + mmResult.Clear; + mmResult.Lines.Add('100,000ʱ:'+IntToStr(T)+'ms'); +finally + AJson.Free; +end; +end; + +procedure TForm1.Button3Click(Sender: TObject); +var + AJson:TQJson; + T:Cardinal; + Speed:Cardinal; + lkJson:TlkJSONbase; + +begin +if OpenDialog1.Execute then + begin +// uJsonTest; + AJson:=TQJson.Create; + try + T:=GetTickCount; + AJson.LoadFromFile(OpenDialog1.FileName,QString.teUtf8); + T:=GetTickCount-T; + if T>0 then + Speed:=(GetFileSize(OpenDialog1.FileName)*1000 div T) + else + Speed:=0; + mmResult.Clear; +// mmResult.Lines.Add('صJSONļݣ'); +// mmResult.Lines.Add(AJson.Encode(True)); + mmResult.Lines.Add('QJsonʱ:'+IntToStr(T)+'msٶ:'+RollupSize(Speed)); + T:=GetTickCount; + lkJson:=TlkJSONstreamed.LoadFromFile(OpenDialog1.FileName); + T:=GetTickCount-T; + if T>0 then + Speed:=(GetFileSize(OpenDialog1.FileName)*1000 div T) + else + Speed:=0; + mmResult.Lines.Add('lkJsonʱ:'+IntToStr(T)+'msٶ:'+RollupSize(Speed)); + lkJson.Free; + finally + AJson.Free; + end; + end; +end; + +procedure TForm1.Button4Click(Sender: TObject); +var + AJson:TQJson; + I:Integer; + T1,T2:Cardinal; + Speed:Cardinal; +begin +if SaveDialog1.Execute then + begin + AJson:=TQJson.Create; + try + mmResult.Clear; + T1:=GetTickCount; + with AJson.Add('Integers',qjson.jdtObject) do + begin + for I := 0 to 1000000 do + Add('Node'+IntToStr(I)).AsInteger :=I; + end; + T1:=GetTickCount-T1; + T2:=GetTickCount; + AJson.SaveToFile(SaveDialog1.FileName,qstring.teAnsi,false); + T2:=GetTickCount-T2; + if T2>0 then + Speed:=(GetFileSize(SaveDialog1.FileName)*1000 div T2) + else + Speed:=0; + mmResult.Lines.Add('10ʱ'+IntToStr(T1)+'ms,ʱ:'+IntToStr(T2)+'msٶȣ'+RollupSize(Speed)); + finally + AJson.Free; + end; + end; +end; + +procedure TForm1.Button5Click(Sender: TObject); +var + AJson:TQJson; +begin +AJson:=TQJson.Create; +try + AJson.Parse('{"results":[],"status":102,"msg":"IP\/SN\/SCODE\/REFERER Illegal:"}'); +// '{"name":"object_0","Id":1}'); + ShowMessage(AJson.Encode(True)); +finally + AJson.Free; +end; +end; + +procedure TForm1.Button7Click(Sender: TObject); +var + AStream:TMemoryStream; + AJson:TQJson; + S:QStringW; + AEncode:qstring.TTextEncoding; +begin +AStream:=TMemoryStream.Create; +AJson:=TQJson.Create; +try + AJson.DataType:=qjson.jdtObject; + S:='{"record1":{"id":100,"name":"name1"}}'#13#10+ + '{"record2":{"id":200,"name":"name2"}}'#13#10+ + '{"record3":{"id":300,"name":"name3"}}'#13#10; + //UCS2 + mmResult.Lines.Add('Unicode 16 LE:'); + AEncode:=qstring.teUnicode16LE; + AStream.Size:=0; + SaveTextW(AStream,S,False); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add('һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); + //UTF-8 + mmResult.Lines.Add('UTF8:'); + AEncode:=qstring.teUtf8; + AStream.Size:=0; + SaveTextU(AStream,qstring.Utf8Encode(S),False); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); + //ANSI + mmResult.Lines.Add(#13#10'ANSI:'); + AEncode:=qstring.teAnsi; + AStream.Size:=0; + SaveTextA(AStream,qstring.AnsiEncode(S)); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add('һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); + //UCS2BE + mmResult.Lines.Add(#13#10'Unicode16BE:'); + AEncode:=qstring.teUnicode16BE; + AStream.Size:=0; + SaveTextWBE(AStream,S,False); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add('һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); +finally + AStream.Free; + AJson.Free; +end; +end; + +procedure TForm1.Button8Click(Sender: TObject); +var + AJson,AItem:TQJson; +begin +AJson:=TQJson.Create; +try + //ԪصNַʽʾ + // 1. ֱӵAddԪıķʽ + AJson.Add('AddArrayText','["Item1",100,null,true,false,123.4]',jdtArray);//jdtArrayʡԻԶԣȷ֪ͲҪжӿ + // 2. ֱ + AJson.Add('AddArray',['Item1',100,Null,True,False,123.4]); + // 3. ֱVarArrayOfֵ + AJson.Add('AsVariant').AsVariant:=VarArrayOf(['Item1',100,Null,True,False,123.4]); + // 4. ֱAsArrayļ + AJson.Add('AsArray').AsArray:='["Item1",100,null,true,false,123.4]'; + // 5. ֶԪ + with AJson.Add('Manul') do + begin + DataType:=jdtArray; + Add.AsString:='Item1'; + Add.AsInteger:=100; + Add; + Add.AsBoolean:=True; + Add.AsBoolean:=False; + Add.AsFloat:=123.4; + end; + // Ӷֻͣӽ㻻ǶͿ + AJson.Add('Object',[TQJson.Create.Add('Item1',100).Parent,TQJson.Create.Add('Item2',true).Parent]); + mmResult.Lines.Add(AJson.AsJson); +finally + FreeObject(AJson); +end; +end; + +procedure TForm1.CreateTest; +var + AJson:TQJson; + lkJson:TlkJSONobject; + lkItem:TlkJsonNumber; + SuperObj:ISuperObject; + YJson:JSONObject; + I:Integer; + T1,T2,T3,T4:Cardinal; +begin +AJson:=TQJson.Create; +SuperObj:=TSuperObject.Create; +lkJson:=TlkJSONObject.Create; +YJson:=JsonObject.Create; +try + mmResult.Lines.Add('100,000'); + T1:=GetTickCount; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),Now); + T1:=GetTickCount-T1; + T2:=GetTickCount; + for I := 0 to 100000 do + begin + lkJson.Add('_'+IntToStr(I),TlkJsonNumber.Generate(Now)); + end; + T2:=GetTickCount-T2; + T3:=GetTickCount; + for I := 0 to 100000 do + SuperObj.D['_'+IntToStr(I)]:=Now; + T3:=GetTickCount-T3; + T4:=GetTickCount; + for I := 0 to 100000 do + YJson.put('_'+IntToStr(I),Now); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' Խ:QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+',T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); +finally + AJson.Free; + lkJson.Free; + yJson.Free; +end; +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin +ReportMemoryLeaksOnShutdown:=True; +end; + +procedure TForm1.IOTest; +begin + +end; + +procedure TForm1.LoadTest; +var + I:Integer; + auk:TlkJSONbase; + AJson:TQJson; + SuperObj:ISuperObject; + YJson:JsonObject; + T1,T2,T3,T4:Cardinal; +const + AFileName:String='Preferences.txt'; + procedure PreCache; + var + AStream:TMemoryStream; + begin + AStream:=TMemoryStream.Create; + try + AStream.LoadFromFile(AFileName); + finally + AStream.Free; + end; + end; +begin +PreCache; +mmResult.Lines.Add('Լļٶ'); +AJson:=TQJson.Create; +T1:=GetTickCount; +for I := 0 to 10 do + AJson.LoadFromFile(AFileName); +T1:=GetTickCount-T1; +AJson.Free; +T2:=GetTickCount; +for I := 0 to 10 do + begin + auk:=TlkJSONstreamed.LoadFromFile(AFileName); + auk.Free; + end; +T2:=GetTickCount-T2; +T3:=GetTickCount; +for I := 0 to 10 do + SuperObj:=TSuperObject.ParseFile(AFileName,false); +T3:=GetTickCount-T3; +YJson:=JsonObject.Create; +T4:=GetTickCount; +for I := 0 to 10 do + YJson.LoadFromFile(AFileName); +T4:=GetTickCount-T4; +YJson.Free; +mmResult.Lines.Add(' Խ:QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); +end; + +procedure TForm1.SaveTest; +var + I:Integer; + auk:TlkJSONbase; + AJson:TQJson; + YJson:JsonObject; + SuperObj:ISuperObject; + T1,T2,T3,T4:Cardinal; +const + AFileName:String='Preferences.txt'; + ASaveFileName:String='saved.json'; + ACount:Integer=1; +begin +AJson:=TQJson.Create; +AJson.LoadFromFile(AFileName); +mmResult.Lines.Add('Աļٶ'); +T1:=GetTickCount; +for I := 0 to ACount do + AJson.SaveToFile(ASaveFileName,qstring.teUnicode16LE,true); +T1:=GetTickCount-T1; +AJson.Free; +auk:=TlkJSONstreamed.LoadFromFile(AFileName); +T2:=GetTickCount; +for I := 0 to ACount do + begin + TlkJSONStreamed.SaveToFile(auk,ASaveFileName); + end; +T2:=GetTickCount-T2; +auk.Free; +//SuperObj:=TSuperObject.ParseFile(AFileName,false); +T3:=GetTickCount; +//for I := 0 to ACount do +// SuperObj.SaveTo(ASaveFileName); +// SuperObj:=TSuperObject.ParseFile(AFileName,false); +T3:=GetTickCount-T3 + 10000; +YJson:=JsonObject.Create; +YJson.LoadFromFile(AFileName); +T4:=GetTickCount; +for I := 0 to ACount do + YJson.SaveToFile(ASaveFileName); +T4:=GetTickCount-T4; +YJson.Free; + +if T2 = 0 then T2 := 1; +mmResult.Lines.Add(' Խ:QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+ + FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); +end; + +procedure TForm1.TypeTest; +var + auk:TlkJSONbase; + AJson:TQJson; + SuperObj:ISuperObject; + YJson:JsonObject; + S:WideString; + AStream:TMemoryStream; + T1,T2,T3,T4:Cardinal; + Ansi:AnsiString; + procedure TestLongString; + var + I:Integer; + begin + SetLength(S,10*1024*1024); + for I := 0 to Length(S)-1 do + PWideChar(S)[I]:=WideChar(Ord('a')+random(26)); + AJson.Clear; + AJson.Add('LongString',S,qjson.jdtString); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' ַ:QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,YJson='+IntToStr(T4)+', T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + function RandomString(ALen:Integer):QStringW; + var + I:Integer; + p:PQCharW; + begin + SetLength(Result,ALen); + p:=PWideChar(Result); + for I := 0 to ALen-1 do + p[I]:=WideChar(Ord('A')+56); + end; + procedure TestString; + var + I:Integer; + begin + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),RandomString(20),qjson.jdtString); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' ַ:QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + procedure TestInteger; + var + I:Integer; + begin + AJson.Clear; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),I); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' :QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + + procedure TestNumeric; + var + I:Integer; + begin + AJson.Clear; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),I+random(10000)*0.0001); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' :QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + + procedure TestBoolean; + var + I:Integer; + begin + AJson.Clear; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),random(10)>5); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' :QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + + procedure TestNull; + var + I:Integer; + begin + AJson.Clear; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I)); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' ֵ:QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + + procedure TestObject; + var + I:Integer; + begin + AJson.Clear; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),'{"name":"object_'+IntToStr(I)+'"}'); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' :QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + + procedure TestArray; + var + I:Integer; + begin + AJson.Clear; + for I := 0 to 100000 do + AJson.Add('_'+IntToStr(I),'["name",'+IntToStr(I)+']'); + S:=AJson.AsJson; + T1:=GetTickCount; + AJson.Parse(S); + T1:=GetTickCount-T1; + Ansi:=S; + T2:=GetTickCount; + auk:=TlkJson.ParseText(Ansi); + T2:=GetTickCount-T2; + auk.Free; + T3:=GetTickCount; + SuperObj:=TSuperObject.ParseString(PWideChar(S),False); + T3:=GetTickCount-T3; + T4:=GetTickCount; + YJson.parse(S); + T4:=GetTickCount-T4; + mmResult.Lines.Add(' :QJson='+IntToStr(T1)+'ms,lkJson='+IntToStr(T2)+ + 'ms,SuperObject='+IntToStr(T3)+'ms,YJson='+IntToStr(T4)+'ms,T1/T2='+FormatFloat('0.##',T1*1.0/T2)+',T1/T3='+ + FormatFloat('0.##',T1*1.0/T3)+',T1/T4='+FormatFloat('0.##',T1*1.0/T4) + ); + end; + +begin +mmResult.Lines.Add('Բͬݽٶ'); +AJson:=TQJson.Create; +AJson.DataType:=qjson.jdtObject; +YJson:=JsonObject.Create; +TestLongString; +TestString; +TestInteger; +TestNumeric; +TestBoolean; +TestNull; +TestObject; +TestArray; +AJson.Free; +YJson.Free; +end; + +end. diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/superobject.pas" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/superobject.pas" new file mode 100644 index 0000000..191d53d --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/superobject.pas" @@ -0,0 +1,6555 @@ +(* + * Super Object Toolkit + * + * Usage allowed under the restrictions of the Lesser GNU General Public License + * or alternatively the restrictions of the Mozilla Public License 1.1 + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + * the specific language governing rights and limitations under the License. + * + * Unit owner : Henri Gourvest + * Web site : http://www.progdigy.com + * + * This unit is inspired from the json c lib: + * Michael Clark + * http://oss.metaparadigm.com/json-c/ + * + * CHANGES: + * v1.2 + * + support of currency data type + * + right trim unquoted string + * + read Unicode Files and streams (Litle Endian with BOM) + * + Fix bug on javadate functions + windows nt compatibility + * + Now you can force to parse only the canonical syntax of JSON using the stric parameter + * + Delphi 2010 RTTI marshalling + * v1.1 + * + Double licence MPL or LGPL. + * + Delphi 2009 compatibility & Unicode support. + * + AsString return a string instead of PChar. + * + Escaped and Unascaped JSON serialiser. + * + Missed FormFeed added \f + * - Removed @ trick, uses forcepath() method instead. + * + Fixed parse error with uppercase E symbol in numbers. + * + Fixed possible buffer overflow when enlarging array. + * + Added "delete", "pack", "insert" methods for arrays and/or objects + * + Multi parametters when calling methods + * + Delphi Enumerator (for obj1 in obj2 do ...) + * + Format method ex: obj.format('<%name%>%tab[1]%') + * + ParseFile and ParseStream methods + * + Parser now understand hexdecimal c syntax ex: \xFF + * + Null Object Design Patern (ex: for obj in values.N['path'] do ...) + * v1.0 + * + renamed class + * + interfaced object + * + added a new data type: the method + * + parser can now evaluate properties and call methods + * - removed obselet rpc class + * - removed "find" method, now you can use "parse" method instead + * v0.6 + * + refactoring + * v0.5 + * + new find method to get or set value using a path syntax + * ex: obj.s['obj.prop[1]'] := 'string value'; + * obj.a['@obj.array'].b[n] := true; // @ -> create property if necessary + * v0.4 + * + bug corrected: AVL tree badly balanced. + * v0.3 + * + New validator partially based on the Kwalify syntax. + * + extended syntax to parse unquoted fields. + * + Freepascal compatibility win32/64 Linux32/64. + * + JavaToDelphiDateTime and DelphiToJavaDateTime improved for UTC. + * + new TJsonObject.Compare function. + * v0.2 + * + Hashed string list replaced with a faster AVL tree + * + JsonInt data type can be changed to int64 + * + JavaToDelphiDateTime and DelphiToJavaDateTime helper fonctions + * + from json-c v0.7 + * + Add escaping of backslash to json output + * + Add escaping of foward slash on tokenizing and output + * + Changes to internal tokenizer from using recursion to + * using a depth state structure to allow incremental parsing + * v0.1 + * + first release + *) + +{$IFDEF FPC} + {$MODE OBJFPC}{$H+} +{$ENDIF} + +{$DEFINE SUPER_METHOD} +{$DEFINE WINDOWSNT_COMPATIBILITY} +{.$DEFINE DEBUG} // track memory leack + +unit superobject; + +interface +uses + Classes +{$IFDEF VER210} + ,Generics.Collections, RTTI, TypInfo +{$ENDIF} + ; + +type +{$IFNDEF FPC} + PtrInt = longint; + PtrUInt = Longword; +{$ENDIF} + SuperInt = Int64; + +{$if (sizeof(Char) = 1)} + SOChar = WideChar; + SOIChar = Word; + PSOChar = PWideChar; + SOString = WideString; +{$else} + SOChar = Char; + SOIChar = Word; + PSOChar = PChar; + SOString = string; +{$ifend} + +const + SUPER_ARRAY_LIST_DEFAULT_SIZE = 32; + SUPER_TOKENER_MAX_DEPTH = 32; + + SUPER_AVL_MAX_DEPTH = sizeof(longint) * 8; + SUPER_AVL_MASK_HIGH_BIT = not ((not longword(0)) shr 1); + +type + // forward declarations + TSuperObject = class; + ISuperObject = interface; + TSuperArray = class; + +(* AVL Tree + * This is a "special" autobalanced AVL tree + * It use a hash value for fast compare + *) + +{$IFDEF SUPER_METHOD} + TSuperMethod = procedure(const This, Params: ISuperObject; var Result: ISuperObject); +{$ENDIF} + + + TSuperAvlBitArray = set of 0..SUPER_AVL_MAX_DEPTH - 1; + + TSuperAvlSearchType = (stEQual, stLess, stGreater); + TSuperAvlSearchTypes = set of TSuperAvlSearchType; + TSuperAvlIterator = class; + + TSuperAvlEntry = class + private + FGt, FLt: TSuperAvlEntry; + FBf: integer; + FHash: Cardinal; + FName: SOString; + FPtr: Pointer; + function GetValue: ISuperObject; + procedure SetValue(const val: ISuperObject); + public + class function Hash(const k: SOString): Cardinal; virtual; + constructor Create(const AName: SOString; Obj: Pointer); virtual; + property Name: SOString read FName; + property Ptr: Pointer read FPtr; + property Value: ISuperObject read GetValue write SetValue; + end; + + TSuperAvlTree = class + private + FRoot: TSuperAvlEntry; + FCount: Integer; + function balance(bal: TSuperAvlEntry): TSuperAvlEntry; + protected + procedure doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); virtual; + function CompareNodeNode(node1, node2: TSuperAvlEntry): integer; virtual; + function CompareKeyNode(const k: SOString; h: TSuperAvlEntry): integer; virtual; + function Insert(h: TSuperAvlEntry): TSuperAvlEntry; virtual; + function Search(const k: SOString; st: TSuperAvlSearchTypes = [stEqual]): TSuperAvlEntry; virtual; + public + constructor Create; virtual; + destructor Destroy; override; + function IsEmpty: boolean; + procedure Clear(all: boolean = false); virtual; + procedure Pack(all: boolean); + function Delete(const k: SOString): ISuperObject; + function GetEnumerator: TSuperAvlIterator; + property count: Integer read FCount; + end; + + TSuperTableString = class(TSuperAvlTree) + protected + procedure doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); override; + procedure PutO(const k: SOString; const value: ISuperObject); + function GetO(const k: SOString): ISuperObject; + procedure PutS(const k: SOString; const value: SOString); + function GetS(const k: SOString): SOString; + procedure PutI(const k: SOString; value: SuperInt); + function GetI(const k: SOString): SuperInt; + procedure PutD(const k: SOString; value: Double); + function GetD(const k: SOString): Double; + procedure PutB(const k: SOString; value: Boolean); + function GetB(const k: SOString): Boolean; +{$IFDEF SUPER_METHOD} + procedure PutM(const k: SOString; value: TSuperMethod); + function GetM(const k: SOString): TSuperMethod; +{$ENDIF} + procedure PutN(const k: SOString; const value: ISuperObject); + function GetN(const k: SOString): ISuperObject; + procedure PutC(const k: SOString; value: Currency); + function GetC(const k: SOString): Currency; + public + property O[const k: SOString]: ISuperObject read GetO write PutO; default; + property S[const k: SOString]: SOString read GetS write PutS; + property I[const k: SOString]: SuperInt read GetI write PutI; + property D[const k: SOString]: Double read GetD write PutD; + property B[const k: SOString]: Boolean read GetB write PutB; +{$IFDEF SUPER_METHOD} + property M[const k: SOString]: TSuperMethod read GetM write PutM; +{$ENDIF} + property N[const k: SOString]: ISuperObject read GetN write PutN; + property C[const k: SOString]: Currency read GetC write PutC; + + function GetValues: ISuperObject; + function GetNames: ISuperObject; + end; + + TSuperAvlIterator = class + private + FTree: TSuperAvlTree; + FBranch: TSuperAvlBitArray; + FDepth: LongInt; + FPath: array[0..SUPER_AVL_MAX_DEPTH - 2] of TSuperAvlEntry; + public + constructor Create(tree: TSuperAvlTree); virtual; + procedure Search(const k: SOString; st: TSuperAvlSearchTypes = [stEQual]); + procedure First; + procedure Last; + function GetIter: TSuperAvlEntry; + procedure Next; + procedure Prior; + // delphi enumerator + function MoveNext: Boolean; + property Current: TSuperAvlEntry read GetIter; + end; + + TSuperObjectArray = array[0..(high(PtrInt) div sizeof(TSuperObject))-1] of ISuperObject; + PSuperObjectArray = ^TSuperObjectArray; + + TSuperArray = class + private + FArray: PSuperObjectArray; + FLength: Integer; + FSize: Integer; + procedure Expand(max: Integer); + protected + function GetO(const index: integer): ISuperObject; + procedure PutO(const index: integer; const Value: ISuperObject); + function GetB(const index: integer): Boolean; + procedure PutB(const index: integer; Value: Boolean); + function GetI(const index: integer): SuperInt; + procedure PutI(const index: integer; Value: SuperInt); + function GetD(const index: integer): Double; + procedure PutD(const index: integer; Value: Double); + function GetC(const index: integer): Currency; + procedure PutC(const index: integer; Value: Currency); + function GetS(const index: integer): SOString; + procedure PutS(const index: integer; const Value: SOString); +{$IFDEF SUPER_METHOD} + function GetM(const index: integer): TSuperMethod; + procedure PutM(const index: integer; Value: TSuperMethod); +{$ENDIF} + function GetN(const index: integer): ISuperObject; + procedure PutN(const index: integer; const Value: ISuperObject); + public + constructor Create; virtual; + destructor Destroy; override; + function Add(const Data: ISuperObject): Integer; + function Delete(index: Integer): ISuperObject; + procedure Insert(index: Integer; const value: ISuperObject); + procedure Clear(all: boolean = false); + procedure Pack(all: boolean); + property Length: Integer read FLength; + + property N[const index: integer]: ISuperObject read GetN write PutN; + property O[const index: integer]: ISuperObject read GetO write PutO; default; + property B[const index: integer]: boolean read GetB write PutB; + property I[const index: integer]: SuperInt read GetI write PutI; + property D[const index: integer]: Double read GetD write PutD; + property C[const index: integer]: Currency read GetC write PutC; + property S[const index: integer]: SOString read GetS write PutS; +{$IFDEF SUPER_METHOD} + property M[const index: integer]: TSuperMethod read GetM write PutM; +{$ENDIF} +// property A[const index: integer]: TSuperArray read GetA; + end; + + TSuperWriter = class + public + // abstact methods to overide + function Append(buf: PSOChar; Size: Integer): Integer; overload; virtual; abstract; + function Append(buf: PSOChar): Integer; overload; virtual; abstract; + procedure Reset; virtual; abstract; + end; + + TSuperWriterString = class(TSuperWriter) + private + FBuf: PSOChar; + FBPos: integer; + FSize: integer; + public + function Append(buf: PSOChar; Size: Integer): Integer; overload; override; + function Append(buf: PSOChar): Integer; overload; override; + procedure Reset; override; + procedure TrimRight; + constructor Create; virtual; + destructor Destroy; override; + function GetString: SOString; + property Data: PSOChar read FBuf; + property Size: Integer read FSize; + property Position: integer read FBPos; + end; + + TSuperWriterStream = class(TSuperWriter) + private + FStream: TStream; + public + function Append(buf: PSOChar): Integer; override; + procedure Reset; override; + constructor Create(AStream: TStream); reintroduce; virtual; + end; + + TSuperAnsiWriterStream = class(TSuperWriterStream) + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + end; + + TSuperUnicodeWriterStream = class(TSuperWriterStream) + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + end; + + TSuperWriterFake = class(TSuperWriter) + private + FSize: Integer; + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + function Append(buf: PSOChar): Integer; override; + procedure Reset; override; + constructor Create; reintroduce; virtual; + property size: integer read FSize; + end; + + TSuperWriterSock = class(TSuperWriter) + private + FSocket: longint; + FSize: Integer; + public + function Append(buf: PSOChar; Size: Integer): Integer; override; + function Append(buf: PSOChar): Integer; override; + procedure Reset; override; + constructor Create(ASocket: longint); reintroduce; virtual; + property Socket: longint read FSocket; + property Size: Integer read FSize; + end; + + TSuperTokenizerError = ( + teSuccess, + teContinue, + teDepth, + teParseEof, + teParseUnexpected, + teParseNull, + teParseBoolean, + teParseNumber, + teParseArray, + teParseObjectKeyName, + teParseObjectKeySep, + teParseObjectValueSep, + teParseString, + teParseComment, + teEvalObject, + teEvalArray, + teEvalMethod, + teEvalInt + ); + + TSuperTokenerState = ( + tsEatws, + tsStart, + tsFinish, + tsNull, + tsCommentStart, + tsComment, + tsCommentEol, + tsCommentEnd, + tsString, + tsStringEscape, + tsIdentifier, + tsEscapeUnicode, + tsEscapeHexadecimal, + tsBoolean, + tsNumber, + tsArray, + tsArrayAdd, + tsArraySep, + tsObjectFieldStart, + tsObjectField, + tsObjectUnquotedField, + tsObjectFieldEnd, + tsObjectValue, + tsObjectValueAdd, + tsObjectSep, + tsEvalProperty, + tsEvalArray, + tsEvalMethod, + tsParamValue, + tsParamPut, + tsMethodValue, + tsMethodPut + ); + + PSuperTokenerSrec = ^TSuperTokenerSrec; + TSuperTokenerSrec = record + state, saved_state: TSuperTokenerState; + obj: ISuperObject; + current: ISuperObject; + field_name: SOString; + parent: ISuperObject; + gparent: ISuperObject; + end; + + TSuperTokenizer = class + public + str: PSOChar; + pb: TSuperWriterString; + depth, is_double, floatcount, st_pos, char_offset: Integer; + err: TSuperTokenizerError; + ucs_char: Word; + quote_char: SOChar; + stack: array[0..SUPER_TOKENER_MAX_DEPTH-1] of TSuperTokenerSrec; + line, col: Integer; + public + constructor Create; virtual; + destructor Destroy; override; + procedure ResetLevel(adepth: integer); + procedure Reset; + end; + + // supported object types + TSuperType = ( + stNull, + stBoolean, + stDouble, + stCurrency, + stInt, + stObject, + stArray, + stString +{$IFDEF SUPER_METHOD} + ,stMethod +{$ENDIF} + ); + + TSuperValidateError = ( + veRuleMalformated, + veFieldIsRequired, + veInvalidDataType, + veFieldNotFound, + veUnexpectedField, + veDuplicateEntry, + veValueNotInEnum, + veInvalidLength, + veInvalidRange + ); + + TSuperFindOption = ( + foCreatePath, + foPutValue, + foDelete +{$IFDEF SUPER_METHOD} + ,foCallMethod +{$ENDIF} + ); + + TSuperFindOptions = set of TSuperFindOption; + TSuperCompareResult = (cpLess, cpEqu, cpGreat, cpError); + TSuperOnValidateError = procedure(sender: Pointer; error: TSuperValidateError; const objpath: SOString); + + TSuperEnumerator = class + private + FObj: ISuperObject; + FObjEnum: TSuperAvlIterator; + FCount: Integer; + public + constructor Create(const obj: ISuperObject); virtual; + destructor Destroy; override; + function MoveNext: Boolean; + function GetCurrent: ISuperObject; + property Current: ISuperObject read GetCurrent; + end; + + ISuperObject = interface + ['{4B86A9E3-E094-4E5A-954A-69048B7B6327}'] + function GetEnumerator: TSuperEnumerator; + function GetDataType: TSuperType; + function GetProcessing: boolean; + procedure SetProcessing(value: boolean); + function ForcePath(const path: SOString; dataType: TSuperType = stObject): ISuperObject; + function Format(const str: SOString; BeginSep: SOChar = '%'; EndSep: SOChar = '%'): SOString; + + function GetO(const path: SOString): ISuperObject; + procedure PutO(const path: SOString; const Value: ISuperObject); + function GetB(const path: SOString): Boolean; + procedure PutB(const path: SOString; Value: Boolean); + function GetI(const path: SOString): SuperInt; + procedure PutI(const path: SOString; Value: SuperInt); + function GetD(const path: SOString): Double; + procedure PutC(const path: SOString; Value: Currency); + function GetC(const path: SOString): Currency; + procedure PutD(const path: SOString; Value: Double); + function GetS(const path: SOString): SOString; + procedure PutS(const path: SOString; const Value: SOString); +{$IFDEF SUPER_METHOD} + function GetM(const path: SOString): TSuperMethod; + procedure PutM(const path: SOString; Value: TSuperMethod); +{$ENDIF} + function GetA(const path: SOString): TSuperArray; + + // Null Object Design patern + function GetN(const path: SOString): ISuperObject; + procedure PutN(const path: SOString; const Value: ISuperObject); + + // Writers + function Write(writer: TSuperWriter; indent: boolean; escape: boolean; level: integer): Integer; + function SaveTo(stream: TStream; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(const FileName: string; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(socket: longint; indent: boolean = false; escape: boolean = true): integer; overload; + function CalcSize(indent: boolean = false; escape: boolean = true): integer; + + // convert + function AsBoolean: Boolean; + function AsInteger: SuperInt; + function AsDouble: Double; + function AsCurrency: Currency; + function AsString: SOString; + function AsArray: TSuperArray; + function AsObject: TSuperTableString; +{$IFDEF SUPER_METHOD} + function AsMethod: TSuperMethod; +{$ENDIF} + function AsJSon(indent: boolean = false; escape: boolean = true): SOString; + + procedure Clear(all: boolean = false); + procedure Pack(all: boolean = false); + + property N[const path: SOString]: ISuperObject read GetN write PutN; + property O[const path: SOString]: ISuperObject read GetO write PutO; default; + property B[const path: SOString]: boolean read GetB write PutB; + property I[const path: SOString]: SuperInt read GetI write PutI; + property D[const path: SOString]: Double read GetD write PutD; + property C[const path: SOString]: Currency read GetC write PutC; + property S[const path: SOString]: SOString read GetS write PutS; +{$IFDEF SUPER_METHOD} + property M[const path: SOString]: TSuperMethod read GetM write PutM; +{$ENDIF} + property A[const path: SOString]: TSuperArray read GetA; + +{$IFDEF SUPER_METHOD} + function call(const path: SOString; const param: ISuperObject = nil): ISuperObject; overload; + function call(const path, param: SOString): ISuperObject; overload; +{$ENDIF} + // clone a node + function Clone: ISuperObject; + function Delete(const path: SOString): ISuperObject; + // merges tow objects of same type, if reference is true then nodes are not cloned + procedure Merge(const obj: ISuperObject; reference: boolean = false); overload; + procedure Merge(const str: SOString); overload; + + // validate methods + function Validate(const rules: SOString; const defs: SOString = ''; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + function Validate(const rules: ISuperObject; const defs: ISuperObject = nil; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + + // compare + function Compare(const obj: ISuperObject): TSuperCompareResult; overload; + function Compare(const str: SOString): TSuperCompareResult; overload; + + // the data type + function IsType(AType: TSuperType): boolean; + property DataType: TSuperType read GetDataType; + property Processing: boolean read GetProcessing write SetProcessing; + + function GetDataPtr: Pointer; + procedure SetDataPtr(const Value: Pointer); + property DataPtr: Pointer read GetDataPtr write SetDataPtr; + end; + + TSuperObject = class(TObject, ISuperObject) + private + FRefCount: Integer; + FProcessing: boolean; + FDataType: TSuperType; + FDataPtr: Pointer; +{.$if true} + FO: record + case TSuperType of + stBoolean: (c_boolean: boolean); + stDouble: (c_double: double); + stCurrency: (c_currency: Currency); + stInt: (c_int: SuperInt); + stObject: (c_object: TSuperTableString); + stArray: (c_array: TSuperArray); +{$IFDEF SUPER_METHOD} + stMethod: (c_method: TSuperMethod); +{$ENDIF} + end; +{.$ifend} + FOString: SOString; + function GetDataType: TSuperType; + function GetDataPtr: Pointer; + procedure SetDataPtr(const Value: Pointer); + protected + function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall; + function _AddRef: Integer; virtual; stdcall; + function _Release: Integer; virtual; stdcall; + + function GetO(const path: SOString): ISuperObject; + procedure PutO(const path: SOString; const Value: ISuperObject); + function GetB(const path: SOString): Boolean; + procedure PutB(const path: SOString; Value: Boolean); + function GetI(const path: SOString): SuperInt; + procedure PutI(const path: SOString; Value: SuperInt); + function GetD(const path: SOString): Double; + procedure PutD(const path: SOString; Value: Double); + procedure PutC(const path: SOString; Value: Currency); + function GetC(const path: SOString): Currency; + function GetS(const path: SOString): SOString; + procedure PutS(const path: SOString; const Value: SOString); +{$IFDEF SUPER_METHOD} + function GetM(const path: SOString): TSuperMethod; + procedure PutM(const path: SOString; Value: TSuperMethod); +{$ENDIF} + function GetA(const path: SOString): TSuperArray; + function Write(writer: TSuperWriter; indent: boolean; escape: boolean; level: integer): Integer; virtual; + public + function GetEnumerator: TSuperEnumerator; + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + class function NewInstance: TObject; override; + property RefCount: Integer read FRefCount; + + function GetProcessing: boolean; + procedure SetProcessing(value: boolean); + + // Writers + function SaveTo(stream: TStream; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(const FileName: string; indent: boolean = false; escape: boolean = true): integer; overload; + function SaveTo(socket: longint; indent: boolean = false; escape: boolean = true): integer; overload; + function CalcSize(indent: boolean = false; escape: boolean = true): integer; + function AsJSon(indent: boolean = false; escape: boolean = true): SOString; + + // parser ... owned! + class function ParseString(s: PSOChar; strict: Boolean; partial: boolean = true; const this: ISuperObject = nil; options: TSuperFindOptions = []; + const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + class function ParseStream(stream: TStream; strict: Boolean; partial: boolean = true; const this: ISuperObject = nil; options: TSuperFindOptions = []; + const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + class function ParseFile(const FileName: string; strict: Boolean; partial: boolean = true; const this: ISuperObject = nil; options: TSuperFindOptions = []; + const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + class function ParseEx(tok: TSuperTokenizer; str: PSOChar; len: integer; strict: Boolean; const this: ISuperObject = nil; + options: TSuperFindOptions = []; const put: ISuperObject = nil; dt: TSuperType = stNull): ISuperObject; + + // constructors / destructor + constructor Create(jt: TSuperType = stObject); overload; virtual; + constructor Create(b: boolean); overload; virtual; + constructor Create(i: SuperInt); overload; virtual; + constructor Create(d: double); overload; virtual; + constructor CreateCurrency(c: Currency); overload; virtual; + constructor Create(const s: SOString); overload; virtual; +{$IFDEF SUPER_METHOD} + constructor Create(m: TSuperMethod); overload; virtual; +{$ENDIF} + destructor Destroy; override; + + // convert + function AsBoolean: Boolean; virtual; + function AsInteger: SuperInt; virtual; + function AsDouble: Double; virtual; + function AsCurrency: Currency; virtual; + function AsString: SOString; virtual; + function AsArray: TSuperArray; virtual; + function AsObject: TSuperTableString; virtual; +{$IFDEF SUPER_METHOD} + function AsMethod: TSuperMethod; virtual; +{$ENDIF} + procedure Clear(all: boolean = false); virtual; + procedure Pack(all: boolean = false); virtual; + function GetN(const path: SOString): ISuperObject; + procedure PutN(const path: SOString; const Value: ISuperObject); + function ForcePath(const path: SOString; dataType: TSuperType = stObject): ISuperObject; + function Format(const str: SOString; BeginSep: SOChar = '%'; EndSep: SOChar = '%'): SOString; + + property N[const path: SOString]: ISuperObject read GetN write PutN; + property O[const path: SOString]: ISuperObject read GetO write PutO; default; + property B[const path: SOString]: boolean read GetB write PutB; + property I[const path: SOString]: SuperInt read GetI write PutI; + property D[const path: SOString]: Double read GetD write PutD; + property C[const path: SOString]: Currency read GetC write PutC; + property S[const path: SOString]: SOString read GetS write PutS; +{$IFDEF SUPER_METHOD} + property M[const path: SOString]: TSuperMethod read GetM write PutM; +{$ENDIF} + property A[const path: SOString]: TSuperArray read GetA; + +{$IFDEF SUPER_METHOD} + function call(const path: SOString; const param: ISuperObject = nil): ISuperObject; overload; virtual; + function call(const path, param: SOString): ISuperObject; overload; virtual; +{$ENDIF} + // clone a node + function Clone: ISuperObject; virtual; + function Delete(const path: SOString): ISuperObject; + // merges tow objects of same type, if reference is true then nodes are not cloned + procedure Merge(const obj: ISuperObject; reference: boolean = false); overload; + procedure Merge(const str: SOString); overload; + + // validate methods + function Validate(const rules: SOString; const defs: SOString = ''; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + function Validate(const rules: ISuperObject; const defs: ISuperObject = nil; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; overload; + + // compare + function Compare(const obj: ISuperObject): TSuperCompareResult; overload; + function Compare(const str: SOString): TSuperCompareResult; overload; + + // the data type + function IsType(AType: TSuperType): boolean; + property DataType: TSuperType read GetDataType; + // a data pointer to link to something ele, a treeview for example + property DataPtr: Pointer read GetDataPtr write SetDataPtr; + property Processing: boolean read GetProcessing; + end; + +{$IFDEF VER210} + TSuperRttiContext = class; + + TSerialFromJson = function(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; + TSerialToJson = function(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; + + TSuperAttribute = class(TCustomAttribute) + private + FName: string; + public + constructor Create(const AName: string); + property Name: string read FName; + end; + + SOName = class(TSuperAttribute); + SODefault = class(TSuperAttribute); + + + TSuperRttiContext = class + private + class function GetFieldName(r: TRttiField): string; + class function GetFieldDefault(r: TRttiField; const obj: ISuperObject): ISuperObject; + public + Context: TRttiContext; + SerialFromJson: TDictionary; + SerialToJson: TDictionary; + constructor Create; virtual; + destructor Destroy; override; + function FromJson(TypeInfo: PTypeInfo; const obj: ISuperObject; var Value: TValue): Boolean; virtual; + function ToJson(var value: TValue; const index: ISuperObject): ISuperObject; virtual; + function AsType(const obj: ISuperObject): T; + function AsJson(const obj: T; const index: ISuperObject = nil): ISuperObject; + end; + + TSuperObjectHelper = class helper for TObject + public + function ToJson(ctx: TSuperRttiContext = nil): ISuperObject; + constructor FromJson(const obj: ISuperObject; ctx: TSuperRttiContext = nil); overload; + constructor FromJson(const str: string; ctx: TSuperRttiContext = nil); overload; + end; +{$ENDIF} + + TSuperObjectIter = record + key: SOString; + val: ISuperObject; + Ite: TSuperAvlIterator; + end; + +function ObjectIsError(obj: TSuperObject): boolean; +function ObjectIsType(const obj: ISuperObject; typ: TSuperType): boolean; +function ObjectGetType(const obj: ISuperObject): TSuperType; + +function ObjectFindFirst(const obj: ISuperObject; var F: TSuperObjectIter): boolean; +function ObjectFindNext(var F: TSuperObjectIter): boolean; +procedure ObjectFindClose(var F: TSuperObjectIter); + +function SO(const s: SOString = '{}'): ISuperObject; overload; +function SO(const value: Variant): ISuperObject; overload; +function SO(const Args: array of const): ISuperObject; overload; + +function SA(const Args: array of const): ISuperObject; overload; + +function JavaToDelphiDateTime(const dt: int64): TDateTime; +function DelphiToJavaDateTime(const dt: TDateTime): int64; + +{$IFDEF VER210} + +type + TSuperInvokeResult = ( + irSuccess, + irMethothodError, // method don't exist + irParamError, // invalid parametters + irError // other error + ); + +function TrySOInvoke(var ctx: TSuperRttiContext; const obj: TValue; const method: string; const params: ISuperObject; var Return: ISuperObject): TSuperInvokeResult; overload; +function SOInvoke(const obj: TValue; const method: string; const params: ISuperObject; ctx: TSuperRttiContext = nil): ISuperObject; overload; +function SOInvoke(const obj: TValue; const method: string; const params: string; ctx: TSuperRttiContext = nil): ISuperObject; overload; +{$ENDIF} + +implementation +uses sysutils, +{$IFDEF UNIX} + baseunix, unix, DateUtils +{$ELSE} + Windows +{$ENDIF} +{$IFDEF FPC} + ,sockets +{$ELSE} + ,WinSock +{$ENDIF}; + +{$IFDEF DEBUG} +var + debugcount: integer = 0; +{$ENDIF} + +const + super_number_chars_set = ['0'..'9','.','+','-','e','E']; + super_hex_chars: PSOChar = '0123456789abcdef'; + super_hex_chars_set = ['0'..'9','a'..'f','A'..'F']; + + ESC_BS: PSOChar = '\b'; + ESC_LF: PSOChar = '\n'; + ESC_CR: PSOChar = '\r'; + ESC_TAB: PSOChar = '\t'; + ESC_FF: PSOChar = '\f'; + ESC_QUOT: PSOChar = '\"'; + ESC_SL: PSOChar = '\\'; + ESC_SR: PSOChar = '\/'; + ESC_ZERO: PSOChar = '\u0000'; + + TOK_CRLF: PSOChar = #13#10; + TOK_SP: PSOChar = #32; + TOK_BS: PSOChar = #8; + TOK_TAB: PSOChar = #9; + TOK_LF: PSOChar = #10; + TOK_FF: PSOChar = #12; + TOK_CR: PSOChar = #13; +// TOK_SL: PSOChar = '\'; +// TOK_SR: PSOChar = '/'; + TOK_NULL: PSOChar = 'null'; + TOK_CBL: PSOChar = '{'; // curly bracket left + TOK_CBR: PSOChar = '}'; // curly bracket right + TOK_ARL: PSOChar = '['; + TOK_ARR: PSOChar = ']'; + TOK_ARRAY: PSOChar = '[]'; + TOK_OBJ: PSOChar = '{}'; // empty object + TOK_COM: PSOChar = ','; // Comma + TOK_DQT: PSOChar = '"'; // Double Quote + TOK_TRUE: PSOChar = 'true'; + TOK_FALSE: PSOChar = 'false'; + +{$if (sizeof(Char) = 1)} +function StrLComp(const Str1, Str2: PSOChar; MaxLen: Cardinal): Integer; +var + P1, P2: PWideChar; + I: Cardinal; + C1, C2: WideChar; +begin + P1 := Str1; + P2 := Str2; + I := 0; + while I < MaxLen do + begin + C1 := P1^; + C2 := P2^; + + if (C1 <> C2) or (C1 = #0) then + begin + Result := Ord(C1) - Ord(C2); + Exit; + end; + + Inc(P1); + Inc(P2); + Inc(I); + end; + Result := 0; +end; + +function StrComp(const Str1, Str2: PSOChar): Integer; +var + P1, P2: PWideChar; + C1, C2: WideChar; +begin + P1 := Str1; + P2 := Str2; + while True do + begin + C1 := P1^; + C2 := P2^; + + if (C1 <> C2) or (C1 = #0) then + begin + Result := Ord(C1) - Ord(C2); + Exit; + end; + + Inc(P1); + Inc(P2); + end; +end; + +function StrLen(const Str: PSOChar): Cardinal; +var + p: PSOChar; +begin + Result := 0; + if Str <> nil then + begin + p := Str; + while p^ <> #0 do inc(p); + Result := (p - Str); + end; +end; +{$ifend} + +function CurrToStr(c: Currency): SOString; +var + p: PSOChar; + i, len: Integer; +begin + Result := IntToStr(Abs(PInt64(@c)^)); + len := Length(Result); + SetLength(Result, len+1); + if c <> 0 then + begin + while len <= 4 do + begin + Result := '0' + Result; + inc(len); + end; + + p := PSOChar(Result); + inc(p, len-1); + i := 0; + repeat + if p^ <> '0' then + begin + len := len - i + 1; + repeat + p[1] := p^; + dec(p); + inc(i); + until i > 3; + Break; + end; + dec(p); + inc(i); + if i > 3 then + begin + len := len - i + 1; + Break; + end; + until false; + p[1] := '.'; + SetLength(Result, len); + if c < 0 then + Result := '-' + Result; + end; +end; + +{$IFDEF UNIX} + {$linklib c} +{$ENDIF} +function gcvt(value: Double; ndigit: longint; buf: PAnsiChar): PAnsiChar; cdecl; + external {$IFDEF MSWINDOWS} 'msvcrt.dll' name '_gcvt'{$ENDIF}; + +{$IFDEF UNIX} +type + ptm = ^tm; + tm = record + tm_sec: Integer; (* Seconds: 0-59 (K&R says 0-61?) *) + tm_min: Integer; (* Minutes: 0-59 *) + tm_hour: Integer; (* Hours since midnight: 0-23 *) + tm_mday: Integer; (* Day of the month: 1-31 *) + tm_mon: Integer; (* Months *since* january: 0-11 *) + tm_year: Integer; (* Years since 1900 *) + tm_wday: Integer; (* Days since Sunday (0-6) *) + tm_yday: Integer; (* Days since Jan. 1: 0-365 *) + tm_isdst: Integer; (* +1 Daylight Savings Time, 0 No DST, -1 don't know *) + end; + +function mktime(p: ptm): LongInt; cdecl; external; +function gmtime(const t: PLongint): ptm; cdecl; external; +function localtime (const t: PLongint): ptm; cdecl; external; + +function DelphiToJavaDateTime(const dt: TDateTime): Int64; +var + p: ptm; + l, ms: Integer; + v: Int64; +begin + v := Round((dt - 25569) * 86400000); + ms := v mod 1000; + l := v div 1000; + p := localtime(@l); + Result := Int64(mktime(p)) * 1000 + ms; +end; + +function JavaToDelphiDateTime(const dt: int64): TDateTime; +var + p: ptm; + l, ms: Integer; +begin + l := dt div 1000; + ms := dt mod 1000; + p := gmtime(@l); + Result := EncodeDateTime(p^.tm_year+1900, p^.tm_mon+1, p^.tm_mday, p^.tm_hour, p^.tm_min, p^.tm_sec, ms); +end; +{$ELSE} + +{$IFDEF WINDOWSNT_COMPATIBILITY} +function DayLightCompareDate(const date: PSystemTime; + const compareDate: PSystemTime): Integer; +var + limit_day, dayinsecs, weekofmonth: Integer; + First: Word; +begin + if (date^.wMonth < compareDate^.wMonth) then + begin + Result := -1; (* We are in a month before the date limit. *) + Exit; + end; + + if (date^.wMonth > compareDate^.wMonth) then + begin + Result := 1; (* We are in a month after the date limit. *) + Exit; + end; + + (* if year is 0 then date is in day-of-week format, otherwise + * it's absolute date. + *) + if (compareDate^.wYear = 0) then + begin + (* compareDate.wDay is interpreted as number of the week in the month + * 5 means: the last week in the month *) + weekofmonth := compareDate^.wDay; + (* calculate the day of the first DayOfWeek in the month *) + First := (6 + compareDate^.wDayOfWeek - date^.wDayOfWeek + date^.wDay) mod 7 + 1; + limit_day := First + 7 * (weekofmonth - 1); + (* check needed for the 5th weekday of the month *) + if (limit_day > MonthDays[(date^.wMonth=2) and IsLeapYear(date^.wYear)][date^.wMonth - 1]) then + dec(limit_day, 7); + end + else + limit_day := compareDate^.wDay; + + (* convert to seconds *) + limit_day := ((limit_day * 24 + compareDate^.wHour) * 60 + compareDate^.wMinute ) * 60; + dayinsecs := ((date^.wDay * 24 + date^.wHour) * 60 + date^.wMinute ) * 60 + date^.wSecond; + (* and compare *) + + if dayinsecs < limit_day then + Result := -1 else + if dayinsecs > limit_day then + Result := 1 else + Result := 0; (* date is equal to the date limit. *) +end; + +function CompTimeZoneID(const pTZinfo: PTimeZoneInformation; + lpFileTime: PFileTime; islocal: Boolean): LongWord; +var + ret: Integer; + beforeStandardDate, afterDaylightDate: Boolean; + llTime: Int64; + SysTime: TSystemTime; + ftTemp: TFileTime; +begin + llTime := 0; + + if (pTZinfo^.DaylightDate.wMonth <> 0) then + begin + (* if year is 0 then date is in day-of-week format, otherwise + * it's absolute date. + *) + if ((pTZinfo^.StandardDate.wMonth = 0) or + ((pTZinfo^.StandardDate.wYear = 0) and + ((pTZinfo^.StandardDate.wDay < 1) or + (pTZinfo^.StandardDate.wDay > 5) or + (pTZinfo^.DaylightDate.wDay < 1) or + (pTZinfo^.DaylightDate.wDay > 5)))) then + begin + SetLastError(ERROR_INVALID_PARAMETER); + Result := TIME_ZONE_ID_INVALID; + Exit; + end; + + if (not islocal) then + begin + llTime := PInt64(lpFileTime)^; + dec(llTime, Int64(pTZinfo^.Bias + pTZinfo^.DaylightBias) * 600000000); + PInt64(@ftTemp)^ := llTime; + lpFileTime := @ftTemp; + end; + + FileTimeToSystemTime(lpFileTime^, SysTime); + + (* check for daylight savings *) + ret := DayLightCompareDate(@SysTime, @pTZinfo^.StandardDate); + if (ret = -2) then + begin + Result := TIME_ZONE_ID_INVALID; + Exit; + end; + + beforeStandardDate := ret < 0; + + if (not islocal) then + begin + dec(llTime, Int64(pTZinfo^.StandardBias - pTZinfo^.DaylightBias) * 600000000); + PInt64(@ftTemp)^ := llTime; + FileTimeToSystemTime(lpFileTime^, SysTime); + end; + + ret := DayLightCompareDate(@SysTime, @pTZinfo^.DaylightDate); + if (ret = -2) then + begin + Result := TIME_ZONE_ID_INVALID; + Exit; + end; + + afterDaylightDate := ret >= 0; + + Result := TIME_ZONE_ID_STANDARD; + if( pTZinfo^.DaylightDate.wMonth < pTZinfo^.StandardDate.wMonth ) then + begin + (* Northern hemisphere *) + if( beforeStandardDate and afterDaylightDate) then + Result := TIME_ZONE_ID_DAYLIGHT; + end else (* Down south *) + if( beforeStandardDate or afterDaylightDate) then + Result := TIME_ZONE_ID_DAYLIGHT; + end else + (* No transition date *) + Result := TIME_ZONE_ID_UNKNOWN; +end; + +function GetTimezoneBias(const pTZinfo: PTimeZoneInformation; + lpFileTime: PFileTime; islocal: Boolean; pBias: PLongint): Boolean; +var + bias: LongInt; + tzid: LongWord; +begin + bias := pTZinfo^.Bias; + tzid := CompTimeZoneID(pTZinfo, lpFileTime, islocal); + + if( tzid = TIME_ZONE_ID_INVALID) then + begin + Result := False; + Exit; + end; + if (tzid = TIME_ZONE_ID_DAYLIGHT) then + inc(bias, pTZinfo^.DaylightBias) + else if (tzid = TIME_ZONE_ID_STANDARD) then + inc(bias, pTZinfo^.StandardBias); + pBias^ := bias; + Result := True; +end; + +function SystemTimeToTzSpecificLocalTime( + lpTimeZoneInformation: PTimeZoneInformation; + lpUniversalTime, lpLocalTime: PSystemTime): BOOL; +var + ft: TFileTime; + lBias: LongInt; + llTime: Int64; + tzinfo: TTimeZoneInformation; +begin + if (lpTimeZoneInformation <> nil) then + tzinfo := lpTimeZoneInformation^ else + if (GetTimeZoneInformation(tzinfo) = TIME_ZONE_ID_INVALID) then + begin + Result := False; + Exit; + end; + + if (not SystemTimeToFileTime(lpUniversalTime^, ft)) then + begin + Result := False; + Exit; + end; + llTime := PInt64(@ft)^; + if (not GetTimezoneBias(@tzinfo, @ft, False, @lBias)) then + begin + Result := False; + Exit; + end; + (* convert minutes to 100-nanoseconds-ticks *) + dec(llTime, Int64(lBias) * 600000000); + PInt64(@ft)^ := llTime; + Result := FileTimeToSystemTime(ft, lpLocalTime^); +end; + +function TzSpecificLocalTimeToSystemTime( + const lpTimeZoneInformation: PTimeZoneInformation; + const lpLocalTime: PSystemTime; lpUniversalTime: PSystemTime): BOOL; +var + ft: TFileTime; + lBias: LongInt; + t: Int64; + tzinfo: TTimeZoneInformation; +begin + if (lpTimeZoneInformation <> nil) then + tzinfo := lpTimeZoneInformation^ + else + if (GetTimeZoneInformation(tzinfo) = TIME_ZONE_ID_INVALID) then + begin + Result := False; + Exit; + end; + + if (not SystemTimeToFileTime(lpLocalTime^, ft)) then + begin + Result := False; + Exit; + end; + t := PInt64(@ft)^; + if (not GetTimezoneBias(@tzinfo, @ft, True, @lBias)) then + begin + Result := False; + Exit; + end; + (* convert minutes to 100-nanoseconds-ticks *) + inc(t, Int64(lBias) * 600000000); + PInt64(@ft)^ := t; + Result := FileTimeToSystemTime(ft, lpUniversalTime^); +end; +{$ELSE} +function TzSpecificLocalTimeToSystemTime( + lpTimeZoneInformation: PTimeZoneInformation; + lpLocalTime, lpUniversalTime: PSystemTime): BOOL; stdcall; external 'kernel32.dll'; + +function SystemTimeToTzSpecificLocalTime( + lpTimeZoneInformation: PTimeZoneInformation; + lpUniversalTime, lpLocalTime: PSystemTime): BOOL; stdcall; external 'kernel32.dll'; +{$ENDIF} + +function JavaToDelphiDateTime(const dt: int64): TDateTime; +var + t: TSystemTime; +begin + DateTimeToSystemTime(25569 + (dt / 86400000), t); + SystemTimeToTzSpecificLocalTime(nil, @t, @t); + Result := SystemTimeToDateTime(t); +end; + +function DelphiToJavaDateTime(const dt: TDateTime): int64; +var + t: TSystemTime; +begin + DateTimeToSystemTime(dt, t); + TzSpecificLocalTimeToSystemTime(nil, @t, @t); + Result := Round((SystemTimeToDateTime(t) - 25569) * 86400000) +end; +{$ENDIF} + + +function SO(const s: SOString): ISuperObject; overload; +begin + Result := TSuperObject.ParseString(PSOChar(s), False); +end; + +function SA(const Args: array of const): ISuperObject; overload; +type + TByteArray = array[0..sizeof(integer) - 1] of byte; + PByteArray = ^TByteArray; +var + j: Integer; + intf: IInterface; +begin + Result := TSuperObject.Create(stArray); + for j := 0 to length(Args) - 1 do + with Result.AsArray do + case TVarRec(Args[j]).VType of + vtInteger : Add(TSuperObject.Create(TVarRec(Args[j]).VInteger)); + vtInt64 : Add(TSuperObject.Create(TVarRec(Args[j]).VInt64^)); + vtBoolean : Add(TSuperObject.Create(TVarRec(Args[j]).VBoolean)); + vtChar : Add(TSuperObject.Create(SOString(TVarRec(Args[j]).VChar))); + vtWideChar: Add(TSuperObject.Create(SOChar(TVarRec(Args[j]).VWideChar))); + vtExtended: Add(TSuperObject.Create(TVarRec(Args[j]).VExtended^)); + vtCurrency: Add(TSuperObject.CreateCurrency(TVarRec(Args[j]).VCurrency^)); + vtString : Add(TSuperObject.Create(SOString(TVarRec(Args[j]).VString^))); + vtPChar : Add(TSuperObject.Create(SOString(TVarRec(Args[j]).VPChar^))); + vtAnsiString: Add(TSuperObject.Create(SOString(AnsiString(TVarRec(Args[j]).VAnsiString)))); + vtWideString: Add(TSuperObject.Create(SOString(PWideChar(TVarRec(Args[j]).VWideString)))); + vtInterface: + if TVarRec(Args[j]).VInterface = nil then + Add(nil) else + if IInterface(TVarRec(Args[j]).VInterface).QueryInterface(ISuperObject, intf) = 0 then + Add(ISuperObject(intf)) else + Add(nil); + vtPointer : + if TVarRec(Args[j]).VPointer = nil then + Add(nil) else + Add(TSuperObject.Create(PtrInt(TVarRec(Args[j]).VPointer))); + vtVariant: + Add(SO(TVarRec(Args[j]).VVariant^)); + vtObject: + if TVarRec(Args[j]).VPointer = nil then + Add(nil) else + Add(TSuperObject.Create(PtrInt(TVarRec(Args[j]).VPointer))); + vtClass: + if TVarRec(Args[j]).VPointer = nil then + Add(nil) else + Add(TSuperObject.Create(PtrInt(TVarRec(Args[j]).VPointer))); +{$if declared(vtUnicodeString)} + vtUnicodeString: + Add(TSuperObject.Create(SOString(string(TVarRec(Args[j]).VUnicodeString)))); +{$ifend} + else + assert(false); + end; +end; + +function SO(const Args: array of const): ISuperObject; overload; +var + j: Integer; + arr: ISuperObject; +begin + Result := TSuperObject.Create(stObject); + arr := SA(Args); + with arr.AsArray do + for j := 0 to (Length div 2) - 1 do + Result.AsObject.PutO(O[j*2].AsString, O[(j*2) + 1]); +end; + +function SO(const value: Variant): ISuperObject; overload; +begin + with TVarData(value) do + case VType of + varNull: Result := nil; + varEmpty: Result := nil; + varSmallInt: Result := TSuperObject.Create(VSmallInt); + varInteger: Result := TSuperObject.Create(VInteger); + varSingle: Result := TSuperObject.Create(VSingle); + varDouble: Result := TSuperObject.Create(VDouble); + varCurrency: Result := TSuperObject.CreateCurrency(VCurrency); + varDate: Result := TSuperObject.Create(DelphiToJavaDateTime(vDate)); + varOleStr: Result := TSuperObject.Create(SOString(VOleStr)); + varBoolean: Result := TSuperObject.Create(VBoolean); + varShortInt: Result := TSuperObject.Create(VShortInt); + varByte: Result := TSuperObject.Create(VByte); + varWord: Result := TSuperObject.Create(VWord); + varLongWord: Result := TSuperObject.Create(VLongWord); + varInt64: Result := TSuperObject.Create(VInt64); + varString: Result := TSuperObject.Create(SOString(AnsiString(VString))); +{$if declared(varUString)} + varUString: Result := TSuperObject.Create(SOString(string(VUString))); +{$ifend} + else + raise Exception.CreateFmt('Unsuported variant data type: %d', [VType]); + end; +end; + +function ObjectIsError(obj: TSuperObject): boolean; +begin + Result := PtrUInt(obj) > PtrUInt(-4000); +end; + +function ObjectIsType(const obj: ISuperObject; typ: TSuperType): boolean; +begin + if obj <> nil then + Result := typ = obj.DataType else + Result := typ = stNull; +end; + +function ObjectGetType(const obj: ISuperObject): TSuperType; +begin + if obj <> nil then + Result := obj.DataType else + Result := stNull; +end; + +function ObjectFindFirst(const obj: ISuperObject; var F: TSuperObjectIter): boolean; +var + i: TSuperAvlEntry; +begin + if ObjectIsType(obj, stObject) then + begin + F.Ite := TSuperAvlIterator.Create(obj.AsObject); + F.Ite.First; + i := F.Ite.GetIter; + if i <> nil then + begin + f.key := i.Name; + f.val := i.Value; + Result := true; + end else + Result := False; + end else + Result := False; +end; + +function ObjectFindNext(var F: TSuperObjectIter): boolean; +var + i: TSuperAvlEntry; +begin + F.Ite.Next; + i := F.Ite.GetIter; + if i <> nil then + begin + f.key := i.FName; + f.val := i.Value; + Result := true; + end else + Result := False; +end; + +procedure ObjectFindClose(var F: TSuperObjectIter); +begin + F.Ite.Free; + F.val := nil; +end; + +{$IFDEF VER210} + +function serialtoboolean(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; +begin + Result := TSuperObject.Create(TValueData(value).FAsSLong <> 0); +end; + +function serialtodatetime(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; +begin + Result := TSuperObject.Create(DelphiToJavaDateTime(TValueData(value).FAsDouble)); +end; + +function serialtoguid(ctx: TSuperRttiContext; var value: TValue; const index: ISuperObject): ISuperObject; +var + g: TGUID; +begin + value.ExtractRawData(@g); + Result := TSuperObject.Create( + format('%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x', + [g.D1, g.D2, g.D3, + g.D4[0], g.D4[1], g.D4[2], + g.D4[3], g.D4[4], g.D4[5], + g.D4[6], g.D4[7]]) + ); +end; + +function serialfromboolean(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; +var + o: ISuperObject; +begin + case ObjectGetType(obj) of + stBoolean: + begin + TValueData(Value).FAsSLong := obj.AsInteger; + Result := True; + end; + stInt: + begin + TValueData(Value).FAsSLong := ord(obj.AsInteger <> 0); + Result := True; + end; + stString: + begin + o := SO(obj.AsString); + if not ObjectIsType(o, stString) then + Result := serialfromboolean(ctx, SO(obj.AsString), Value) else + Result := False; + end; + else + Result := False; + end; +end; + +function serialfromdatetime(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; +var + dt: TDateTime; +begin + case ObjectGetType(obj) of + stInt: + begin + TValueData(Value).FAsDouble := JavaToDelphiDateTime(obj.AsInteger); + Result := True; + end; + stString: + begin + if TryStrToDateTime(obj.AsString, dt) then + begin + TValueData(Value).FAsDouble := dt; + Result := True; + end else + Result := False; + end; + else + Result := False; + end; +end; + +function UuidFromString(const s: PSOChar; Uuid: PGUID): Boolean; +const + hex2bin: array[#0..#102] of short = ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x00 *) + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x10 *) + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x20 *) + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, (* 0x30 *) + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x40 *) + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, (* 0x50 *) + -1,10,11,12,13,14,15); (* 0x60 *) +var + i: Integer; +begin + if (strlen(s) <> 36) then Exit(False); + + if ((s[8] <> '-') or (s[13] <> '-') or (s[18] <> '-') or (s[23] <> '-')) then + Exit(False); + + for i := 0 to 35 do + begin + if not i in [8,13,18,23] then + if ((s[i] > 'f') or ((hex2bin[s[i]] = -1) and (s[i] <> ''))) then + Exit(False); + end; + + uuid.D1 := ((hex2bin[s[0]] shl 28) or (hex2bin[s[1]] shl 24) or (hex2bin[s[2]] shl 20) or (hex2bin[s[3]] shl 16) or + (hex2bin[s[4]] shl 12) or (hex2bin[s[5]] shl 8) or (hex2bin[s[6]] shl 4) or hex2bin[s[7]]); + uuid.D2 := (hex2bin[s[9]] shl 12) or (hex2bin[s[10]] shl 8) or (hex2bin[s[11]] shl 4) or hex2bin[s[12]]; + uuid.D3 := (hex2bin[s[14]] shl 12) or (hex2bin[s[15]] shl 8) or (hex2bin[s[16]] shl 4) or hex2bin[s[17]]; + + uuid.D4[0] := (hex2bin[s[19]] shl 4) or hex2bin[s[20]]; + uuid.D4[1] := (hex2bin[s[21]] shl 4) or hex2bin[s[22]]; + uuid.D4[2] := (hex2bin[s[24]] shl 4) or hex2bin[s[25]]; + uuid.D4[3] := (hex2bin[s[26]] shl 4) or hex2bin[s[27]]; + uuid.D4[4] := (hex2bin[s[28]] shl 4) or hex2bin[s[29]]; + uuid.D4[5] := (hex2bin[s[30]] shl 4) or hex2bin[s[31]]; + uuid.D4[6] := (hex2bin[s[32]] shl 4) or hex2bin[s[33]]; + uuid.D4[7] := (hex2bin[s[34]] shl 4) or hex2bin[s[35]]; + Result := True; +end; + +function serialfromguid(ctx: TSuperRttiContext; const obj: ISuperObject; var Value: TValue): Boolean; +begin + case ObjectGetType(obj) of + stNull: + begin + FillChar(Value.GetReferenceToRawData^, SizeOf(TGUID), 0); + Result := True; + end; + stString: Result := UuidFromString(PSOChar(obj.AsString), Value.GetReferenceToRawData); + else + Result := False; + end; +end; + +function SOInvoke(const obj: TValue; const method: string; const params: ISuperObject; ctx: TSuperRttiContext): ISuperObject; overload; +var + owned: Boolean; +begin + if ctx = nil then + begin + ctx := TSuperRttiContext.Create; + owned := True; + end else + owned := False; + try + if TrySOInvoke(ctx, obj, method, params, Result) <> irSuccess then + raise Exception.Create('Invalid method call'); + finally + if owned then + ctx.Free; + end; +end; + +function SOInvoke(const obj: TValue; const method: string; const params: string; ctx: TSuperRttiContext): ISuperObject; overload; +begin + Result := SOInvoke(obj, method, so(params), ctx) +end; + +function TrySOInvoke(var ctx: TSuperRttiContext; const obj: TValue; + const method: string; const params: ISuperObject; + var Return: ISuperObject): TSuperInvokeResult; +var + t: TRttiInstanceType; + m: TRttiMethod; + a: TArray; + ps: TArray; + v: TValue; + index: ISuperObject; + + function GetParams: Boolean; + var + i: Integer; + begin + case ObjectGetType(params) of + stArray: + for i := 0 to Length(ps) - 1 do + if (pfOut in ps[i].Flags) then + TValue.Make(nil, ps[i].ParamType.Handle, a[i]) else + if not ctx.FromJson(ps[i].ParamType.Handle, params.AsArray[i], a[i]) then + Exit(False); + stObject: + for i := 0 to Length(ps) - 1 do + if (pfOut in ps[i].Flags) then + TValue.Make(nil, ps[i].ParamType.Handle, a[i]) else + if not ctx.FromJson(ps[i].ParamType.Handle, params.AsObject[ps[i].Name], a[i]) then + Exit(False); + stNull: ; + else + Exit(False); + end; + Result := True; + end; + + procedure SetParams; + var + i: Integer; + begin + case ObjectGetType(params) of + stArray: + for i := 0 to Length(ps) - 1 do + if (ps[i].Flags * [pfVar, pfOut]) <> [] then + params.AsArray[i] := ctx.ToJson(a[i], index); + stObject: + for i := 0 to Length(ps) - 1 do + if (ps[i].Flags * [pfVar, pfOut]) <> [] then + params.AsObject[ps[i].Name] := ctx.ToJson(a[i], index); + end; + end; + +begin + Result := irSuccess; + index := SO; + case obj.Kind of + tkClass: + begin + t := TRttiInstanceType(ctx.Context.GetType(obj.AsObject.ClassType)); + m := t.GetMethod(method); + if m = nil then Exit(irMethothodError); + ps := m.GetParameters; + SetLength(a, Length(ps)); + if not GetParams then Exit(irParamError); + if m.IsClassMethod then + begin + v := m.Invoke(obj.AsObject.ClassType, a); + Return := ctx.ToJson(v, index); + SetParams; + end else + begin + v := m.Invoke(obj, a); + Return := ctx.ToJson(v, index); + SetParams; + end; + end; + tkClassRef: + begin + t := TRttiInstanceType(ctx.Context.GetType(obj.AsClass)); + m := t.GetMethod(method); + if m = nil then Exit(irMethothodError); + ps := m.GetParameters; + SetLength(a, Length(ps)); + + if not GetParams then Exit(irParamError); + if m.IsClassMethod then + begin + v := m.Invoke(obj, a); + Return := ctx.ToJson(v, index); + SetParams; + end else + Exit(irError); + end; + else + Exit(irError); + end; +end; + +{$ENDIF} + +{ TSuperEnumerator } + +constructor TSuperEnumerator.Create(const obj: ISuperObject); +begin + FObj := obj; + FCount := -1; + if ObjectIsType(FObj, stObject) then + FObjEnum := FObj.AsObject.GetEnumerator else + FObjEnum := nil; +end; + +destructor TSuperEnumerator.Destroy; +begin + if FObjEnum <> nil then + FObjEnum.Free; +end; + +function TSuperEnumerator.MoveNext: Boolean; +begin + case ObjectGetType(FObj) of + stObject: Result := FObjEnum.MoveNext; + stArray: + begin + inc(FCount); + if FCount < FObj.AsArray.Length then + Result := True else + Result := False; + end; + else + Result := false; + end; +end; + +function TSuperEnumerator.GetCurrent: ISuperObject; +begin + case ObjectGetType(FObj) of + stObject: Result := FObjEnum.Current.Value; + stArray: Result := FObj.AsArray.GetO(FCount); + else + Result := FObj; + end; +end; + +{ TSuperObject } + +constructor TSuperObject.Create(jt: TSuperType); +begin + inherited Create; +{$IFDEF DEBUG} + InterlockedIncrement(debugcount); +{$ENDIF} + + FProcessing := false; + FDataPtr := nil; + FDataType := jt; + case FDataType of + stObject: FO.c_object := TSuperTableString.Create; + stArray: FO.c_array := TSuperArray.Create; + stString: FOString := ''; + else + FO.c_object := nil; + end; +end; + +constructor TSuperObject.Create(b: boolean); +begin + Create(stBoolean); + FO.c_boolean := b; +end; + +constructor TSuperObject.Create(i: SuperInt); +begin + Create(stInt); + FO.c_int := i; +end; + +constructor TSuperObject.Create(d: double); +begin + Create(stDouble); + FO.c_double := d; +end; + +constructor TSuperObject.CreateCurrency(c: Currency); +begin + Create(stCurrency); + FO.c_currency := c; +end; + +destructor TSuperObject.Destroy; +begin +{$IFDEF DEBUG} + InterlockedDecrement(debugcount); +{$ENDIF} + case FDataType of + stObject: FO.c_object.Free; + stArray: FO.c_array.Free; + end; + inherited; +end; + +function TSuperObject.Write(writer: TSuperWriter; indent: boolean; escape: boolean; level: integer): Integer; +function DoEscape(str: PSOChar; len: Integer): Integer; +var + pos, start_offset: Integer; + c: SOChar; + buf: array[0..5] of SOChar; +type + TByteChar = record + case integer of + 0: (a, b: Byte); + 1: (c: WideChar); + end; + begin + if str = nil then + begin + Result := 0; + exit; + end; + pos := 0; start_offset := 0; + with writer do + while pos < len do + begin + c := str[pos]; + case c of + #8,#9,#10,#12,#13,'"','\','/': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + + if(c = #8) then Append(ESC_BS, 2) + else if (c = #9) then Append(ESC_TAB, 2) + else if (c = #10) then Append(ESC_LF, 2) + else if (c = #12) then Append(ESC_FF, 2) + else if (c = #13) then Append(ESC_CR, 2) + else if (c = '"') then Append(ESC_QUOT, 2) + else if (c = '\') then Append(ESC_SL, 2) + else if (c = '/') then Append(ESC_SR, 2); + inc(pos); + start_offset := pos; + end; + else + if (SOIChar(c) > 255) then + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + buf[0] := '\'; + buf[1] := 'u'; + buf[2] := super_hex_chars[TByteChar(c).b shr 4]; + buf[3] := super_hex_chars[TByteChar(c).b and $f]; + buf[4] := super_hex_chars[TByteChar(c).a shr 4]; + buf[5] := super_hex_chars[TByteChar(c).a and $f]; + Append(@buf, 6); + inc(pos); + start_offset := pos; + end else + if (c < #32) or (c > #127) then + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + buf[0] := '\'; + buf[1] := 'u'; + buf[2] := '0'; + buf[3] := '0'; + buf[4] := super_hex_chars[ord(c) shr 4]; + buf[5] := super_hex_chars[ord(c) and $f]; + Append(buf, 6); + inc(pos); + start_offset := pos; + end else + inc(pos); + end; + end; + if(pos - start_offset > 0) then + writer.Append(str + start_offset, pos - start_offset); + Result := 0; + end; + +function DoMinimalEscape(str: PSOChar; len: Integer): Integer; +var + pos, start_offset: Integer; + c: SOChar; +type + TByteChar = record + case integer of + 0: (a, b: Byte); + 1: (c: WideChar); + end; + begin + if str = nil then + begin + Result := 0; + exit; + end; + pos := 0; start_offset := 0; + with writer do + while pos < len do + begin + c := str[pos]; + case c of + #0: + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_ZERO, 6); + inc(pos); + start_offset := pos; + end; + '"': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_QUOT, 2); + inc(pos); + start_offset := pos; + end; + '\': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_SL, 2); + inc(pos); + start_offset := pos; + end; + '/': + begin + if(pos - start_offset > 0) then + Append(str + start_offset, pos - start_offset); + Append(ESC_SR, 2); + inc(pos); + start_offset := pos; + end; + else + inc(pos); + end; + end; + if(pos - start_offset > 0) then + writer.Append(str + start_offset, pos - start_offset); + Result := 0; + end; + + + procedure _indent(i: shortint; r: boolean); + begin + inc(level, i); + if r then + with writer do + begin +{$IFDEF MSWINDOWS} + Append(TOK_CRLF, 2); +{$ELSE} + Append(TOK_LF, 1); +{$ENDIF} + for i := 0 to level - 1 do + Append(TOK_SP, 1); + end; + end; +var + k,j: Integer; + iter: TSuperObjectIter; + st: AnsiString; + val: ISuperObject; + fbuffer: array[0..31] of AnsiChar; +const + ENDSTR_A: PSOChar = '": '; + ENDSTR_B: PSOChar = '":'; +begin + + if FProcessing then + begin + Result := writer.Append(TOK_NULL, 4); + Exit; + end; + + FProcessing := true; + with writer do + try + case FDataType of + stObject: + if FO.c_object.FCount > 0 then + begin + k := 0; + Append(TOK_CBL, 1); + if indent then _indent(1, false); + if ObjectFindFirst(Self, iter) then + repeat + {$IFDEF SUPER_METHOD} + if (iter.val = nil) or not ObjectIsType(iter.val, stMethod) then + begin + {$ENDIF} + if (iter.val = nil) or (not iter.val.Processing) then + begin + if(k <> 0) then + Append(TOK_COM, 1); + if indent then _indent(0, true); + Append(TOK_DQT, 1); + if escape then + doEscape(PSOChar(iter.key), Length(iter.key)) else + DoMinimalEscape(PSOChar(iter.key), Length(iter.key)); + if indent then + Append(ENDSTR_A, 3) else + Append(ENDSTR_B, 2); + if(iter.val = nil) then + Append(TOK_NULL, 4) else + iter.val.write(writer, indent, escape, level); + inc(k); + end; + {$IFDEF SUPER_METHOD} + end; + {$ENDIF} + until not ObjectFindNext(iter); + ObjectFindClose(iter); + if indent then _indent(-1, true); + Result := Append(TOK_CBR, 1); + end else + Result := Append(TOK_OBJ, 2); + stBoolean: + begin + if (FO.c_boolean) then + Result := Append(TOK_TRUE, 4) else + Result := Append(TOK_FALSE, 5); + end; + stInt: + begin + str(FO.c_int, st); + Result := Append(PSOChar(SOString(st))); + end; + stDouble: + Result := Append(PSOChar(SOString(gcvt(FO.c_double, 15, fbuffer)))); + stCurrency: + begin + Result := Append(PSOChar(CurrToStr(FO.c_currency))); + end; + stString: + begin + Append(TOK_DQT, 1); + if escape then + doEscape(PSOChar(FOString), Length(FOString)) else + DoMinimalEscape(PSOChar(FOString), Length(FOString)); + Append(TOK_DQT, 1); + Result := 0; + end; + stArray: + if FO.c_array.FLength > 0 then + begin + Append(TOK_ARL, 1); + if indent then _indent(1, true); + k := 0; + j := 0; + while k < FO.c_array.FLength do + begin + + val := FO.c_array.GetO(k); + {$IFDEF SUPER_METHOD} + if not ObjectIsType(val, stMethod) then + begin + {$ENDIF} + if (val = nil) or (not val.Processing) then + begin + if (j <> 0) then + Append(TOK_COM, 1); + if(val = nil) then + Append(TOK_NULL, 4) else + val.write(writer, indent, escape, level); + inc(j); + end; + {$IFDEF SUPER_METHOD} + end; + {$ENDIF} + inc(k); + end; + if indent then _indent(-1, false); + Result := Append(TOK_ARR, 1); + end else + Result := Append(TOK_ARRAY, 2); + stNull: + Result := Append(TOK_NULL, 4); + else + Result := 0; + end; + finally + FProcessing := false; + end; +end; + +function TSuperObject.IsType(AType: TSuperType): boolean; +begin + Result := AType = FDataType; +end; + +function TSuperObject.AsBoolean: boolean; +begin + case FDataType of + stBoolean: Result := FO.c_boolean; + stInt: Result := (FO.c_int <> 0); + stDouble: Result := (FO.c_double <> 0); + stCurrency: Result := (FO.c_currency <> 0); + stString: Result := (Length(FOString) <> 0); + stNull: Result := False; + else + Result := True; + end; +end; + +function TSuperObject.AsInteger: SuperInt; +var + code: integer; + cint: SuperInt; +begin + case FDataType of + stInt: Result := FO.c_int; + stDouble: Result := round(FO.c_double); + stCurrency: Result := round(FO.c_currency); + stBoolean: Result := ord(FO.c_boolean); + stString: + begin + Val(FOString, cint, code); + if code = 0 then + Result := cint else + Result := 0; + end; + else + Result := 0; + end; +end; + +function TSuperObject.AsDouble: Double; +var + code: integer; + cdouble: double; +begin + case FDataType of + stDouble: Result := FO.c_double; + stCurrency: Result := FO.c_currency; + stInt: Result := FO.c_int; + stBoolean: Result := ord(FO.c_boolean); + stString: + begin + Val(FOString, cdouble, code); + if code = 0 then + Result := cdouble else + Result := 0.0; + end; + else + Result := 0.0; + end; +end; + +function TSuperObject.AsCurrency: Currency; +var + code: integer; + cdouble: double; +begin + case FDataType of + stDouble: Result := FO.c_double; + stCurrency: Result := FO.c_currency; + stInt: Result := FO.c_int; + stBoolean: Result := ord(FO.c_boolean); + stString: + begin + Val(FOString, cdouble, code); + if code = 0 then + Result := cdouble else + Result := 0.0; + end; + else + Result := 0.0; + end; +end; + +function TSuperObject.AsString: SOString; +begin + if FDataType = stString then + Result := FOString else + Result := AsJSon(false, false); +end; + +function TSuperObject.GetEnumerator: TSuperEnumerator; +begin + Result := TSuperEnumerator.Create(Self); +end; + +procedure TSuperObject.AfterConstruction; +begin + InterlockedDecrement(FRefCount); +end; + +procedure TSuperObject.BeforeDestruction; +begin + if RefCount <> 0 then + raise Exception.Create('Invalid pointer'); +end; + +function TSuperObject.AsArray: TSuperArray; +begin + if FDataType = stArray then + Result := FO.c_array else + Result := nil; +end; + +function TSuperObject.AsObject: TSuperTableString; +begin + if FDataType = stObject then + Result := FO.c_object else + Result := nil; +end; + +function TSuperObject.AsJSon(indent, escape: boolean): SOString; +var + pb: TSuperWriterString; +begin + pb := TSuperWriterString.Create; + try + if(Write(pb, indent, escape, 0) < 0) then + begin + Result := ''; + Exit; + end; + if pb.FBPos > 0 then + Result := pb.FBuf else + Result := ''; + finally + pb.Free; + end; +end; + +class function TSuperObject.ParseString(s: PSOChar; strict: Boolean; partial: boolean; const this: ISuperObject; + options: TSuperFindOptions; const put: ISuperObject; dt: TSuperType): ISuperObject; +var + tok: TSuperTokenizer; + obj: ISuperObject; +begin + tok := TSuperTokenizer.Create; + obj := ParseEx(tok, s, -1, strict, this, options, put, dt); + if(tok.err <> teSuccess) or (not partial and (s[tok.char_offset] <> #0)) then + Result := nil else + Result := obj; + tok.Free; +end; + +class function TSuperObject.ParseStream(stream: TStream; strict: Boolean; + partial: boolean; const this: ISuperObject; options: TSuperFindOptions; + const put: ISuperObject; dt: TSuperType): ISuperObject; +const + BUFFER_SIZE = 1024; +var + tok: TSuperTokenizer; + buffera: array[0..BUFFER_SIZE-1] of AnsiChar; + bufferw: array[0..BUFFER_SIZE-1] of SOChar; + bom: array[0..1] of byte; + unicode: boolean; + j, size: Integer; + st: string; +begin + st := ''; + tok := TSuperTokenizer.Create; + + if (stream.Read(bom, sizeof(bom)) = 2) and (bom[0] = $FF) and (bom[1] = $FE) then + begin + unicode := true; + size := stream.Read(bufferw, BUFFER_SIZE * SizeOf(SoChar)) div SizeOf(SoChar); + end else + begin + unicode := false; + stream.Seek(0, soFromBeginning); + size := stream.Read(buffera, BUFFER_SIZE); + end; + + while size > 0 do + begin + if not unicode then + for j := 0 to size - 1 do + bufferw[j] := SOChar(buffera[j]); + ParseEx(tok, bufferw, size, strict, this, options, put, dt); + + if tok.err = teContinue then + begin + if not unicode then + size := stream.Read(buffera, BUFFER_SIZE) else + size := stream.Read(bufferw, BUFFER_SIZE * SizeOf(SoChar)) div SizeOf(SoChar); + end else + Break; + end; + if(tok.err <> teSuccess) or (not partial and (st[tok.char_offset] <> #0)) then + Result := nil else + Result := tok.stack[tok.depth].current; + tok.Free; +end; + +class function TSuperObject.ParseFile(const FileName: string; strict: Boolean; + partial: boolean; const this: ISuperObject; options: TSuperFindOptions; + const put: ISuperObject; dt: TSuperType): ISuperObject; +var + stream: TFileStream; +begin + stream := TFileStream.Create(FileName, fmOpenRead, fmShareDenyWrite); + try + Result := ParseStream(stream, strict, partial, this, options, put, dt); + finally + stream.Free; + end; +end; + +class function TSuperObject.ParseEx(tok: TSuperTokenizer; str: PSOChar; len: integer; + strict: Boolean; const this: ISuperObject; options: TSuperFindOptions; const put: ISuperObject; dt: TSuperType): ISuperObject; + +const + spaces = [#32,#8,#9,#10,#12,#13]; + delimiters = ['"', '.', '[', ']', '{', '}', '(', ')', ',', ':', #0]; + reserved = delimiters + spaces; + path = ['a'..'z', 'A'..'Z', '.', '_']; + + function hexdigit(x: SOChar): byte; + begin + if x <= '9' then + Result := byte(x) - byte('0') else + Result := (byte(x) and 7) + 9; + end; + function min(v1, v2: integer): integer; begin if v1 < v2 then result := v1 else result := v2 end; + +var + obj: ISuperObject; + v: SOChar; +{$IFDEF SUPER_METHOD} + sm: TSuperMethod; +{$ENDIF} + numi: SuperInt; + numd: Double; + code: integer; + TokRec: PSuperTokenerSrec; + evalstack: integer; + p: PSOChar; + + function IsEndDelimiter(v: AnsiChar): Boolean; + begin + if tok.depth > 0 then + case tok.stack[tok.depth - 1].state of + tsArrayAdd: Result := v in [',', ']', #0]; + tsObjectValueAdd: Result := v in [',', '}', #0]; + else + Result := v = #0; + end else + Result := v = #0; + end; + +label out, redo_char; +begin + evalstack := 0; + obj := nil; + Result := nil; + TokRec := @tok.stack[tok.depth]; + + tok.char_offset := 0; + tok.err := teSuccess; + + repeat + if (tok.char_offset = len) then + begin + if (tok.depth = 0) and (TokRec^.state = tsEatws) and + (TokRec^.saved_state = tsFinish) then + tok.err := teSuccess else + tok.err := teContinue; + goto out; + end; + + v := str^; + + case v of + #10: + begin + inc(tok.line); + tok.col := 0; + end; + #9: inc(tok.col, 4); + else + inc(tok.col); + end; + +redo_char: + case TokRec^.state of + tsEatws: + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in spaces) then {nop} else + if (v = '/') then + begin + tok.pb.Reset; + tok.pb.Append(@v, 1); + TokRec^.state := tsCommentStart; + end else begin + TokRec^.state := TokRec^.saved_state; + goto redo_char; + end + end; + + tsStart: + case v of + '"', + '''': + begin + TokRec^.state := tsString; + tok.pb.Reset; + tok.quote_char := v; + end; + '-': + begin + TokRec^.state := tsNumber; + tok.pb.Reset; + tok.is_double := 0; + tok.floatcount := -1; + goto redo_char; + end; + + '0'..'9': + begin + if (tok.depth = 0) then + case ObjectGetType(this) of + stObject: + begin + TokRec^.state := tsIdentifier; + TokRec^.current := this; + goto redo_char; + end; + end; + TokRec^.state := tsNumber; + tok.pb.Reset; + tok.is_double := 0; + tok.floatcount := -1; + goto redo_char; + end; + '{': + begin + TokRec^.state := tsEatws; + TokRec^.saved_state := tsObjectFieldStart; + TokRec^.current := TSuperObject.Create(stObject); + end; + '[': + begin + TokRec^.state := tsEatws; + TokRec^.saved_state := tsArray; + TokRec^.current := TSuperObject.Create(stArray); + end; +{$IFDEF SUPER_METHOD} + '(': + begin + if (tok.depth = 0) and ObjectIsType(this, stMethod) then + begin + TokRec^.current := this; + TokRec^.state := tsParamValue; + end; + end; +{$ENDIF} + 'N', + 'n': + begin + TokRec^.state := tsNull; + tok.pb.Reset; + tok.st_pos := 0; + goto redo_char; + end; + 'T', + 't', + 'F', + 'f': + begin + TokRec^.state := tsBoolean; + tok.pb.Reset; + tok.st_pos := 0; + goto redo_char; + end; + else + TokRec^.state := tsIdentifier; + tok.pb.Reset; + goto redo_char; + end; + + tsFinish: + begin + if(tok.depth = 0) then goto out; + obj := TokRec^.current; + tok.ResetLevel(tok.depth); + dec(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + + tsNull: + begin + tok.pb.Append(@v, 1); + if (StrLComp(TOK_NULL, PSOChar(tok.pb.FBuf), min(tok.st_pos + 1, 4)) = 0) then + begin + if (tok.st_pos = 4) then + if (((SOIChar(v) < 256) and (AnsiChar(v) in path)) or (SOIChar(v) >= 256)) then + TokRec^.state := tsIdentifier else + begin + TokRec^.current := TSuperObject.Create(stNull); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end; + end else + begin + TokRec^.state := tsIdentifier; + tok.pb.FBuf[tok.st_pos] := #0; + dec(tok.pb.FBPos); + goto redo_char; + end; + inc(tok.st_pos); + end; + + tsCommentStart: + begin + if(v = '*') then + begin + TokRec^.state := tsComment; + end else + if (v = '/') then + begin + TokRec^.state := tsCommentEol; + end else + begin + tok.err := teParseComment; + goto out; + end; + tok.pb.Append(@v, 1); + end; + + tsComment: + begin + if(v = '*') then + TokRec^.state := tsCommentEnd; + tok.pb.Append(@v, 1); + end; + + tsCommentEol: + begin + if (v = #10) then + TokRec^.state := tsEatws else + tok.pb.Append(@v, 1); + end; + + tsCommentEnd: + begin + tok.pb.Append(@v, 1); + if (v = '/') then + TokRec^.state := tsEatws else + TokRec^.state := tsComment; + end; + + tsString: + begin + if (v = tok.quote_char) then + begin + TokRec^.current := TSuperObject.Create(SOString(tok.pb.GetString)); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsString; + TokRec^.state := tsStringEscape; + end else + begin + tok.pb.Append(@v, 1); + end + end; + + tsEvalProperty: + begin + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stObject); + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, TokRec^.current) + end else + if not ObjectIsType(TokRec^.current, stObject) then + begin + tok.err := teEvalObject; + goto out; + end; + tok.pb.Reset; + TokRec^.state := tsIdentifier; + goto redo_char; + end; + + tsEvalArray: + begin + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stArray); + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, TokRec^.current) + end else + if not ObjectIsType(TokRec^.current, stArray) then + begin + tok.err := teEvalArray; + goto out; + end; + tok.pb.Reset; + TokRec^.state := tsParamValue; + goto redo_char; + end; +{$IFDEF SUPER_METHOD} + tsEvalMethod: + begin + if ObjectIsType(TokRec^.current, stMethod) and assigned(TokRec^.current.AsMethod) then + begin + tok.pb.Reset; + TokRec^.obj := TSuperObject.Create(stArray); + TokRec^.state := tsMethodValue; + goto redo_char; + end else + begin + tok.err := teEvalMethod; + goto out; + end; + end; + + tsMethodValue: + begin + case v of + ')': + TokRec^.state := tsIdentifier; + else + if (tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + inc(evalstack); + TokRec^.state := tsMethodPut; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + end; + + tsMethodPut: + begin + TokRec^.obj.AsArray.Add(obj); + case v of + ',': + begin + tok.pb.Reset; + TokRec^.saved_state := tsMethodValue; + TokRec^.state := tsEatws; + end; + ')': + begin + if TokRec^.obj.AsArray.Length = 1 then + TokRec^.obj := TokRec^.obj.AsArray.GetO(0); + dec(evalstack); + tok.pb.Reset; + TokRec^.saved_state := tsIdentifier; + TokRec^.state := tsEatws; + end; + else + tok.err := teEvalMethod; + goto out; + end; + end; +{$ENDIF} + tsParamValue: + begin + case v of + ']': + TokRec^.state := tsIdentifier; + else + if (tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + inc(evalstack); + TokRec^.state := tsParamPut; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + end; + + tsParamPut: + begin + dec(evalstack); + TokRec^.obj := obj; + tok.pb.Reset; + TokRec^.saved_state := tsIdentifier; + TokRec^.state := tsEatws; + if v <> ']' then + begin + tok.err := teEvalArray; + goto out; + end; + end; + + tsIdentifier: + begin + if (this = nil) then + begin + if (SOIChar(v) < 256) and IsEndDelimiter(AnsiChar(v)) then + begin + if not strict then + begin + tok.pb.TrimRight; + TokRec^.current := TSuperObject.Create(tok.pb.Fbuf); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end else + begin + tok.err := teParseString; + goto out; + end; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsIdentifier; + TokRec^.state := tsStringEscape; + end else + tok.pb.Append(@v, 1); + end else + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in reserved) then + begin + TokRec^.gparent := TokRec^.parent; + if TokRec^.current = nil then + TokRec^.parent := this else + TokRec^.parent := TokRec^.current; + + case ObjectGetType(TokRec^.parent) of + stObject: + case v of + '.': + begin + TokRec^.state := tsEvalProperty; + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + end; + '[': + begin + TokRec^.state := tsEvalArray; + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + end; + '(': + begin + TokRec^.state := tsEvalMethod; + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + end; + else + if tok.pb.FBPos > 0 then + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + if (foPutValue in options) and (evalstack = 0) then + begin + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, put); + TokRec^.current := put + end else + if (foDelete in options) and (evalstack = 0) then + begin + TokRec^.current := TokRec^.parent.AsObject.Delete(tok.pb.Fbuf); + end else + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(dt); + TokRec^.parent.AsObject.PutO(tok.pb.Fbuf, TokRec^.current); + end; + TokRec^.current := TokRec^.parent.AsObject.GetO(tok.pb.Fbuf); + TokRec^.state := tsFinish; + goto redo_char; + end; + stArray: + begin + if TokRec^.obj <> nil then + begin + if not ObjectIsType(TokRec^.obj, stInt) or (TokRec^.obj.AsInteger < 0) then + begin + tok.err := teEvalInt; + TokRec^.obj := nil; + goto out; + end; + numi := TokRec^.obj.AsInteger; + TokRec^.obj := nil; + + TokRec^.current := TokRec^.parent.AsArray.GetO(numi); + case v of + '.': + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stObject); + TokRec^.parent.AsArray.PutO(numi, TokRec^.current); + end else + if (TokRec^.current = nil) then + begin + tok.err := teEvalObject; + goto out; + end; + '[': + begin + if (TokRec^.current = nil) and (foCreatePath in options) then + begin + TokRec^.current := TSuperObject.Create(stArray); + TokRec^.parent.AsArray.Add(TokRec^.current); + end else + if (TokRec^.current = nil) then + begin + tok.err := teEvalArray; + goto out; + end; + TokRec^.state := tsEvalArray; + end; + '(': TokRec^.state := tsEvalMethod; + else + if (foPutValue in options) and (evalstack = 0) then + begin + TokRec^.parent.AsArray.PutO(numi, put); + TokRec^.current := put; + end else + if (foDelete in options) and (evalstack = 0) then + begin + TokRec^.current := TokRec^.parent.AsArray.Delete(numi); + end else + TokRec^.current := TokRec^.parent.AsArray.GetO(numi); + TokRec^.state := tsFinish; + goto redo_char + end; + end else + begin + case v of + '.': + begin + if (foPutValue in options) then + begin + TokRec^.current := TSuperObject.Create(stObject); + TokRec^.parent.AsArray.Add(TokRec^.current); + end else + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1); + end; + '[': + begin + if (foPutValue in options) then + begin + TokRec^.current := TSuperObject.Create(stArray); + TokRec^.parent.AsArray.Add(TokRec^.current); + end else + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1); + TokRec^.state := tsEvalArray; + end; + '(': + begin + if not (foPutValue in options) then + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1) else + TokRec^.current := nil; + + TokRec^.state := tsEvalMethod; + end; + else + if (foPutValue in options) and (evalstack = 0) then + begin + TokRec^.parent.AsArray.Add(put); + TokRec^.current := put; + end else + if tok.pb.FBPos = 0 then + TokRec^.current := TokRec^.parent.AsArray.GetO(TokRec^.parent.AsArray.FLength - 1); + TokRec^.state := tsFinish; + goto redo_char + end; + end; + end; +{$IFDEF SUPER_METHOD} + stMethod: + case v of + '.': + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.obj := nil; + end; + '[': + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.state := tsEvalArray; + TokRec^.obj := nil; + end; + '(': + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.state := tsEvalMethod; + TokRec^.obj := nil; + end; + else + if not (foPutValue in options) or (evalstack > 0) then + begin + TokRec^.current := nil; + sm := TokRec^.parent.AsMethod; + sm(TokRec^.gparent, TokRec^.obj, TokRec^.current); + TokRec^.obj := nil; + TokRec^.state := tsFinish; + goto redo_char + end else + begin + tok.err := teEvalMethod; + TokRec^.obj := nil; + goto out; + end; + end; +{$ENDIF} + end; + end else + tok.pb.Append(@v, 1); + end; + end; + + tsStringEscape: + case v of + 'b', + 'n', + 'r', + 't', + 'f': + begin + if(v = 'b') then tok.pb.Append(TOK_BS, 1) + else if(v = 'n') then tok.pb.Append(TOK_LF, 1) + else if(v = 'r') then tok.pb.Append(TOK_CR, 1) + else if(v = 't') then tok.pb.Append(TOK_TAB, 1) + else if(v = 'f') then tok.pb.Append(TOK_FF, 1); + TokRec^.state := TokRec^.saved_state; + end; + 'u': + begin + tok.ucs_char := 0; + tok.st_pos := 0; + TokRec^.state := tsEscapeUnicode; + end; + 'x': + begin + tok.ucs_char := 0; + tok.st_pos := 0; + TokRec^.state := tsEscapeHexadecimal; + end + else + tok.pb.Append(@v, 1); + TokRec^.state := TokRec^.saved_state; + end; + + tsEscapeUnicode: + begin + if ((SOIChar(v) < 256) and (AnsiChar(v) in super_hex_chars_set)) then + begin + inc(tok.ucs_char, (Word(hexdigit(v)) shl ((3-tok.st_pos)*4))); + inc(tok.st_pos); + if (tok.st_pos = 4) then + begin + tok.pb.Append(@tok.ucs_char, 1); + TokRec^.state := TokRec^.saved_state; + end + end else + begin + tok.err := teParseString; + goto out; + end + end; + tsEscapeHexadecimal: + begin + if ((SOIChar(v) < 256) and (AnsiChar(v) in super_hex_chars_set)) then + begin + inc(tok.ucs_char, (Word(hexdigit(v)) shl ((1-tok.st_pos)*4))); + inc(tok.st_pos); + if (tok.st_pos = 2) then + begin + tok.pb.Append(@tok.ucs_char, 1); + TokRec^.state := TokRec^.saved_state; + end + end else + begin + tok.err := teParseString; + goto out; + end + end; + tsBoolean: + begin + tok.pb.Append(@v, 1); + if (StrLComp('true', PSOChar(tok.pb.FBuf), min(tok.st_pos + 1, 4)) = 0) then + begin + if (tok.st_pos = 4) then + if (((SOIChar(v) < 256) and (AnsiChar(v) in path)) or (SOIChar(v) >= 256)) then + TokRec^.state := tsIdentifier else + begin + TokRec^.current := TSuperObject.Create(true); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end + end else + if (StrLComp('false', PSOChar(tok.pb.FBuf), min(tok.st_pos + 1, 5)) = 0) then + begin + if (tok.st_pos = 5) then + if (((SOIChar(v) < 256) and (AnsiChar(v) in path)) or (SOIChar(v) >= 256)) then + TokRec^.state := tsIdentifier else + begin + TokRec^.current := TSuperObject.Create(false); + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end + end else + begin + TokRec^.state := tsIdentifier; + tok.pb.FBuf[tok.st_pos] := #0; + dec(tok.pb.FBPos); + goto redo_char; + end; + inc(tok.st_pos); + end; + + tsNumber: + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in super_number_chars_set) then + begin + tok.pb.Append(@v, 1); + if (SOIChar(v) < 256) then + case v of + '.': begin + tok.is_double := 1; + tok.floatcount := 0; + end; + 'e','E': + begin + tok.is_double := 1; + tok.floatcount := -1; + end; + '0'..'9': + begin + + if (tok.is_double = 1) and (tok.floatcount >= 0) then + begin + inc(tok.floatcount); + if tok.floatcount > 4 then + tok.floatcount := -1; + end; + end; + end; + end else + begin + if (tok.is_double = 0) then + begin + val(tok.pb.FBuf, numi, code); + if ObjectIsType(this, stArray) then + begin + if (foPutValue in options) and (evalstack = 0) then + begin + this.AsArray.PutO(numi, put); + TokRec^.current := put; + end else + if (foDelete in options) and (evalstack = 0) then + TokRec^.current := this.AsArray.Delete(numi) else + TokRec^.current := this.AsArray.GetO(numi); + end else + TokRec^.current := TSuperObject.Create(numi); + + end else + if (tok.is_double <> 0) then + begin + if tok.floatcount >= 0 then + begin + p := tok.pb.FBuf; + while p^ <> '.' do inc(p); + for code := 0 to tok.floatcount - 1 do + begin + p^ := p[1]; + inc(p); + end; + p^ := #0; + val(tok.pb.FBuf, numi, code); + case tok.floatcount of + 0: numi := numi * 10000; + 1: numi := numi * 1000; + 2: numi := numi * 100; + 3: numi := numi * 10; + end; + TokRec^.current := TSuperObject.CreateCurrency(PCurrency(@numi)^); + end else + begin + val(tok.pb.FBuf, numd, code); + TokRec^.current := TSuperObject.Create(numd); + end; + end else + begin + tok.err := teParseNumber; + goto out; + end; + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + goto redo_char; + end + end; + + tsArray: + begin + if (v = ']') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + begin + if(tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + TokRec^.state := tsArrayAdd; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end + end; + + tsArrayAdd: + begin + TokRec^.current.AsArray.Add(obj); + TokRec^.saved_state := tsArraySep; + TokRec^.state := tsEatws; + goto redo_char; + end; + + tsArraySep: + begin + if (v = ']') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (v = ',') then + begin + TokRec^.saved_state := tsArray; + TokRec^.state := tsEatws; + end else + begin + tok.err := teParseArray; + goto out; + end + end; + + tsObjectFieldStart: + begin + if (v = '}') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (SOIChar(v) < 256) and (AnsiChar(v) in ['"', '''']) then + begin + tok.quote_char := v; + tok.pb.Reset; + TokRec^.state := tsObjectField; + end else + if not((SOIChar(v) < 256) and ((AnsiChar(v) in reserved) or strict)) then + begin + TokRec^.state := tsObjectUnquotedField; + tok.pb.Reset; + goto redo_char; + end else + begin + tok.err := teParseObjectKeyName; + goto out; + end + end; + + tsObjectField: + begin + if (v = tok.quote_char) then + begin + TokRec^.field_name := tok.pb.FBuf; + TokRec^.saved_state := tsObjectFieldEnd; + TokRec^.state := tsEatws; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsObjectField; + TokRec^.state := tsStringEscape; + end else + begin + tok.pb.Append(@v, 1); + end + end; + + tsObjectUnquotedField: + begin + if (SOIChar(v) < 256) and (AnsiChar(v) in [':', #0]) then + begin + TokRec^.field_name := tok.pb.FBuf; + TokRec^.saved_state := tsObjectFieldEnd; + TokRec^.state := tsEatws; + goto redo_char; + end else + if (v = '\') then + begin + TokRec^.saved_state := tsObjectUnquotedField; + TokRec^.state := tsStringEscape; + end else + tok.pb.Append(@v, 1); + end; + + tsObjectFieldEnd: + begin + if (v = ':') then + begin + TokRec^.saved_state := tsObjectValue; + TokRec^.state := tsEatws; + end else + begin + tok.err := teParseObjectKeySep; + goto out; + end + end; + + tsObjectValue: + begin + if (tok.depth >= SUPER_TOKENER_MAX_DEPTH-1) then + begin + tok.err := teDepth; + goto out; + end; + TokRec^.state := tsObjectValueAdd; + inc(tok.depth); + tok.ResetLevel(tok.depth); + TokRec := @tok.stack[tok.depth]; + goto redo_char; + end; + + tsObjectValueAdd: + begin + TokRec^.current.AsObject.PutO(TokRec^.field_name, obj); + TokRec^.field_name := ''; + TokRec^.saved_state := tsObjectSep; + TokRec^.state := tsEatws; + goto redo_char; + end; + + tsObjectSep: + begin + if (v = '}') then + begin + TokRec^.saved_state := tsFinish; + TokRec^.state := tsEatws; + end else + if (v = ',') then + begin + TokRec^.saved_state := tsObjectFieldStart; + TokRec^.state := tsEatws; + end else + begin + tok.err := teParseObjectValueSep; + goto out; + end + end; + end; + inc(str); + inc(tok.char_offset); + until v = #0; + + if(TokRec^.state <> tsFinish) and + (TokRec^.saved_state <> tsFinish) then + tok.err := teParseEof; + + out: + if(tok.err in [teSuccess]) then + begin +{$IFDEF SUPER_METHOD} + if (foCallMethod in options) and ObjectIsType(TokRec^.current, stMethod) and assigned(TokRec^.current.AsMethod) then + begin + sm := TokRec^.current.AsMethod; + sm(TokRec^.parent, put, Result); + end else +{$ENDIF} + Result := TokRec^.current; + end else + Result := nil; +end; + +procedure TSuperObject.PutO(const path: SOString; const Value: ISuperObject); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], Value); +end; + +procedure TSuperObject.PutB(const path: SOString; Value: Boolean); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +procedure TSuperObject.PutD(const path: SOString; Value: Double); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +procedure TSuperObject.PutC(const path: SOString; Value: Currency); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.CreateCurrency(Value)); +end; + +procedure TSuperObject.PutI(const path: SOString; Value: SuperInt); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +procedure TSuperObject.PutS(const path: SOString; const Value: SOString); +begin + ParseString(PSOChar(path), true, False, self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; + +function TSuperObject.QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; +begin + if GetInterface(IID, Obj) then + Result := 0 + else + Result := E_NOINTERFACE; +end; + +function TSuperObject.SaveTo(stream: TStream; indent, escape: boolean): integer; +var + pb: TSuperWriterStream; +begin + if escape then + pb := TSuperAnsiWriterStream.Create(stream) else + pb := TSuperUnicodeWriterStream.Create(stream); + + if(Write(pb, indent, escape, 0) < 0) then + begin + pb.Reset; + pb.Free; + Result := 0; + Exit; + end; + Result := stream.Size; + pb.Free; +end; + +function TSuperObject.CalcSize(indent, escape: boolean): integer; +var + pb: TSuperWriterFake; +begin + pb := TSuperWriterFake.Create; + if(Write(pb, indent, escape, 0) < 0) then + begin + pb.Free; + Result := 0; + Exit; + end; + Result := pb.FSize; + pb.Free; +end; + +function TSuperObject.SaveTo(socket: Integer; indent, escape: boolean): integer; +var + pb: TSuperWriterSock; +begin + pb := TSuperWriterSock.Create(socket); + if(Write(pb, indent, escape, 0) < 0) then + begin + pb.Free; + Result := 0; + Exit; + end; + Result := pb.FSize; + pb.Free; +end; + +constructor TSuperObject.Create(const s: SOString); +begin + Create(stString); + FOString := s; +end; + +procedure TSuperObject.Clear(all: boolean); +begin + if FProcessing then exit; + FProcessing := true; + try + case FDataType of + stBoolean: FO.c_boolean := false; + stDouble: FO.c_double := 0.0; + stCurrency: FO.c_currency := 0.0; + stInt: FO.c_int := 0; + stObject: FO.c_object.Clear(all); + stArray: FO.c_array.Clear(all); + stString: FOString := ''; +{$IFDEF SUPER_METHOD} + stMethod: FO.c_method := nil; +{$ENDIF} + end; + finally + FProcessing := false; + end; +end; + +procedure TSuperObject.Pack(all: boolean = false); +begin + if FProcessing then exit; + FProcessing := true; + try + case FDataType of + stObject: FO.c_object.Pack(all); + stArray: FO.c_array.Pack(all); + end; + finally + FProcessing := false; + end; +end; + +function TSuperObject.GetN(const path: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, true, self); + if Result = nil then + Result := TSuperObject.Create(stNull); +end; + +procedure TSuperObject.PutN(const path: SOString; const Value: ISuperObject); +begin + if Value = nil then + ParseString(PSOChar(path), False, True, self, [foCreatePath, foPutValue], TSuperObject.Create(stNull)) else + ParseString(PSOChar(path), False, True, self, [foCreatePath, foPutValue], Value); +end; + +function TSuperObject.Delete(const path: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, true, self, [foDelete]); +end; + +function TSuperObject.Clone: ISuperObject; +var + ite: TSuperObjectIter; + arr: TSuperArray; + j: integer; +begin + case FDataType of + stBoolean: Result := TSuperObject.Create(FO.c_boolean); + stDouble: Result := TSuperObject.Create(FO.c_double); + stCurrency: Result := TSuperObject.CreateCurrency(FO.c_currency); + stInt: Result := TSuperObject.Create(FO.c_int); + stString: Result := TSuperObject.Create(FOString); +{$IFDEF SUPER_METHOD} + stMethod: Result := TSuperObject.Create(FO.c_method); +{$ENDIF} + stObject: + begin + Result := TSuperObject.Create(stObject); + if ObjectFindFirst(self, ite) then + with Result.AsObject do + repeat + PutO(ite.key, ite.val.Clone); + until not ObjectFindNext(ite); + ObjectFindClose(ite); + end; + stArray: + begin + Result := TSuperObject.Create(stArray); + arr := AsArray; + with Result.AsArray do + for j := 0 to arr.Length - 1 do + Add(arr.GetO(j).Clone); + end; + else + Result := nil; + end; +end; + +procedure TSuperObject.Merge(const obj: ISuperObject; reference: boolean); +var + prop1, prop2: ISuperObject; + ite: TSuperObjectIter; + arr: TSuperArray; + j: integer; +begin + if ObjectIsType(obj, FDataType) then + case FDataType of + stBoolean: FO.c_boolean := obj.AsBoolean; + stDouble: FO.c_double := obj.AsDouble; + stCurrency: FO.c_currency := obj.AsCurrency; + stInt: FO.c_int := obj.AsInteger; + stString: FOString := obj.AsString; +{$IFDEF SUPER_METHOD} + stMethod: FO.c_method := obj.AsMethod; +{$ENDIF} + stObject: + begin + if ObjectFindFirst(obj, ite) then + with FO.c_object do + repeat + prop1 := FO.c_object.GetO(ite.key); + if (prop1 <> nil) and (ite.val <> nil) and (prop1.DataType = ite.val.DataType) then + prop1.Merge(ite.val) else + if reference then + PutO(ite.key, ite.val) else + PutO(ite.key, ite.val.Clone); + until not ObjectFindNext(ite); + ObjectFindClose(ite); + end; + stArray: + begin + arr := obj.AsArray; + with FO.c_array do + for j := 0 to arr.Length - 1 do + begin + prop1 := GetO(j); + prop2 := arr.GetO(j); + if (prop1 <> nil) and (prop2 <> nil) and (prop1.DataType = prop2.DataType) then + prop1.Merge(prop2) else + if reference then + PutO(j, prop2) else + PutO(j, prop2.Clone); + end; + end; + end; +end; + +procedure TSuperObject.Merge(const str: SOString); +begin + Merge(TSuperObject.ParseString(PSOChar(str), False), true); +end; + +class function TSuperObject.NewInstance: TObject; +begin + Result := inherited NewInstance; + TSuperObject(Result).FRefCount := 1; +end; + +function TSuperObject.ForcePath(const path: SOString; dataType: TSuperType = stObject): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self, [foCreatePath], nil, dataType); +end; + +function TSuperObject.Format(const str: SOString; BeginSep: SOChar; EndSep: SOChar): SOString; +var + p1, p2: PSOChar; +begin + Result := ''; + p2 := PSOChar(str); + p1 := p2; + while true do + if p2^ = BeginSep then + begin + if p2 > p1 then + Result := Result + Copy(p1, 0, p2-p1); + inc(p2); + p1 := p2; + while true do + if p2^ = EndSep then Break else + if p2^ = #0 then Exit else + inc(p2); + Result := Result + GetS(copy(p1, 0, p2-p1)); + inc(p2); + p1 := p2; + end + else if p2^ = #0 then + begin + if p2 > p1 then + Result := Result + Copy(p1, 0, p2-p1); + Break; + end else + inc(p2); +end; + +function TSuperObject.GetO(const path: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self); +end; + +function TSuperObject.GetA(const path: SOString): TSuperArray; +var + obj: ISuperObject; +begin + obj := ParseString(PSOChar(path), False, True, Self); + if obj <> nil then + Result := obj.AsArray else + Result := nil; +end; + +function TSuperObject.GetB(const path: SOString): Boolean; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsBoolean else + Result := false; +end; + +function TSuperObject.GetD(const path: SOString): Double; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsDouble else + Result := 0.0; +end; + +function TSuperObject.GetC(const path: SOString): Currency; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsCurrency else + Result := 0.0; +end; + +function TSuperObject.GetI(const path: SOString): SuperInt; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsInteger else + Result := 0; +end; + +function TSuperObject.GetDataPtr: Pointer; +begin + Result := FDataPtr; +end; + +function TSuperObject.GetDataType: TSuperType; +begin + Result := FDataType +end; + +function TSuperObject.GetS(const path: SOString): SOString; +var + obj: ISuperObject; +begin + obj := GetO(path); + if obj <> nil then + Result := obj.AsString else + Result := ''; +end; + +function TSuperObject.SaveTo(const FileName: string; indent, escape: boolean): integer; +var + stream: TFileStream; +begin + stream := TFileStream.Create(FileName, fmCreate); + try + Result := SaveTo(stream, indent, escape); + finally + stream.Free; + end; +end; + +function TSuperObject.Validate(const rules: SOString; const defs: SOString = ''; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; +begin + Result := Validate(TSuperObject.ParseString(PSOChar(rules), False), TSuperObject.ParseString(PSOChar(defs), False), callback, sender); +end; + +function TSuperObject.Validate(const rules: ISuperObject; const defs: ISuperObject = nil; callback: TSuperOnValidateError = nil; sender: Pointer = nil): boolean; +type + TDataType = (dtUnknown, dtStr, dtInt, dtFloat, dtNumber, dtText, dtBool, + dtMap, dtSeq, dtScalar, dtAny); +var + datatypes: ISuperObject; + names: ISuperObject; + + function FindInheritedProperty(const prop: PSOChar; p: ISuperObject): ISuperObject; + var + o: ISuperObject; + e: TSuperAvlEntry; + begin + o := p[prop]; + if o <> nil then + result := o else + begin + o := p['inherit']; + if (o <> nil) and ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + Result := FindInheritedProperty(prop, e.Value) else + Result := nil; + end else + Result := nil; + end; + end; + + function FindDataType(o: ISuperObject): TDataType; + var + e: TSuperAvlEntry; + obj: ISuperObject; + begin + obj := FindInheritedProperty('type', o); + if obj <> nil then + begin + e := datatypes.AsObject.Search(obj.AsString); + if e <> nil then + Result := TDataType(e.Value.AsInteger) else + Result := dtUnknown; + end else + Result := dtUnknown; + end; + + procedure GetNames(o: ISuperObject); + var + obj: ISuperObject; + f: TSuperObjectIter; + begin + obj := o['name']; + if ObjectIsType(obj, stString) then + names[obj.AsString] := o; + + case FindDataType(o) of + dtMap: + begin + obj := o['mapping']; + if ObjectIsType(obj, stObject) then + begin + if ObjectFindFirst(obj, f) then + repeat + if ObjectIsType(f.val, stObject) then + GetNames(f.val); + until not ObjectFindNext(f); + ObjectFindClose(f); + end; + end; + dtSeq: + begin + obj := o['sequence']; + if ObjectIsType(obj, stObject) then + GetNames(obj); + end; + end; + end; + + function FindInheritedField(const prop: SOString; p: ISuperObject): ISuperObject; + var + o: ISuperObject; + e: TSuperAvlEntry; + begin + o := p['mapping']; + if ObjectIsType(o, stObject) then + begin + o := o.AsObject.GetO(prop); + if o <> nil then + begin + Result := o; + Exit; + end; + end; + + o := p['inherit']; + if ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + Result := FindInheritedField(prop, e.Value) else + Result := nil; + end else + Result := nil; + end; + + function InheritedFieldExist(const obj: ISuperObject; p: ISuperObject; const name: SOString = ''): boolean; + var + o: ISuperObject; + e: TSuperAvlEntry; + j: TSuperAvlIterator; + begin + Result := true; + o := p['mapping']; + if ObjectIsType(o, stObject) then + begin + j := TSuperAvlIterator.Create(o.AsObject); + try + j.First; + e := j.GetIter; + while e <> nil do + begin + if obj.AsObject.Search(e.Name) = nil then + begin + Result := False; + if assigned(callback) then + callback(sender, veFieldNotFound, name + '.' + e.Name); + end; + j.Next; + e := j.GetIter; + end; + + finally + j.Free; + end; + end; + + o := p['inherit']; + if ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + Result := InheritedFieldExist(obj, e.Value, name) and Result; + end; + end; + + function getInheritedBool(f: PSOChar; p: ISuperObject; default: boolean = false): boolean; + var + o: ISuperObject; + begin + o := FindInheritedProperty(f, p); + case ObjectGetType(o) of + stBoolean: Result := o.AsBoolean; + stNull: Result := Default; + else + Result := default; + if assigned(callback) then + callback(sender, veRuleMalformated, f); + end; + end; + + procedure GetInheritedFieldList(list: ISuperObject; p: ISuperObject); + var + o: ISuperObject; + e: TSuperAvlEntry; + i: TSuperAvlIterator; + begin + Result := true; + o := p['mapping']; + if ObjectIsType(o, stObject) then + begin + i := TSuperAvlIterator.Create(o.AsObject); + try + i.First; + e := i.GetIter; + while e <> nil do + begin + if list.AsObject.Search(e.Name) = nil then + list[e.Name] := e.Value; + i.Next; + e := i.GetIter; + end; + + finally + i.Free; + end; + end; + + o := p['inherit']; + if ObjectIsType(o, stString) then + begin + e := names.AsObject.Search(o.AsString); + if (e <> nil) then + GetInheritedFieldList(list, e.Value); + end; + end; + + function CheckEnum(o: ISuperObject; p: ISuperObject; name: SOString = ''): boolean; + var + enum: ISuperObject; + i: integer; + begin + Result := false; + enum := FindInheritedProperty('enum', p); + case ObjectGetType(enum) of + stArray: + for i := 0 to enum.AsArray.Length - 1 do + if (o.AsString = enum.AsArray[i].AsString) then + begin + Result := true; + exit; + end; + stNull: Result := true; + else + Result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + Exit; + end; + + if (not Result) and assigned(callback) then + callback(sender, veValueNotInEnum, name); + end; + + function CheckLength(len: integer; p: ISuperObject; const objpath: SOString): boolean; + var + length, o: ISuperObject; + begin + result := true; + length := FindInheritedProperty('length', p); + case ObjectGetType(length) of + stObject: + begin + o := length.AsObject.GetO('min'); + if (o <> nil) and (o.AsInteger > len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + o := length.AsObject.GetO('max'); + if (o <> nil) and (o.AsInteger < len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + o := length.AsObject.GetO('minex'); + if (o <> nil) and (o.AsInteger >= len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + o := length.AsObject.GetO('maxex'); + if (o <> nil) and (o.AsInteger <= len) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidLength, objpath); + end; + end; + stNull: ; + else + Result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + end; + end; + + function CheckRange(obj: ISuperObject; p: ISuperObject; const objpath: SOString): boolean; + var + length, o: ISuperObject; + begin + result := true; + length := FindInheritedProperty('range', p); + case ObjectGetType(length) of + stObject: + begin + o := length.AsObject.GetO('min'); + if (o <> nil) and (o.Compare(obj) = cpGreat) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + o := length.AsObject.GetO('max'); + if (o <> nil) and (o.Compare(obj) = cpLess) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + o := length.AsObject.GetO('minex'); + if (o <> nil) and (o.Compare(obj) in [cpGreat, cpEqu]) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + o := length.AsObject.GetO('maxex'); + if (o <> nil) and (o.Compare(obj) in [cpLess, cpEqu]) then + begin + Result := false; + if assigned(callback) then + callback(sender, veInvalidRange, objpath); + end; + end; + stNull: ; + else + Result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + end; + end; + + + function process(o: ISuperObject; p: ISuperObject; objpath: SOString = ''): boolean; + var + ite: TSuperAvlIterator; + ent: TSuperAvlEntry; + p2, o2, sequence: ISuperObject; + s: SOString; + i: integer; + uniquelist, fieldlist: ISuperObject; + begin + Result := true; + if (o = nil) then + begin + if getInheritedBool('required', p) then + begin + if assigned(callback) then + callback(sender, veFieldIsRequired, objpath); + result := false; + end; + end else + case FindDataType(p) of + dtStr: + case ObjectGetType(o) of + stString: + begin + Result := Result and CheckLength(Length(o.AsString), p, objpath); + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtBool: + case ObjectGetType(o) of + stBoolean: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtInt: + case ObjectGetType(o) of + stInt: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtFloat: + case ObjectGetType(o) of + stDouble, stCurrency: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtMap: + case ObjectGetType(o) of + stObject: + begin + // all objects have and match a rule ? + ite := TSuperAvlIterator.Create(o.AsObject); + try + ite.First; + ent := ite.GetIter; + while ent <> nil do + begin + p2 := FindInheritedField(ent.Name, p); + if ObjectIsType(p2, stObject) then + result := process(ent.Value, p2, objpath + '.' + ent.Name) and result else + begin + if assigned(callback) then + callback(sender, veUnexpectedField, objpath + '.' + ent.Name); + result := false; // field have no rule + end; + ite.Next; + ent := ite.GetIter; + end; + finally + ite.Free; + end; + + // all expected field exists ? + Result := InheritedFieldExist(o, p, objpath) and Result; + end; + stNull: {nop}; + else + result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + end; + dtSeq: + case ObjectGetType(o) of + stArray: + begin + sequence := FindInheritedProperty('sequence', p); + if sequence <> nil then + case ObjectGetType(sequence) of + stObject: + begin + for i := 0 to o.AsArray.Length - 1 do + result := process(o.AsArray.GetO(i), sequence, objpath + '[' + IntToStr(i) + ']') and result; + if getInheritedBool('unique', sequence) then + begin + // type is unique ? + uniquelist := TSuperObject.Create(stObject); + try + for i := 0 to o.AsArray.Length - 1 do + begin + s := o.AsArray.GetO(i).AsString; + if (s <> '') then + begin + if uniquelist.AsObject.Search(s) = nil then + uniquelist[s] := nil else + begin + Result := False; + if Assigned(callback) then + callback(sender, veDuplicateEntry, objpath + '[' + IntToStr(i) + ']'); + end; + end; + end; + finally + uniquelist := nil; + end; + end; + + // field is unique ? + if (FindDataType(sequence) = dtMap) then + begin + fieldlist := TSuperObject.Create(stObject); + try + GetInheritedFieldList(fieldlist, sequence); + ite := TSuperAvlIterator.Create(fieldlist.AsObject); + try + ite.First; + ent := ite.GetIter; + while ent <> nil do + begin + if getInheritedBool('unique', ent.Value) then + begin + uniquelist := TSuperObject.Create(stObject); + try + for i := 0 to o.AsArray.Length - 1 do + begin + o2 := o.AsArray.GetO(i); + if o2 <> nil then + begin + s := o2.AsObject.GetO(ent.Name).AsString; + if (s <> '') then + if uniquelist.AsObject.Search(s) = nil then + uniquelist[s] := nil else + begin + Result := False; + if Assigned(callback) then + callback(sender, veDuplicateEntry, objpath + '[' + IntToStr(i) + '].' + ent.name); + end; + end; + end; + finally + uniquelist := nil; + end; + end; + ite.Next; + ent := ite.GetIter; + end; + finally + ite.Free; + end; + finally + fieldlist := nil; + end; + end; + + + end; + stNull: {nop}; + else + result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + end; + Result := Result and CheckLength(o.AsArray.Length, p, objpath); + + end; + else + result := false; + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + end; + dtNumber: + case ObjectGetType(o) of + stInt, + stDouble, stCurrency: + begin + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtText: + case ObjectGetType(o) of + stInt, + stDouble, + stCurrency, + stString: + begin + result := result and CheckLength(Length(o.AsString), p, objpath); + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtScalar: + case ObjectGetType(o) of + stBoolean, + stDouble, + stCurrency, + stInt, + stString: + begin + result := result and CheckLength(Length(o.AsString), p, objpath); + Result := Result and CheckRange(o, p, objpath); + end; + else + if assigned(callback) then + callback(sender, veInvalidDataType, objpath); + result := false; + end; + dtAny:; + else + if assigned(callback) then + callback(sender, veRuleMalformated, objpath); + result := false; + end; + Result := Result and CheckEnum(o, p, objpath) + + end; +var + j: integer; + +begin + Result := False; + datatypes := TSuperObject.Create(stObject); + names := TSuperObject.Create; + try + datatypes.I['str'] := ord(dtStr); + datatypes.I['int'] := ord(dtInt); + datatypes.I['float'] := ord(dtFloat); + datatypes.I['number'] := ord(dtNumber); + datatypes.I['text'] := ord(dtText); + datatypes.I['bool'] := ord(dtBool); + datatypes.I['map'] := ord(dtMap); + datatypes.I['seq'] := ord(dtSeq); + datatypes.I['scalar'] := ord(dtScalar); + datatypes.I['any'] := ord(dtAny); + + if ObjectIsType(defs, stArray) then + for j := 0 to defs.AsArray.Length - 1 do + if ObjectIsType(defs.AsArray[j], stObject) then + GetNames(defs.AsArray[j]) else + begin + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + Exit; + end; + + + if ObjectIsType(rules, stObject) then + GetNames(rules) else + begin + if assigned(callback) then + callback(sender, veRuleMalformated, ''); + Exit; + end; + + Result := process(self, rules); + + finally + datatypes := nil; + names := nil; + end; +end; + +function TSuperObject._AddRef: Integer; stdcall; +begin + Result := InterlockedIncrement(FRefCount); +end; + +function TSuperObject._Release: Integer; stdcall; +begin + Result := InterlockedDecrement(FRefCount); + if Result = 0 then + Destroy; +end; + +function TSuperObject.Compare(const str: SOString): TSuperCompareResult; +begin + Result := Compare(TSuperObject.ParseString(PSOChar(str), False)); +end; + +function TSuperObject.Compare(const obj: ISuperObject): TSuperCompareResult; + function GetIntCompResult(const i: int64): TSuperCompareResult; + begin + if i < 0 then result := cpLess else + if i = 0 then result := cpEqu else + Result := cpGreat; + end; + + function GetDblCompResult(const d: double): TSuperCompareResult; + begin + if d < 0 then result := cpLess else + if d = 0 then result := cpEqu else + Result := cpGreat; + end; + +begin + case DataType of + stBoolean: + case ObjectGetType(obj) of + stBoolean: Result := GetIntCompResult(ord(FO.c_boolean) - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(ord(FO.c_boolean) - obj.AsDouble); + stCurrency:Result := GetDblCompResult(ord(FO.c_boolean) - obj.AsCurrency); + stInt: Result := GetIntCompResult(ord(FO.c_boolean) - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stDouble: + case ObjectGetType(obj) of + stBoolean: Result := GetDblCompResult(FO.c_double - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(FO.c_double - obj.AsDouble); + stCurrency:Result := GetDblCompResult(FO.c_double - obj.AsCurrency); + stInt: Result := GetDblCompResult(FO.c_double - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stCurrency: + case ObjectGetType(obj) of + stBoolean: Result := GetDblCompResult(FO.c_currency - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(FO.c_currency - obj.AsDouble); + stCurrency:Result := GetDblCompResult(FO.c_currency - obj.AsCurrency); + stInt: Result := GetDblCompResult(FO.c_currency - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stInt: + case ObjectGetType(obj) of + stBoolean: Result := GetIntCompResult(FO.c_int - ord(obj.AsBoolean)); + stDouble: Result := GetDblCompResult(FO.c_int - obj.AsDouble); + stCurrency:Result := GetDblCompResult(FO.c_int - obj.AsCurrency); + stInt: Result := GetIntCompResult(FO.c_int - obj.AsInteger); + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + stString: + case ObjectGetType(obj) of + stBoolean, + stDouble, + stCurrency, + stInt, + stString: Result := GetIntCompResult(StrComp(PSOChar(AsString), PSOChar(obj.AsString))); + else + Result := cpError; + end; + else + Result := cpError; + end; +end; + +{$IFDEF SUPER_METHOD} +function TSuperObject.AsMethod: TSuperMethod; +begin + if FDataType = stMethod then + Result := FO.c_method else + Result := nil; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +constructor TSuperObject.Create(m: TSuperMethod); +begin + Create(stMethod); + FO.c_method := m; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperObject.GetM(const path: SOString): TSuperMethod; +var + v: ISuperObject; +begin + v := ParseString(PSOChar(path), False, True, Self); + if (v <> nil) and (ObjectGetType(v) = stMethod) then + Result := v.AsMethod else + Result := nil; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +procedure TSuperObject.PutM(const path: SOString; Value: TSuperMethod); +begin + ParseString(PSOChar(path), False, True, Self, [foCreatePath, foPutValue], TSuperObject.Create(Value)); +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperObject.call(const path: SOString; const param: ISuperObject): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self, [foCallMethod], param); +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperObject.call(const path, param: SOString): ISuperObject; +begin + Result := ParseString(PSOChar(path), False, True, Self, [foCallMethod], TSuperObject.ParseString(PSOChar(param), False)); +end; +{$ENDIF} + +function TSuperObject.GetProcessing: boolean; +begin + Result := FProcessing; +end; + +procedure TSuperObject.SetDataPtr(const Value: Pointer); +begin + FDataPtr := Value; +end; + +procedure TSuperObject.SetProcessing(value: boolean); +begin + FProcessing := value; +end; + +{ TSuperArray } + +function TSuperArray.Add(const Data: ISuperObject): Integer; +begin + Result := FLength; + PutO(Result, data); +end; + +function TSuperArray.Delete(index: Integer): ISuperObject; +begin + if (Index >= 0) and (Index < FLength) then + begin + Result := FArray^[index]; + FArray^[index] := nil; + Dec(FLength); + if Index < FLength then + begin + Move(FArray^[index + 1], FArray^[index], + (FLength - index) * SizeOf(Pointer)); + Pointer(FArray^[FLength]) := nil; + end; + end; +end; + +procedure TSuperArray.Insert(index: Integer; const value: ISuperObject); +begin + if (Index >= 0) then + if (index < FLength) then + begin + if FLength = FSize then + Expand(index); + if Index < FLength then + Move(FArray^[index], FArray^[index + 1], + (FLength - index) * SizeOf(Pointer)); + Pointer(FArray^[index]) := nil; + FArray^[index] := value; + Inc(FLength); + end else + PutO(index, value); +end; + +procedure TSuperArray.Clear(all: boolean); +var + j: Integer; +begin + for j := 0 to FLength - 1 do + if FArray^[j] <> nil then + begin + if all then + FArray^[j].Clear(all); + FArray^[j] := nil; + end; + FLength := 0; +end; + +procedure TSuperArray.Pack(all: boolean); +var + PackedCount, StartIndex, EndIndex, j: Integer; +begin + if FLength > 0 then + begin + PackedCount := 0; + StartIndex := 0; + repeat + while (StartIndex < FLength) and (FArray^[StartIndex] = nil) do + Inc(StartIndex); + if StartIndex < FLength then + begin + EndIndex := StartIndex; + while (EndIndex < FLength) and (FArray^[EndIndex] <> nil) do + Inc(EndIndex); + + Dec(EndIndex); + + if StartIndex > PackedCount then + Move(FArray^[StartIndex], FArray^[PackedCount], (EndIndex - StartIndex + 1) * SizeOf(Pointer)); + + Inc(PackedCount, EndIndex - StartIndex + 1); + StartIndex := EndIndex + 1; + end; + until StartIndex >= FLength; + FillChar(FArray^[PackedCount], (FLength - PackedCount) * sizeof(Pointer), 0); + FLength := PackedCount; + if all then + for j := 0 to FLength - 1 do + FArray^[j].Pack(all); + end; +end; + +constructor TSuperArray.Create; +begin + inherited Create; + FSize := SUPER_ARRAY_LIST_DEFAULT_SIZE; + FLength := 0; + GetMem(FArray, sizeof(Pointer) * FSize); + FillChar(FArray^, sizeof(Pointer) * FSize, 0); +end; + +destructor TSuperArray.Destroy; +begin + Clear; + FreeMem(FArray); + inherited; +end; + +procedure TSuperArray.Expand(max: Integer); +var + new_size: Integer; +begin + if (max < FSize) then + Exit; + if max < (FSize shl 1) then + new_size := (FSize shl 1) else + new_size := max + 1; + ReallocMem(FArray, new_size * sizeof(Pointer)); + FillChar(FArray^[FSize], (new_size - FSize) * sizeof(Pointer), 0); + FSize := new_size; +end; + +function TSuperArray.GetO(const index: Integer): ISuperObject; +begin + if(index >= FLength) then + Result := nil else + Result := FArray^[index]; +end; + +function TSuperArray.GetB(const index: integer): Boolean; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsBoolean else + Result := false; +end; + +function TSuperArray.GetD(const index: integer): Double; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsDouble else + Result := 0.0; +end; + +function TSuperArray.GetI(const index: integer): SuperInt; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsInteger else + Result := 0; +end; + +function TSuperArray.GetS(const index: integer): SOString; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsString else + Result := ''; +end; + +procedure TSuperArray.PutO(const index: Integer; const Value: ISuperObject); +begin + Expand(index); + FArray^[index] := value; + if(FLength <= index) then FLength := index + 1; +end; + +function TSuperArray.GetN(const index: integer): ISuperObject; +begin + Result := GetO(index); + if Result = nil then + Result := TSuperObject.Create(stNull); +end; + +procedure TSuperArray.PutN(const index: integer; const Value: ISuperObject); +begin + if Value <> nil then + PutO(index, Value) else + PutO(index, TSuperObject.Create(stNull)); +end; + +procedure TSuperArray.PutB(const index: integer; Value: Boolean); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +procedure TSuperArray.PutD(const index: integer; Value: Double); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +function TSuperArray.GetC(const index: integer): Currency; +var + obj: ISuperObject; +begin + obj := GetO(index); + if obj <> nil then + Result := obj.AsCurrency else + Result := 0.0; +end; + +procedure TSuperArray.PutC(const index: integer; Value: Currency); +begin + PutO(index, TSuperObject.CreateCurrency(Value)); +end; + +procedure TSuperArray.PutI(const index: integer; Value: SuperInt); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +procedure TSuperArray.PutS(const index: integer; const Value: SOString); +begin + PutO(index, TSuperObject.Create(Value)); +end; + +{$IFDEF SUPER_METHOD} +function TSuperArray.GetM(const index: integer): TSuperMethod; +var + v: ISuperObject; +begin + v := GetO(index); + if (ObjectGetType(v) = stMethod) then + Result := v.AsMethod else + Result := nil; +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +procedure TSuperArray.PutM(const index: integer; Value: TSuperMethod); +begin + PutO(index, TSuperObject.Create(Value)); +end; +{$ENDIF} + +{ TSuperWriterString } + +function TSuperWriterString.Append(buf: PSOChar; Size: Integer): Integer; + function max(a, b: Integer): integer; begin if a > b then Result := a else Result := b end; +begin + Result := size; + if Size > 0 then + begin + if (FSize - FBPos <= size) then + begin + FSize := max(FSize * 2, FBPos + size + 8); + ReallocMem(FBuf, FSize * SizeOf(SOChar)); + end; + // fast move + case size of + 1: FBuf[FBPos] := buf^; + 2: PInteger(@FBuf[FBPos])^ := PInteger(buf)^; + 4: PInt64(@FBuf[FBPos])^ := PInt64(buf)^; + else + move(buf^, FBuf[FBPos], size * SizeOf(SOChar)); + end; + inc(FBPos, size); + FBuf[FBPos] := #0; + end; +end; + +function TSuperWriterString.Append(buf: PSOChar): Integer; +begin + Result := Append(buf, strlen(buf)); +end; + +constructor TSuperWriterString.Create; +begin + inherited; + FSize := 32; + FBPos := 0; + GetMem(FBuf, FSize * SizeOf(SOChar)); +end; + +destructor TSuperWriterString.Destroy; +begin + inherited; + if FBuf <> nil then + FreeMem(FBuf) +end; + +function TSuperWriterString.GetString: SOString; +begin + SetString(Result, FBuf, FBPos); +end; + +procedure TSuperWriterString.Reset; +begin + FBuf[0] := #0; + FBPos := 0; +end; + +procedure TSuperWriterString.TrimRight; +begin + while (FBPos > 0) and (FBuf[FBPos-1] < #256) and (AnsiChar(FBuf[FBPos-1]) in [#32, #13, #10]) do + begin + dec(FBPos); + FBuf[FBPos] := #0; + end; +end; + +{ TSuperWriterStream } + +function TSuperWriterStream.Append(buf: PSOChar): Integer; +begin + Result := Append(buf, StrLen(buf)); +end; + +constructor TSuperWriterStream.Create(AStream: TStream); +begin + inherited Create; + FStream := AStream; +end; + +procedure TSuperWriterStream.Reset; +begin + FStream.Size := 0; +end; + +{ TSuperWriterStream } + +function TSuperAnsiWriterStream.Append(buf: PSOChar; Size: Integer): Integer; +var + Buffer: array[0..1023] of AnsiChar; + pBuffer: PAnsiChar; + i: Integer; +begin + if Size = 1 then + Result := FStream.Write(buf^, Size) else + begin + if Size > SizeOf(Buffer) then + GetMem(pBuffer, Size) else + pBuffer := @Buffer; + try + for i := 0 to Size - 1 do + pBuffer[i] := AnsiChar(buf[i]); + Result := FStream.Write(pBuffer^, Size); + finally + if pBuffer <> @Buffer then + FreeMem(pBuffer); + end; + end; +end; + +{ TSuperUnicodeWriterStream } + +function TSuperUnicodeWriterStream.Append(buf: PSOChar; Size: Integer): Integer; +begin + Result := FStream.Write(buf^, Size * 2); +end; + +{ TSuperWriterFake } + +function TSuperWriterFake.Append(buf: PSOChar; Size: Integer): Integer; +begin + inc(FSize, Size); + Result := FSize; +end; + +function TSuperWriterFake.Append(buf: PSOChar): Integer; +begin + inc(FSize, Strlen(buf)); + Result := FSize; +end; + +constructor TSuperWriterFake.Create; +begin + inherited Create; + FSize := 0; +end; + +procedure TSuperWriterFake.Reset; +begin + FSize := 0; +end; + +{ TSuperWriterSock } + +function TSuperWriterSock.Append(buf: PSOChar; Size: Integer): Integer; +var + Buffer: array[0..1023] of AnsiChar; + pBuffer: PAnsiChar; + i: Integer; +begin + if Size = 1 then +{$IFDEF FPC} + Result := fpsend(FSocket, buf, size, 0) else +{$ELSE} + Result := send(FSocket, buf^, size, 0) else +{$ENDIF} + begin + if Size > SizeOf(Buffer) then + GetMem(pBuffer, Size) else + pBuffer := @Buffer; + try + for i := 0 to Size - 1 do + pBuffer[i] := AnsiChar(buf[i]); +{$IFDEF FPC} + Result := fpsend(FSocket, pBuffer, size, 0); +{$ELSE} + Result := send(FSocket, pBuffer^, size, 0); +{$ENDIF} + finally + if pBuffer <> @Buffer then + FreeMem(pBuffer); + end; + end; + inc(FSize, Result); +end; + +function TSuperWriterSock.Append(buf: PSOChar): Integer; +begin + Result := Append(buf, StrLen(buf)); +end; + +constructor TSuperWriterSock.Create(ASocket: Integer); +begin + inherited Create; + FSocket := ASocket; + FSize := 0; +end; + +procedure TSuperWriterSock.Reset; +begin + FSize := 0; +end; + +{ TSuperTokenizer } + +constructor TSuperTokenizer.Create; +begin + pb := TSuperWriterString.Create; + line := 1; + col := 0; + Reset; +end; + +destructor TSuperTokenizer.Destroy; +begin + Reset; + pb.Free; + inherited; +end; + +procedure TSuperTokenizer.Reset; +var + i: integer; +begin + for i := depth downto 0 do + ResetLevel(i); + depth := 0; + err := teSuccess; +end; + +procedure TSuperTokenizer.ResetLevel(adepth: integer); +begin + stack[adepth].state := tsEatws; + stack[adepth].saved_state := tsStart; + stack[adepth].current := nil; + stack[adepth].field_name := ''; + stack[adepth].obj := nil; + stack[adepth].parent := nil; + stack[adepth].gparent := nil; +end; + +{ TSuperAvlTree } + +constructor TSuperAvlTree.Create; +begin + FRoot := nil; + FCount := 0; +end; + +destructor TSuperAvlTree.Destroy; +begin + Clear; + inherited; +end; + +function TSuperAvlTree.IsEmpty: boolean; +begin + result := FRoot = nil; +end; + +function TSuperAvlTree.balance(bal: TSuperAvlEntry): TSuperAvlEntry; +var + deep, old: TSuperAvlEntry; + bf: integer; +begin + if (bal.FBf > 0) then + begin + deep := bal.FGt; + if (deep.FBf < 0) then + begin + old := bal; + bal := deep.FLt; + old.FGt := bal.FLt; + deep.FLt := bal.FGt; + bal.FLt := old; + bal.FGt := deep; + bf := bal.FBf; + if (bf <> 0) then + begin + if (bf > 0) then + begin + old.FBf := -1; + deep.FBf := 0; + end else + begin + deep.FBf := 1; + old.FBf := 0; + end; + bal.FBf := 0; + end else + begin + old.FBf := 0; + deep.FBf := 0; + end; + end else + begin + bal.FGt := deep.FLt; + deep.FLt := bal; + if (deep.FBf = 0) then + begin + deep.FBf := -1; + bal.FBf := 1; + end else + begin + deep.FBf := 0; + bal.FBf := 0; + end; + bal := deep; + end; + end else + begin + (* "Less than" subtree is deeper. *) + + deep := bal.FLt; + if (deep.FBf > 0) then + begin + old := bal; + bal := deep.FGt; + old.FLt := bal.FGt; + deep.FGt := bal.FLt; + bal.FGt := old; + bal.FLt := deep; + + bf := bal.FBf; + if (bf <> 0) then + begin + if (bf < 0) then + begin + old.FBf := 1; + deep.FBf := 0; + end else + begin + deep.FBf := -1; + old.FBf := 0; + end; + bal.FBf := 0; + end else + begin + old.FBf := 0; + deep.FBf := 0; + end; + end else + begin + bal.FLt := deep.FGt; + deep.FGt := bal; + if (deep.FBf = 0) then + begin + deep.FBf := 1; + bal.FBf := -1; + end else + begin + deep.FBf := 0; + bal.FBf := 0; + end; + bal := deep; + end; + end; + Result := bal; +end; + +function TSuperAvlTree.Insert(h: TSuperAvlEntry): TSuperAvlEntry; +var + unbal, parentunbal, hh, parent: TSuperAvlEntry; + depth, unbaldepth: longint; + cmp: integer; + unbalbf: integer; + branch: TSuperAvlBitArray; + p: Pointer; +begin + inc(FCount); + h.FLt := nil; + h.FGt := nil; + h.FBf := 0; + branch := []; + + if (FRoot = nil) then + FRoot := h + else + begin + unbal := nil; + parentunbal := nil; + depth := 0; + unbaldepth := 0; + hh := FRoot; + parent := nil; + repeat + if (hh.FBf <> 0) then + begin + unbal := hh; + parentunbal := parent; + unbaldepth := depth; + end; + if hh.FHash <> h.FHash then + begin + if hh.FHash < h.FHash then cmp := -1 else + if hh.FHash > h.FHash then cmp := 1 else + cmp := 0; + end else + cmp := CompareNodeNode(h, hh); + if (cmp = 0) then + begin + Result := hh; + //exchange data + p := hh.Ptr; + hh.FPtr := h.Ptr; + h.FPtr := p; + doDeleteEntry(h, false); + dec(FCount); + exit; + end; + parent := hh; + if (cmp > 0) then + begin + hh := hh.FGt; + include(branch, depth); + end else + begin + hh := hh.FLt; + exclude(branch, depth); + end; + inc(depth); + until (hh = nil); + + if (cmp < 0) then + parent.FLt := h else + parent.FGt := h; + + depth := unbaldepth; + + if (unbal = nil) then + hh := FRoot + else + begin + if depth in branch then + cmp := 1 else + cmp := -1; + inc(depth); + unbalbf := unbal.FBf; + if (cmp < 0) then + dec(unbalbf) else + inc(unbalbf); + if cmp < 0 then + hh := unbal.FLt else + hh := unbal.FGt; + if ((unbalbf <> -2) and (unbalbf <> 2)) then + begin + unbal.FBf := unbalbf; + unbal := nil; + end; + end; + + if (hh <> nil) then + while (h <> hh) do + begin + if depth in branch then + cmp := 1 else + cmp := -1; + inc(depth); + if (cmp < 0) then + begin + hh.FBf := -1; + hh := hh.FLt; + end else (* cmp > 0 *) + begin + hh.FBf := 1; + hh := hh.FGt; + end; + end; + + if (unbal <> nil) then + begin + unbal := balance(unbal); + if (parentunbal = nil) then + FRoot := unbal + else + begin + depth := unbaldepth - 1; + if depth in branch then + cmp := 1 else + cmp := -1; + if (cmp < 0) then + parentunbal.FLt := unbal else + parentunbal.FGt := unbal; + end; + end; + end; + result := h; +end; + +function TSuperAvlTree.Search(const k: SOString; st: TSuperAvlSearchTypes): TSuperAvlEntry; +var + cmp, target_cmp: integer; + match_h, h: TSuperAvlEntry; + ha: Cardinal; +begin + ha := TSuperAvlEntry.Hash(k); + + match_h := nil; + h := FRoot; + + if (stLess in st) then + target_cmp := 1 else + if (stGreater in st) then + target_cmp := -1 else + target_cmp := 0; + + while (h <> nil) do + begin + if h.FHash < ha then cmp := -1 else + if h.FHash > ha then cmp := 1 else + cmp := 0; + + if cmp = 0 then + cmp := CompareKeyNode(PSOChar(k), h); + if (cmp = 0) then + begin + if (stEqual in st) then + begin + match_h := h; + break; + end; + cmp := -target_cmp; + end + else + if (target_cmp <> 0) then + if ((cmp xor target_cmp) and SUPER_AVL_MASK_HIGH_BIT) = 0 then + match_h := h; + if cmp < 0 then + h := h.FLt else + h := h.FGt; + end; + result := match_h; +end; + +function TSuperAvlTree.Delete(const k: SOString): ISuperObject; +var + depth, rm_depth: longint; + branch: TSuperAvlBitArray; + h, parent, child, path, rm, parent_rm: TSuperAvlEntry; + cmp, cmp_shortened_sub_with_path, reduced_depth, bf: integer; + ha: Cardinal; +begin + ha := TSuperAvlEntry.Hash(k); + cmp_shortened_sub_with_path := 0; + branch := []; + + depth := 0; + h := FRoot; + parent := nil; + while true do + begin + if (h = nil) then + exit; + if h.FHash < ha then cmp := -1 else + if h.FHash > ha then cmp := 1 else + cmp := 0; + + if cmp = 0 then + cmp := CompareKeyNode(k, h); + if (cmp = 0) then + break; + parent := h; + if (cmp > 0) then + begin + h := h.FGt; + include(branch, depth) + end else + begin + h := h.FLt; + exclude(branch, depth) + end; + inc(depth); + cmp_shortened_sub_with_path := cmp; + end; + rm := h; + parent_rm := parent; + rm_depth := depth; + + if (h.FBf < 0) then + begin + child := h.FLt; + exclude(branch, depth); + cmp := -1; + end else + begin + child := h.FGt; + include(branch, depth); + cmp := 1; + end; + inc(depth); + + if (child <> nil) then + begin + cmp := -cmp; + repeat + parent := h; + h := child; + if (cmp < 0) then + begin + child := h.FLt; + exclude(branch, depth); + end else + begin + child := h.FGt; + include(branch, depth); + end; + inc(depth); + until (child = nil); + + if (parent = rm) then + cmp_shortened_sub_with_path := -cmp else + cmp_shortened_sub_with_path := cmp; + + if cmp > 0 then + child := h.FLt else + child := h.FGt; + end; + + if (parent = nil) then + FRoot := child else + if (cmp_shortened_sub_with_path < 0) then + parent.FLt := child else + parent.FGt := child; + + if parent = rm then + path := h else + path := parent; + + if (h <> rm) then + begin + h.FLt := rm.FLt; + h.FGt := rm.FGt; + h.FBf := rm.FBf; + if (parent_rm = nil) then + FRoot := h + else + begin + depth := rm_depth - 1; + if (depth in branch) then + parent_rm.FGt := h else + parent_rm.FLt := h; + end; + end; + + if (path <> nil) then + begin + h := FRoot; + parent := nil; + depth := 0; + while (h <> path) do + begin + if (depth in branch) then + begin + child := h.FGt; + h.FGt := parent; + end else + begin + child := h.FLt; + h.FLt := parent; + end; + inc(depth); + parent := h; + h := child; + end; + + reduced_depth := 1; + cmp := cmp_shortened_sub_with_path; + while true do + begin + if (reduced_depth <> 0) then + begin + bf := h.FBf; + if (cmp < 0) then + inc(bf) else + dec(bf); + if ((bf = -2) or (bf = 2)) then + begin + h := balance(h); + bf := h.FBf; + end else + h.FBf := bf; + reduced_depth := integer(bf = 0); + end; + if (parent = nil) then + break; + child := h; + h := parent; + dec(depth); + if depth in branch then + cmp := 1 else + cmp := -1; + if (cmp < 0) then + begin + parent := h.FLt; + h.FLt := child; + end else + begin + parent := h.FGt; + h.FGt := child; + end; + end; + FRoot := h; + end; + if rm <> nil then + begin + Result := rm.GetValue; + doDeleteEntry(rm, false); + dec(FCount); + end; +end; + +procedure TSuperAvlTree.Pack(all: boolean); +var + node1, node2: TSuperAvlEntry; + list: TList; + i: Integer; +begin + node1 := FRoot; + list := TList.Create; + while node1 <> nil do + begin + if (node1.FLt = nil) then + begin + node2 := node1.FGt; + if (node1.FPtr = nil) then + list.Add(node1) else + if all then + node1.Value.Pack(all); + end + else + begin + node2 := node1.FLt; + node1.FLt := node2.FGt; + node2.FGt := node1; + end; + node1 := node2; + end; + for i := 0 to list.Count - 1 do + Delete(TSuperAvlEntry(list[i]).FName); + list.Free; +end; + +procedure TSuperAvlTree.Clear(all: boolean); +var + node1, node2: TSuperAvlEntry; +begin + node1 := FRoot; + while node1 <> nil do + begin + if (node1.FLt = nil) then + begin + node2 := node1.FGt; + doDeleteEntry(node1, all); + end + else + begin + node2 := node1.FLt; + node1.FLt := node2.FGt; + node2.FGt := node1; + end; + node1 := node2; + end; + FRoot := nil; + FCount := 0; +end; + +function TSuperAvlTree.CompareKeyNode(const k: SOString; h: TSuperAvlEntry): integer; +begin + Result := StrComp(PSOChar(k), PSOChar(h.FName)); +end; + +function TSuperAvlTree.CompareNodeNode(node1, node2: TSuperAvlEntry): integer; +begin + Result := StrComp(PSOChar(node1.FName), PSOChar(node2.FName)); +end; + +{ TSuperAvlIterator } + +(* Initialize depth to invalid value, to indicate iterator is +** invalid. (Depth is zero-base.) It's not necessary to initialize +** iterators prior to passing them to the "start" function. +*) + +constructor TSuperAvlIterator.Create(tree: TSuperAvlTree); +begin + FDepth := not 0; + FTree := tree; +end; + +procedure TSuperAvlIterator.Search(const k: SOString; st: TSuperAvlSearchTypes); +var + h: TSuperAvlEntry; + d: longint; + cmp, target_cmp: integer; + ha: Cardinal; +begin + ha := TSuperAvlEntry.Hash(k); + h := FTree.FRoot; + d := 0; + FDepth := not 0; + if (h = nil) then + exit; + + if (stLess in st) then + target_cmp := 1 else + if (stGreater in st) then + target_cmp := -1 else + target_cmp := 0; + + while true do + begin + if h.FHash < ha then cmp := -1 else + if h.FHash > ha then cmp := 1 else + cmp := 0; + + if cmp = 0 then + cmp := FTree.CompareKeyNode(k, h); + if (cmp = 0) then + begin + if (stEqual in st) then + begin + FDepth := d; + break; + end; + cmp := -target_cmp; + end + else + if (target_cmp <> 0) then + if ((cmp xor target_cmp) and SUPER_AVL_MASK_HIGH_BIT) = 0 then + FDepth := d; + if cmp < 0 then + h := h.FLt else + h := h.FGt; + if (h = nil) then + break; + if (cmp > 0) then + include(FBranch, d) else + exclude(FBranch, d); + FPath[d] := h; + inc(d); + end; +end; + +procedure TSuperAvlIterator.First; +var + h: TSuperAvlEntry; +begin + h := FTree.FRoot; + FDepth := not 0; + FBranch := []; + while (h <> nil) do + begin + if (FDepth <> not 0) then + FPath[FDepth] := h; + inc(FDepth); + h := h.FLt; + end; +end; + +procedure TSuperAvlIterator.Last; +var + h: TSuperAvlEntry; +begin + h := FTree.FRoot; + FDepth := not 0; + FBranch := [0..SUPER_AVL_MAX_DEPTH - 1]; + while (h <> nil) do + begin + if (FDepth <> not 0) then + FPath[FDepth] := h; + inc(FDepth); + h := h.FGt; + end; +end; + +function TSuperAvlIterator.MoveNext: boolean; +begin + if FDepth = not 0 then + First else + Next; + Result := GetIter <> nil; +end; + +function TSuperAvlIterator.GetIter: TSuperAvlEntry; +begin + if (FDepth = not 0) then + begin + result := nil; + exit; + end; + if FDepth = 0 then + Result := FTree.FRoot else + Result := FPath[FDepth - 1]; +end; + +procedure TSuperAvlIterator.Next; +var + h: TSuperAvlEntry; +begin + if (FDepth <> not 0) then + begin + if FDepth = 0 then + h := FTree.FRoot.FGt else + h := FPath[FDepth - 1].FGt; + + if (h = nil) then + repeat + if (FDepth = 0) then + begin + FDepth := not 0; + break; + end; + dec(FDepth); + until (not (FDepth in FBranch)) + else + begin + include(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + while true do + begin + h := h.FLt; + if (h = nil) then + break; + exclude(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + end; + end; + end; +end; + +procedure TSuperAvlIterator.Prior; +var + h: TSuperAvlEntry; +begin + if (FDepth <> not 0) then + begin + if FDepth = 0 then + h := FTree.FRoot.FLt else + h := FPath[FDepth - 1].FLt; + if (h = nil) then + repeat + if (FDepth = 0) then + begin + FDepth := not 0; + break; + end; + dec(FDepth); + until (FDepth in FBranch) + else + begin + exclude(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + while true do + begin + h := h.FGt; + if (h = nil) then + break; + include(FBranch, FDepth); + FPath[FDepth] := h; + inc(FDepth); + end; + end; + end; +end; + +procedure TSuperAvlTree.doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); +begin + Entry.Free; +end; + +function TSuperAvlTree.GetEnumerator: TSuperAvlIterator; +begin + Result := TSuperAvlIterator.Create(Self); +end; + +{ TSuperAvlEntry } + +constructor TSuperAvlEntry.Create(const AName: SOString; Obj: Pointer); +begin + FName := AName; + FPtr := Obj; + FHash := Hash(FName); +end; + +function TSuperAvlEntry.GetValue: ISuperObject; +begin + Result := ISuperObject(FPtr) +end; + +class function TSuperAvlEntry.Hash(const k: SOString): Cardinal; +var + h: cardinal; + i: Integer; +begin + h := 0; +{$Q-} + for i := 1 to Length(k) do + h := h*129 + ord(k[i]) + $9e370001; +{$Q+} + Result := h; +end; + +procedure TSuperAvlEntry.SetValue(const val: ISuperObject); +begin + ISuperObject(FPtr) := val; +end; + +{ TSuperTableString } + +function TSuperTableString.GetValues: ISuperObject; +var + ite: TSuperAvlIterator; + obj: TSuperAvlEntry; +begin + Result := TSuperObject.Create(stArray); + ite := TSuperAvlIterator.Create(Self); + try + ite.First; + obj := ite.GetIter; + while obj <> nil do + begin + Result.AsArray.Add(obj.Value); + ite.Next; + obj := ite.GetIter; + end; + finally + ite.Free; + end; +end; + +function TSuperTableString.GetNames: ISuperObject; +var + ite: TSuperAvlIterator; + obj: TSuperAvlEntry; +begin + Result := TSuperObject.Create(stArray); + ite := TSuperAvlIterator.Create(Self); + try + ite.First; + obj := ite.GetIter; + while obj <> nil do + begin + Result.AsArray.Add(TSuperObject.Create(obj.FName)); + ite.Next; + obj := ite.GetIter; + end; + finally + ite.Free; + end; +end; + +procedure TSuperTableString.doDeleteEntry(Entry: TSuperAvlEntry; all: boolean); +begin + if Entry.Ptr <> nil then + begin + if all then Entry.Value.Clear(true); + Entry.Value := nil; + end; + inherited; +end; + +function TSuperTableString.GetO(const k: SOString): ISuperObject; +var + e: TSuperAvlEntry; +begin + e := Search(k); + if e <> nil then + Result := e.Value else + Result := nil +end; + +procedure TSuperTableString.PutO(const k: SOString; const value: ISuperObject); +var + entry: TSuperAvlEntry; +begin + entry := Insert(TSuperAvlEntry.Create(k, Pointer(value))); + if entry.FPtr <> nil then + ISuperObject(entry.FPtr)._AddRef; +end; + +procedure TSuperTableString.PutS(const k: SOString; const value: SOString); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +function TSuperTableString.GetS(const k: SOString): SOString; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsString else + Result := ''; +end; + +procedure TSuperTableString.PutI(const k: SOString; value: SuperInt); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +function TSuperTableString.GetI(const k: SOString): SuperInt; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsInteger else + Result := 0; +end; + +procedure TSuperTableString.PutD(const k: SOString; value: Double); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +procedure TSuperTableString.PutC(const k: SOString; value: Currency); +begin + PutO(k, TSuperObject.CreateCurrency(Value)); +end; + +function TSuperTableString.GetC(const k: SOString): Currency; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsCurrency else + Result := 0.0; +end; + +function TSuperTableString.GetD(const k: SOString): Double; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsDouble else + Result := 0.0; +end; + +procedure TSuperTableString.PutB(const k: SOString; value: Boolean); +begin + PutO(k, TSuperObject.Create(Value)); +end; + +function TSuperTableString.GetB(const k: SOString): Boolean; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsBoolean else + Result := False; +end; + +{$IFDEF SUPER_METHOD} +procedure TSuperTableString.PutM(const k: SOString; value: TSuperMethod); +begin + PutO(k, TSuperObject.Create(Value)); +end; +{$ENDIF} + +{$IFDEF SUPER_METHOD} +function TSuperTableString.GetM(const k: SOString): TSuperMethod; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj.AsMethod else + Result := nil; +end; +{$ENDIF} + +procedure TSuperTableString.PutN(const k: SOString; const value: ISuperObject); +begin + if value <> nil then + PutO(k, TSuperObject.Create(stNull)) else + PutO(k, value); +end; + +function TSuperTableString.GetN(const k: SOString): ISuperObject; +var + obj: ISuperObject; +begin + obj := GetO(k); + if obj <> nil then + Result := obj else + Result := TSuperObject.Create(stNull); +end; + + +{$IFDEF VER210} + +{ TSuperAttribute } + +constructor TSuperAttribute.Create(const AName: string); +begin + FName := AName; +end; + +{ TSuperRttiContext } + +constructor TSuperRttiContext.Create; +begin + Context := TRttiContext.Create; + SerialFromJson := TDictionary.Create; + SerialToJson := TDictionary.Create; + + SerialFromJson.Add(TypeInfo(Boolean), serialfromboolean); + SerialFromJson.Add(TypeInfo(TDateTime), serialfromdatetime); + SerialFromJson.Add(TypeInfo(TGUID), serialfromguid); + SerialToJson.Add(TypeInfo(Boolean), serialtoboolean); + SerialToJson.Add(TypeInfo(TDateTime), serialtodatetime); + SerialToJson.Add(TypeInfo(TGUID), serialtoguid); +end; + +destructor TSuperRttiContext.Destroy; +begin + SerialFromJson.Free; + SerialToJson.Free; + Context.Free; +end; + +class function TSuperRttiContext.GetFieldName(r: TRttiField): string; +var + o: TCustomAttribute; +begin + for o in r.GetAttributes do + if o is SOName then + Exit(SOName(o).Name); + Result := r.Name; +end; + +class function TSuperRttiContext.GetFieldDefault(r: TRttiField; const obj: ISuperObject): ISuperObject; +var + o: TCustomAttribute; +begin + if not ObjectIsType(obj, stNull) then Exit(obj); + for o in r.GetAttributes do + if o is SODefault then + Exit(SO(SODefault(o).Name)); + Result := obj; +end; + +function TSuperRttiContext.AsType(const obj: ISuperObject): T; +var + ret: TValue; +begin + if FromJson(TypeInfo(T), obj, ret) then + Result := ret.AsType else + raise exception.Create('Marshalling error'); +end; + +function TSuperRttiContext.AsJson(const obj: T; const index: ISuperObject = nil): ISuperObject; +var + v: TValue; +begin + TValue.MakeWithoutCopy(@obj, TypeInfo(T), v); + if index <> nil then + Result := ToJson(v, index) else + Result := ToJson(v, so); +end; + +function TSuperRttiContext.FromJson(TypeInfo: PTypeInfo; const obj: ISuperObject; + var Value: TValue): Boolean; + + procedure FromChar; + begin + if ObjectIsType(obj, stString) and (Length(obj.AsString) = 1) then + begin + Value := string(AnsiString(obj.AsString)[1]); + Result := True; + end else + Result := False; + end; + + procedure FromWideChar; + begin + if ObjectIsType(obj, stString) and (Length(obj.AsString) = 1) then + begin + Value := obj.AsString[1]; + Result := True; + end else + Result := False; + end; + + procedure FromInt64; + var + i: Int64; + begin + case ObjectGetType(obj) of + stInt: + begin + TValue.Make(nil, TypeInfo, Value); + TValueData(Value).FAsSInt64 := obj.AsInteger; + Result := True; + end; + stString: + begin + if TryStrToInt64(obj.AsString, i) then + begin + TValue.Make(nil, TypeInfo, Value); + TValueData(Value).FAsSInt64 := i; + Result := True; + end else + Result := False; + end; + else + Result := False; + end; + end; + + procedure FromInt(const obj: ISuperObject); + var + TypeData: PTypeData; + i: Integer; + o: ISuperObject; + begin + case ObjectGetType(obj) of + stInt, stBoolean: + begin + i := obj.AsInteger; + TypeData := GetTypeData(TypeInfo); + Result := (i >= TypeData.MinValue) and (i <= TypeData.MaxValue); + if Result then + TValue.Make(@i, TypeInfo, Value); + end; + stString: + begin + o := SO(obj.AsString); + if not ObjectIsType(o, stString) then + FromInt(o) else + Result := False; + end; + else + Result := False; + end; + end; + + procedure fromSet; + begin + if ObjectIsType(obj, stInt) then + begin + TValue.Make(nil, TypeInfo, Value); + TValueData(Value).FAsSLong := obj.AsInteger; + Result := True; + end else + Result := False; + end; + + procedure FromFloat(const obj: ISuperObject); + var + o: ISuperObject; + begin + case ObjectGetType(obj) of + stInt, stDouble, stCurrency: + begin + TValue.Make(nil, TypeInfo, Value); + case GetTypeData(TypeInfo).FloatType of + ftSingle: TValueData(Value).FAsSingle := obj.AsDouble; + ftDouble: TValueData(Value).FAsDouble := obj.AsDouble; + ftExtended: TValueData(Value).FAsExtended := obj.AsDouble; + ftComp: TValueData(Value).FAsSInt64 := obj.AsInteger; + ftCurr: TValueData(Value).FAsCurr := obj.AsCurrency; + end; + Result := True; + end; + stString: + begin + o := SO(obj.AsString); + if not ObjectIsType(o, stString) then + FromFloat(o) else + Result := False; + end + else + Result := False; + end; + end; + + procedure FromString; + begin + case ObjectGetType(obj) of + stObject, stArray: + Result := False; + stnull: + begin + Value := ''; + Result := True; + end; + else + Value := obj.AsString; + Result := True; + end; + end; + + procedure FromClass; + var + f: TRttiField; + v: TValue; + begin + case ObjectGetType(obj) of + stObject: + begin + Result := True; + if Value.Kind <> tkClass then + Value := GetTypeData(TypeInfo).ClassType.Create; + for f in Context.GetType(Value.AsObject.ClassType).GetFields do + if f.FieldType <> nil then + begin + Result := FromJson(f.FieldType.Handle, GetFieldDefault(f, obj.AsObject[GetFieldName(f)]), v); + if Result then + f.SetValue(Value.AsObject, v) else + Exit; + end; + end; + stNull: + begin + Value := nil; + Result := True; + end + else + // error + Value := nil; + Result := False; + end; + end; + + procedure FromRecord; + var + f: TRttiField; + p: Pointer; + v: TValue; + begin + Result := True; + TValue.Make(nil, TypeInfo, Value); + for f in Context.GetType(TypeInfo).GetFields do + begin + if ObjectIsType(obj, stObject) and (f.FieldType <> nil) then + begin + p := IValueData(TValueData(Value).FHeapData).GetReferenceToRawData; + Result := FromJson(f.FieldType.Handle, GetFieldDefault(f, obj.AsObject[GetFieldName(f)]), v); + if Result then + f.SetValue(p, v) else + Exit; + end else + begin + Result := False; + Exit; + end; + end; + end; + + procedure FromDynArray; + var + i: Integer; + p: Pointer; + pb: PByte; + val: TValue; + typ: PTypeData; + el: PTypeInfo; + begin + case ObjectGetType(obj) of + stArray: + begin + i := obj.AsArray.Length; + p := nil; + DynArraySetLength(p, TypeInfo, 1, @i); + pb := p; + typ := GetTypeData(TypeInfo); + if typ.elType <> nil then + el := typ.elType^ else + el := typ.elType2^; + + Result := True; + for i := 0 to i - 1 do + begin + Result := FromJson(el, obj.AsArray[i], val); + if not Result then + Break; + val.ExtractRawData(pb); + val := TValue.Empty; + Inc(pb, typ.elSize); + end; + if Result then + TValue.MakeWithoutCopy(@p, TypeInfo, Value) else + DynArrayClear(p, TypeInfo); + end; + stNull: + begin + TValue.MakeWithoutCopy(nil, TypeInfo, Value); + Result := True; + end; + else + i := 1; + p := nil; + DynArraySetLength(p, TypeInfo, 1, @i); + pb := p; + typ := GetTypeData(TypeInfo); + if typ.elType <> nil then + el := typ.elType^ else + el := typ.elType2^; + + Result := FromJson(el, obj, val); + val.ExtractRawData(pb); + val := TValue.Empty; + + if Result then + TValue.MakeWithoutCopy(@p, TypeInfo, Value) else + DynArrayClear(p, TypeInfo); + end; + end; + + procedure FromArray; + var + ArrayData: PArrayTypeData; + idx: Integer; + function ProcessDim(dim: Byte; const o: ISuperobject): Boolean; + var + i: Integer; + v: TValue; + a: PTypeData; + begin + if ObjectIsType(o, stArray) and (ArrayData.Dims[dim-1] <> nil) then + begin + a := @GetTypeData(ArrayData.Dims[dim-1]^).ArrayData; + if (a.MaxValue - a.MinValue + 1) <> o.AsArray.Length then + begin + Result := False; + Exit; + end; + Result := True; + if dim = ArrayData.DimCount then + for i := a.MinValue to a.MaxValue do + begin + Result := FromJson(ArrayData.ElType^, o.AsArray[i], v); + if not Result then + Exit; + Value.SetArrayElement(idx, v); + inc(idx); + end + else + for i := a.MinValue to a.MaxValue do + begin + Result := ProcessDim(dim + 1, o.AsArray[i]); + if not Result then + Exit; + end; + end else + Result := False; + end; + var + i: Integer; + v: TValue; + begin + TValue.Make(nil, TypeInfo, Value); + ArrayData := @GetTypeData(TypeInfo).ArrayData; + idx := 0; + if ArrayData.DimCount = 1 then + begin + if ObjectIsType(obj, stArray) and (obj.AsArray.Length = ArrayData.ElCount) then + begin + Result := True; + for i := 0 to ArrayData.ElCount - 1 do + begin + Result := FromJson(ArrayData.ElType^, obj.AsArray[i], v); + if not Result then + Exit; + Value.SetArrayElement(idx, v); + v := TValue.Empty; + inc(idx); + end; + end else + Result := False; + end else + Result := ProcessDim(1, obj); + end; + + procedure FromClassRef; + var + r: TRttiType; + begin + if ObjectIsType(obj, stString) then + begin + r := Context.FindType(obj.AsString); + if r <> nil then + begin + Value := TRttiInstanceType(r).MetaclassType; + Result := True; + end else + Result := False; + end else + Result := False; + end; + + procedure FromUnknown; + begin + case ObjectGetType(obj) of + stBoolean: + begin + Value := obj.AsBoolean; + Result := True; + end; + stDouble: + begin + Value := obj.AsDouble; + Result := True; + end; + stCurrency: + begin + Value := obj.AsCurrency; + Result := True; + end; + stInt: + begin + Value := obj.AsInteger; + Result := True; + end; + stString: + begin + Value := obj.AsString; + Result := True; + end + else + Value := nil; + Result := False; + end; + end; + + procedure FromInterface; + const soguid: TGuid = '{4B86A9E3-E094-4E5A-954A-69048B7B6327}'; + var + o: ISuperObject; + begin + if CompareMem(@GetTypeData(TypeInfo).Guid, @soguid, SizeOf(TGUID)) then + begin + if obj <> nil then + TValue.Make(@obj, TypeInfo, Value) else + begin + o := TSuperObject.Create(stNull); + TValue.Make(@o, TypeInfo, Value); + end; + Result := True; + end else + Result := False; + end; +var + Serial: TSerialFromJson; +begin + if TypeInfo <> nil then + begin + if not SerialFromJson.TryGetValue(TypeInfo, Serial) then + case TypeInfo.Kind of + tkChar: FromChar; + tkInt64: FromInt64; + tkEnumeration, tkInteger: FromInt(obj); + tkSet: fromSet; + tkFloat: FromFloat(obj); + tkString, tkLString, tkUString, tkWString: FromString; + tkClass: FromClass; + tkMethod: ; + tkWChar: FromWideChar; + tkRecord: FromRecord; + tkPointer: ; + tkInterface: FromInterface; + tkArray: FromArray; + tkDynArray: FromDynArray; + tkClassRef: FromClassRef; + else + FromUnknown + end else + begin + TValue.Make(nil, TypeInfo, Value); + Result := Serial(Self, obj, Value); + end; + end else + Result := False; +end; + +function TSuperRttiContext.ToJson(var value: TValue; const index: ISuperObject): ISuperObject; + procedure ToInt64; + begin + Result := TSuperObject.Create(SuperInt(Value.AsInt64)); + end; + + procedure ToChar; + begin + Result := TSuperObject.Create(string(Value.AsType)); + end; + + procedure ToInteger; + begin + Result := TSuperObject.Create(TValueData(Value).FAsSLong); + end; + + procedure ToFloat; + begin + case Value.TypeData.FloatType of + ftSingle: Result := TSuperObject.Create(TValueData(Value).FAsSingle); + ftDouble: Result := TSuperObject.Create(TValueData(Value).FAsDouble); + ftExtended: Result := TSuperObject.Create(TValueData(Value).FAsExtended); + ftComp: Result := TSuperObject.Create(TValueData(Value).FAsSInt64); + ftCurr: Result := TSuperObject.CreateCurrency(TValueData(Value).FAsCurr); + end; + end; + + procedure ToString; + begin + Result := TSuperObject.Create(string(Value.AsType)); + end; + + procedure ToClass; + var + o: ISuperObject; + f: TRttiField; + v: TValue; + begin + if TValueData(Value).FAsObject <> nil then + begin + o := index[IntToStr(Integer(Value.AsObject))]; + if o = nil then + begin + Result := TSuperObject.Create(stObject); + index[IntToStr(Integer(Value.AsObject))] := Result; + for f in Context.GetType(Value.AsObject.ClassType).GetFields do + if f.FieldType <> nil then + begin + v := f.GetValue(Value.AsObject); + Result.AsObject[GetFieldName(f)] := ToJson(v, index); + end + end else + Result := o; + end else + Result := nil; + end; + + procedure ToWChar; + begin + Result := TSuperObject.Create(string(Value.AsType)); + end; + + procedure ToVariant; + begin + Result := SO(Value.AsVariant); + end; + + procedure ToRecord; + var + f: TRttiField; + v: TValue; + begin + Result := TSuperObject.Create(stObject); + for f in Context.GetType(Value.TypeInfo).GetFields do + begin + v := f.GetValue(IValueData(TValueData(Value).FHeapData).GetReferenceToRawData); + Result.AsObject[GetFieldName(f)] := ToJson(v, index); + end; + end; + + procedure ToArray; + var + idx: Integer; + ArrayData: PArrayTypeData; + + procedure ProcessDim(dim: Byte; const o: ISuperObject); + var + dt: PTypeData; + i: Integer; + o2: ISuperObject; + v: TValue; + begin + if ArrayData.Dims[dim-1] = nil then Exit; + dt := GetTypeData(ArrayData.Dims[dim-1]^); + if Dim = ArrayData.DimCount then + for i := dt.MinValue to dt.MaxValue do + begin + v := Value.GetArrayElement(idx); + o.AsArray.Add(toJSon(v, index)); + inc(idx); + end + else + for i := dt.MinValue to dt.MaxValue do + begin + o2 := TSuperObject.Create(stArray); + o.AsArray.Add(o2); + ProcessDim(dim + 1, o2); + end; + end; + var + i: Integer; + v: TValue; + begin + Result := TSuperObject.Create(stArray); + ArrayData := @Value.TypeData.ArrayData; + idx := 0; + if ArrayData.DimCount = 1 then + for i := 0 to ArrayData.ElCount - 1 do + begin + v := Value.GetArrayElement(i); + Result.AsArray.Add(toJSon(v, index)) + end + else + ProcessDim(1, Result); + end; + + procedure ToDynArray; + var + i: Integer; + v: TValue; + begin + Result := TSuperObject.Create(stArray); + for i := 0 to Value.GetArrayLength - 1 do + begin + v := Value.GetArrayElement(i); + Result.AsArray.Add(toJSon(v, index)); + end; + end; + + procedure ToClassRef; + begin + if TValueData(Value).FAsClass <> nil then + Result := TSuperObject.Create(string( + TValueData(Value).FAsClass.UnitName + '.' + + TValueData(Value).FAsClass.ClassName)) else + Result := nil; + end; + + procedure ToInterface; + begin + if TValueData(Value).FHeapData <> nil then + TValueData(Value).FHeapData.QueryInterface(ISuperObject, Result) else + Result := nil; + end; + +var + Serial: TSerialToJson; +begin + if not SerialToJson.TryGetValue(value.TypeInfo, Serial) then + case Value.Kind of + tkInt64: ToInt64; + tkChar: ToChar; + tkSet, tkInteger, tkEnumeration: ToInteger; + tkFloat: ToFloat; + tkString, tkLString, tkUString, tkWString: ToString; + tkClass: ToClass; + tkWChar: ToWChar; + tkVariant: ToVariant; + tkRecord: ToRecord; + tkArray: ToArray; + tkDynArray: ToDynArray; + tkClassRef: ToClassRef; + tkInterface: ToInterface; + else + result := nil; + end else + Result := Serial(Self, value, index); +end; + +{ TSuperObjectHelper } + +constructor TSuperObjectHelper.FromJson(const obj: ISuperObject; ctx: TSuperRttiContext = nil); +var + v: TValue; + ctxowned: Boolean; +begin + if ctx = nil then + begin + ctx := TSuperRttiContext.Create; + ctxowned := True; + end else + ctxowned := False; + try + v := Self; + if not ctx.FromJson(v.TypeInfo, obj, v) then + raise Exception.Create('Invalid object'); + finally + if ctxowned then + ctx.Free; + end; +end; + +constructor TSuperObjectHelper.FromJson(const str: string; ctx: TSuperRttiContext = nil); +begin + FromJson(SO(str), ctx); +end; + +function TSuperObjectHelper.ToJson(ctx: TSuperRttiContext = nil): ISuperObject; +var + v: TValue; + ctxowned: boolean; +begin + if ctx = nil then + begin + ctx := TSuperRttiContext.Create; + ctxowned := True; + end else + ctxowned := False; + try + v := Self; + Result := ctx.ToJson(v, SO); + finally + if ctxowned then + ctx.Free; + end; +end; + +{$ENDIF} + +{$IFDEF DEBUG} +initialization + +finalization + Assert(debugcount = 0, 'Memory leak'); +{$ENDIF} +end. + diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uJSON.pas" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uJSON.pas" new file mode 100644 index 0000000..4ccdf95 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uJSON.pas" @@ -0,0 +1,4389 @@ +{ + Copyright (C) 2005 Fabio Almeida + fabiorecife@yahoo.com.br + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Autor : Jose Fabio Nascimento de Almeida + Data : 7/11/2005 + + +Change Logs: +2013-11-04 By yangyxd + parent, child֧֡int64֧ + +2009-11-22 By creation_zy + Can parse #10 #13 inside a string. + JSONObject.quote method can deal with special character smaller than space. + Value inside a _String object can Read/Write directly. + +2011-09-02 By creation_zy + Add _Object to store common Object. + +2011-09-20 By creation_zy + Add SafeFreeJObj. + Add "inline" directive. + +2011-12-15 By creation_zy + Add SpaceStr function to optimize toString3. +} +unit uJSON; + +interface + +uses + Windows,SysUtils, Classes, TypInfo; + +{$DEFINE J_OBJECT} // store common Object +{$IF COMPILERVERSION>=18}{$DEFINE INLINE_OPT}{$IFEND} +{$DEFINE BACK_OPT} +{$DEFINE NEXT_OPT} + + +Type + JSONArray = class ; + JSONBase = class; + JSONObject = class; + + TZAbstractObject = class (TObject) + class procedure WriteChar(avOut: TStream; const avData: Char); + class procedure WriteString(avOut: TStream; const avData: string); + class procedure WriteText(avOut: TStream; const avData: string; len: Integer); + + function Equals(const Value: TZAbstractObject): Boolean; virtual; + function Hash: LongInt; + function Clone: TZAbstractObject; virtual; + function toString: string; virtual; + function toJSONObject: JSONObject; + function toJSONArray: JSONArray; + function instanceOf(const Value: TZAbstractObject): Boolean; + procedure SaveToStream(stream: TStream); virtual; + class function getInt(o: TZAbstractObject; DefaultValue: Integer):Integer; + class function getInt64(o: TZAbstractObject; DefaultValue: Int64): Int64; + class function getDouble(o: TZAbstractObject; DefaultValue: Double):Double; + class function getBoolean(o: TZAbstractObject; DefaultValue: Boolean):Boolean; + procedure Free; overload; //2011-10-10 Call SafeFreeJObj + end; + + ClassCastException = class (Exception) end; + NoSuchElementException = class (Exception) end; + NumberFormatException = class (Exception) end; + NullPointerException = class (Exception) end; + NotImplmentedFeature = class (Exception) end; + _Number = class ; + _String = class; + _Double = class; + _NULL = class ; +{$IFDEF J_OBJECT} + _Object = class; //2011-08-09 +{$ENDIF} + + + ParseException = class (Exception) + constructor create (_message: string ; index: integer); + end; + JSONTokener = class (TZAbstractObject) + public + constructor create (const s: string); + procedure back();{$IFDEF INLINE_OPT}inline;{$ENDIF} + class function dehexchar(c: char) :integer; + function more :boolean;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function next(): char; overload ;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function next (c:char ): char; overload ;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function next (n:integer): string; overload ;{$IFDEF INLINE_OPT}inline;{$ENDIF} + function nextClean (): char;//{$IFDEF INLINE_OPT}inline;{$ENDIF} + function nextString (quote: char): string;//{$IFDEF INLINE_OPT}inline;{$ENDIF} + function nextTo (d: char): string; overload ; + function nextTo (const delimiters: string): char; overload ; + function nextValue (parent: JSONBase): TZAbstractObject ;//{$IFDEF INLINE_OPT}inline;{$ENDIF} + procedure skipPast (const _to: string ) ; + function skipTo (_to: char ): char; + function syntaxError (const _message: string): ParseException; + function toString: string; override; + function unescape (const s: string): string; + private + myIndex, Len1: integer; + mySource: string; + end; + + // by yangyxd 2013.11.04 + JSONBase = class(TZAbstractObject) + private + FParent: JSONBase; + FName: string; + protected + function GetCount: Integer; virtual; + function GetChild(Index: Integer): TZAbstractObject; virtual; + procedure SetChild(Index: Integer; const Value: TZAbstractObject); virtual; + public + constructor Create; + function IndexOfObject(aobj: TObject): Integer; virtual; + property Count: Integer read GetCount; + property Parent: JSONBase read FParent write FParent; + property Name: string read FName write FName; + property Child[Index: Integer]: TZAbstractObject read GetChild write SetChild; + end; + + JSONObject = class (JSONBase) + private + myHashMap: TStringList; + function GetPropValues(const Key: String): String; + procedure SetPropValues(const Key: String; const Value: String); + procedure SetAsString(const Value: String); + function GetKeyByIndex(index: Integer): String; + procedure SetCascadeValueEx(const Value: String; const Keys: array of String; + StartIdx: Integer); + function GetValByIndex(index: Integer): String; + procedure UpdateByTokener(x: JSONTokener); + function GetValObjByIndex(index: Integer): TZAbstractObject; + protected + function GetCount: Integer; override; // by yangyxd + function GetChild(Index: Integer): TZAbstractObject; override; // by yangyxd + procedure SetChild(Index: Integer; const Value: TZAbstractObject); override; // by yangyxd + public + constructor Create; overload; + constructor Create (jo: JSONObject; sa: array of string); overload; + constructor Create (x: JSONTokener); overload; + constructor Create (map: TStringList); overload; + constructor Create (const s: string); overload; + constructor CreateInArray(Ay: JSONArray); + + function IndexOfObject(aobj: TObject): Integer; override; // by yangyxd + + procedure Clean; + function Clone: TZAbstractObject; override; + function Accumulate (const key: string; value: TZAbstractObject): JSONObject; + function Get (const key: string): TZAbstractObject; + function GetBoolean (const key: string): boolean; + function GetDouble (const key: string): double; + function GetInt (const key: string): integer; + function GetInt64 (const key: string): Int64; + function GetJSONArray (const key: string) :JSONArray; + function GetJSONObject (const key: string): JSONObject; + function GetString (const key: string): string; + function Has (const key: string): boolean; + function IsNull (const key: string): boolean; + function Keys: TStringList ; + function Length: integer; + function Names: JSONArray; + class function NumberToString (n: _Number): string; + class function ValueToString(value: TZAbstractObject): string; overload; + class procedure ValueToStream(value: TZAbstractObject; stream: TStream); overload; + class function ValueToString(value: TZAbstractObject; + indentFactor, indent: integer): string; overload; + + function NextSibling: JSONObject; + function UpSibling: JSONObject; + + function Opt (const key: string): TZAbstractObject; + function OptBoolean (const key: string): boolean; overload; + function OptBoolean (const key: string; defaultValue: boolean): boolean; overload; + function OptDouble (const key: string): double; overload; + function OptDouble (const key: string; defaultValue: double): double; overload; + function OptInt (const key: string): integer; overload; + function OptInt (const key: string; defaultValue: integer): integer; overload; + function OptInt64 (const key: string): int64; overload; + function OptInt64 (const key: string; defaultValue: int64): int64; overload; + function OptString (const key: string): string; overload; + function OptString (const key, defaultValue: string): string; overload; + + function OptJSONArray (const key: string): JSONArray; overload; + function OptJSONObject (const key: string): JSONObject; overload; + + function Put (const key: string; value: boolean): JSONObject; overload; + function Put (const key: string; value: double): JSONObject; overload; + function Put (const key: string; value: integer): JSONObject; overload; + function Put (const key: string; value: int64): JSONObject; overload; + function Put (const key: string; const value: string): JSONObject; overload; + function Put (const key: string; value: TZAbstractObject): JSONObject; overload; + + function PutOpt (const key: string; value: TZAbstractObject): JSONObject; + class function quote (const s: string): string; + class procedure quoteToStream (stream: TStream; const s: string); + function Remove (const key: string): TZAbstractObject; + procedure AssignTo(json: JSONObject); + + function ToJSONArray (names: JSONArray): JSONArray; + function toString (): string ; overload; override; + function toString2 (indentFactor: integer): string; overload; + function toString3 (indentFactor, indent: integer): string; overload; + + procedure SaveToStream(stream: TStream); override; + + //Add by creation_zy 2008-10-21 + property PropValues[const Key: String]:String read GetPropValues write SetPropValues; default; + property KeyByIndex[index: Integer]:String read GetKeyByIndex; + property ValByIndex[index: Integer]:String read GetValByIndex; + property ValObjByIndex[index: Integer]:TZAbstractObject read GetValObjByIndex; + property AsString:String read ToString write SetAsString; + procedure Assign(Source: JSONObject); + function Opt2(key, key2: string): TZAbstractObject; + function OptString2(key, key2: String; DefaultValue: String=''): String; + function OptInt2(key, key2: String; DefaultValue: Integer=0): Integer; + function GetCascadeValue(const Keys: array of String): String; + procedure SetCascadeValue(const Value: String; const Keys: array of String); + function GetCascadeValEx(const Keys: array of String): String; + function GetCascadeValObj(const Keys: array of String): TZAbstractObject; + function GetDiffFrom(Source: JSONObject; UseSrc: Boolean=true):JSONObject; + procedure Delete(index: Integer); + procedure RemoveByKeyHeader(const Header: String='~'); + function RemoveLastKey:TZAbstractObject; + procedure CleanKey(const Key: String); + function SetKey(idx: Integer; const Key: String):Boolean; + function PropCount:Integer; + function KeyByVal(const Value: String):String; + function PartExtract(KeyNames: TStrings; DoRemove: Boolean):JSONObject; + function ExtractAll:JSONObject; + function TryNewJSONArray(const Key: String):JSONArray; + function TryNewJSONObject(const Key: String):JSONObject; + //Add by creation_zy 2011-08-09 + {$IFDEF J_OBJECT} + function GetObject (const key: string): TObject; + function OptObject (const key: string): TObject; overload; + function OptObject (const key: string; defaultValue: TObject): TObject; overload; + function Put (const key: string; value: TObject): JSONObject; overload; + {$ENDIF} + + destructor Destroy;override; + class function NULL: _NULL; + end; + + JSONArray = class (JSONBase) + public + destructor Destroy ; override; + constructor Create ; overload; + constructor Create (collection: TList); overload; + constructor Create (x: JSONTokener); overload; + constructor Create (const s: string); overload; + + procedure Clean; //by creation_zy 2009-08-19 + function Clone: TZAbstractObject; override; //by creation_zy 2008-10-05 + function get (index: integer): TZAbstractObject; + function getBoolean (index: integer): boolean; + function getDouble (index: integer): double; + function getInt (index: integer): integer; + function getInt64 (index: integer): int64; + function getJSONArray (index: integer): JSONArray; + function getJSONObject (index: integer): JSONObject; + function getString (index: integer): string; + function isNull (index: integer): boolean; + function join (separator: string): string; + function length: integer; + function opt (index: integer): TZAbstractObject; + function optBoolean ( index: integer): boolean; overload; + function optBoolean ( index: integer; defaultValue: boolean): boolean; overload; + function optDouble (index: integer): double; overload; + function optDouble (index: integer; defaultValue :double ): double ; overload; + function optInt (index: integer): integer; overload; + function optInt (index: integer; defaultValue: integer): integer; overload; + function OptInt64 (index: integer): int64; overload; + function OptInt64 (index: integer; defaultValue: int64): int64; overload; + function optJSONArray (index: integer): JSONArray ; overload; + function optJSONObject (index: integer): JSONObject ; overload; + function optString (index: integer): string; overload; + function optString (index: integer; defaultValue: string): string; overload; + {$IFDEF J_OBJECT} + function optObject (index: integer): TObject; overload; + {$ENDIF} + function put ( value: boolean): JSONArray; overload ; + function put ( value: double ): JSONArray; overload ; + function put ( value: integer): JSONArray; overload ; + function put ( value: TZAbstractObject): JSONArray; overload ; + function put ( value: string): JSONArray; overload; + {$IFDEF J_OBJECT} + function put ( value: TObject): JSONArray; overload; + {$ENDIF} + function put ( index: integer ; value: boolean): JSONArray; overload ; + function put ( index: integer ; value: double): JSONArray; overload ; + function put ( index: integer ; value: integer): JSONArray; overload ; + function put ( index: integer ; value: int64): JSONArray; overload ; + function put ( index: integer ; value: TZAbstractObject): JSONArray; overload ; + function put ( index: integer; value: string): JSONArray; overload; + {$IFDEF J_OBJECT} + function put ( index: integer ; value: TObject): JSONArray; overload; + {$ENDIF} + function LastItem: TZAbstractObject; + function toJSONObject (names :JSONArray ): JSONObject ; overload ; + function toString: string; overload; override; + function toString2 (indentFactor: integer): string; overload; + function toString3 (indentFactor, indent: integer): string; overload; + function toList (): TList; + function appendJSONArray( value: JSONArray): Integer ; //2008-10-08 + procedure Assign( Source: JSONArray); + + function IndexOfObject(aobj: TObject): Integer; override; // by yangyxd + private + myArrayList: TList; + protected + function GetCount: Integer; override; // by yangyxd + function GetChild(Index: Integer): TZAbstractObject; override; // by yangyxd + procedure SetChild(Index: Integer; const Value: TZAbstractObject); override; // by yangyxd + end; + + + _Number = class (TZAbstractObject) + public + function doubleValue: double; virtual; abstract; + function intValue: integer; virtual; abstract; + function int64Value: Int64; virtual; abstract; // by yangyxd + end; + + _Boolean = class (TZAbstractObject) + public + class function _TRUE (): _Boolean; + class function _FALSE (): _Boolean; + class function valueOf (b: boolean): _Boolean; + constructor create (b: boolean); + function boolValue: Boolean; //By creation_zy 2008-10-06 + function toString (): string; override; + function Clone :TZAbstractObject; override; + private + fvalue: boolean; + end; + + _Double = class (_Number) + constructor create (const s: string); overload; + constructor create (s: _String); overload; + constructor create (d: double); overload; + function doubleValue: double; override; + function intValue: integer; override; + function int64Value: Int64; override; + function toString (): string ; override; + class function NaN: double; + function Clone :TZAbstractObject; override; + private + fvalue: double; + end; + + _Integer = class (_Number) + class function parseInt64 (const s: string): int64; overload; + class function parseInt64 (s: _String): int64; overload; + class function parseInt (const s: string; i: integer): integer; overload; + class function parseInt (s: _String): integer; overload; + class function toHexString (c: char): string; + constructor create (i: integer); overload; + constructor create (i: int64); overload; + constructor create (const s: string); overload; + function doubleValue: double; override; + function intValue: integer; override; + function int64Value: Int64; override; + function toString (): string; override; + function Clone :TZAbstractObject; override; + private + fvalue: int64; + end; + + _String = class (TZAbstractObject) + private + function GetAsString: String; + procedure SetAsString(const Value: String); + public + constructor create (const s: string); + function equalsIgnoreCase (const s: string): boolean; + function Equals(const Value: TZAbstractObject): Boolean; override; + function toString(): string; override; + function Clone :TZAbstractObject; override; + property AsString: String read GetAsString write SetAsString; //By creation_zy 2009-11-22 + private + fvalue: string; + end; + + _NULL = class (TZAbstractObject) + function Equals(const Value: TZAbstractObject): Boolean; override; + function toString(): string; override; + function Clone :TZAbstractObject; override; //By creation_zy 2009-12-11 + end; + +{$IFDEF J_OBJECT} + _Object = class (TZAbstractObject) + function Equals(const Value: TZAbstractObject): Boolean; override; + function toString(): string; override; + function Clone :TZAbstractObject; override; + private + fvalue: TObject; + constructor Create(value: TObject); + procedure SetAsObject(const Value: TObject); + public + property AsObject: TObject read fvalue write SetAsObject; + end; +{$ENDIF} + + TJObjTransFlag=(jtfDbQouteStr, jtfQouteStr, jtfOtherAsStr); + TJObjTransFlags=set of TJObjTransFlag; + +function HexToInt(const S: String):Integer; +function IsConstJSON(Z: TObject):Boolean; +procedure SafeFreeJObj(Z: TObject);{$IF COMPILERVERSION>=18}inline;{$IFEND} +function SpaceStr(ALen: Integer):String; +function StrToAbstractJObj(const Str: String; Flags: TJObjTransFlags=[jtfDbQouteStr, jtfQouteStr]):TZAbstractObject; + +// by yangyxd 2013.11.06 +function JsonGetAttribute(const JSON, Name: string): string; +function JsonGetAttributeAsInt(const JSON, Name: string): Integer; +function JsonGetAttributeAsDouble(const JSON, Name: string): double; + +var + gcLista: TList; + CNULL: _NULL; + //Set this var to ture to force unicode char (eg: Chinese...) output in the form of \uXXXX + UnicodeOutput: Boolean=false; + SimpleJSON: Boolean=false; //2012-08-03 + +implementation + +//{$D-} + +const + CROTINA_NAO_IMPLEMENTADA :string = 'Not imp'; +var + CONST_FALSE: _Boolean; + CONST_TRUE: _Boolean; + +//By creation_zy +function IsSimpString(const Str:String):Boolean; +var + i:Integer; +begin + Result:=true; + for i:=1 to Length(Str) do + begin + Result:=Str[i] in ['0'..'9','a'..'z','A'..'Z','_']; + if not Result then exit; + end; +end; + +//By creation_zy +function SingleHZToJSONCode(const HZ:String):String; +var + wstr:WideString; +begin + if HZ='' then + begin + Result:=''; + exit; + end; + wstr:=WideString(HZ); + Result:='\u'+IntToHex(PWord(@wstr[1])^,4); +end; + +//By creation_zy 2009-11-21 +function IsConstJSON(Z: TObject):Boolean; +begin + Result:=(Z=CNULL) or (Z=CONST_FALSE) or (Z=CONST_TRUE); +end; + +procedure SafeFreeJObj(Z: TObject); +begin + if not IsConstJSON(Z) then + Z.Free; +end; + +function SpaceStr(ALen: Integer): string; {$IFDEF INLINE_OPT}inline;{$ENDIF} +begin + if ALen > 0 then begin + SetLength(Result, ALen); + FillChar(Result[1], ALen, ' '); + end else Result := ''; +end; + +procedure newNotImplmentedFeature () ; +begin + raise NotImplmentedFeature.create (CROTINA_NAO_IMPLEMENTADA); +end; + +function getFormatSettings: TFormatSettings ; +var + f: TFormatSettings; +begin + {$IFDEF MSWINDOWS} + SysUtils.GetLocaleFormatSettings (Windows.GetThreadLocale,f); + {$ELSE} + newNotImplmentedFeature(); + {$ENDIF} + Result:=f; + Result.DecimalSeparator:='.'; + Result.ThousandSeparator:=','; +end; + + +function HexToInt(const S: String): Integer; +const HexMap:array [Char] of SmallInt = + ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ); +var + i, n, l: Integer; +begin + Result:=0; + l:=Length(S); + if l=0 then exit; + if S[1]='$' then + n:=2 + else if (l>=2) and (S[2] in ['x','X']) then + n:=3 + else + n:=1; + for i:=n to l do + Result:=Result*16+HexMap[S[i]]; +end; + +function StrToAbstractJObj(const Str: String; Flags: TJObjTransFlags):TZAbstractObject; +var + i:Integer; +begin + if Str<>'' then + begin + case Str[1] of + '{': + begin + try + Result:=JSONObject.Create(Str); + except + Result:=nil; + end; + exit; + end; + '[': + begin + try + Result:=JSONArray.Create(Str); + except + Result:=nil; + end; + exit; + end; + '0'..'9','.','-': + begin + try + i:=StrToInt(Str); + Result:=_Integer.Create(i); + except + Result:=_Double.Create(StrToFloatDef(Str,0)); + end; + exit; + end; + 'n': + begin + if Str='null' then + begin + Result:=CNull; + exit; + end; + end; + 't','T','F','f': + begin + if UpperCase(Str)='TRUE' then + begin + Result:=CONST_TRUE; + exit; + end + else if UpperCase(Str)='FALSE' then + begin + Result:=CONST_FALSE; + exit; + end; + end; + end; + end; + Result:=_String.create(Str); +end; + +function mLeftPos(const SrcStr: AnsiString; SubChar: Char; sPos: Integer): Integer; +var + i: Integer; +begin + for i := sPos to Length(SrcStr) do + if SrcStr[i] = SubChar then begin + Result := i; Exit; + end; + Result := -1; +end; + +function mRightPos(const SrcStr: AnsiString; SubChar: Char; sPos: Integer): Integer; +var + i: Integer; +begin + for i := sPos downto 1 do + if SrcStr[i] = SubChar then begin + Result := i; Exit; + end; + Result := -1; +end; + +function mMidStr(const SrcStr: AnsiString; sPos, sCount: Integer): AnsiString; +begin + Result := Copy(SrcStr, sPos, sCount); +end; + +function JsonGetAttribute(const JSON, Name: string): string; +var + i, j: Integer; +begin + i := Pos('"'+Name+'":"', JSON); + if i > 0 then begin + i := i + Length(Name) + 4; + j := mLeftPos(JSON, '"', i); + Result := Copy(JSON, i, j - i); + end; +end; + +function JsonGetAttributeAsInt(const JSON, Name: string): Integer; +var + i, j: Integer; +begin + Result := 0; + i := Pos('"'+Name+'":', JSON); + if i > 0 then begin + i := i + Length(Name) + 3; + j := mLeftPos(JSON, ',', i); + if (j < 0) then + j := mLeftPos(JSON, '}', i); + if (j > 0) then begin + if JSON[i] = '"' then i := i + 1; + if JSON[j-1] = '"' then j := j - 1; + Result := StrToIntDef(Copy(JSON, i, j - i), 0); + end; + end; +end; + +function JsonGetAttributeAsDouble(const JSON, Name: string): double; +var + i, j: Integer; +begin + Result := 0; + i := Pos('"'+Name+'":', JSON); + if i > 0 then begin + i := i + Length(Name) + 3; + j := mLeftPos(JSON, ',', i); + if (j < 0) then + j := mLeftPos(JSON, '}', i); + if (j > 0) then begin + if JSON[i] = '"' then i := i + 1; + if JSON[j-1] = '"' then j := j - 1; + Result := StrToFloatDef(Copy(JSON, i, j - i), 0); + end; + end; +end; + +{ JSONTokener } + +(** + * Construct a JSONTokener from a string. + * + * @param s A source string. + *) +constructor JSONTokener.create(const s: string); +begin + myIndex:=1; + mySource:=s; + Len1:=Length(mySource)+1; +end; + +(** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. +*) +procedure JSONTokener.back; +begin + if myIndex>1 then Dec(myIndex); +end; + +(** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + *) +class function JSONTokener.dehexchar(c: char): integer; +begin + if ((c >= '0') and (c <= '9')) then begin + Result:= (ord(c) - ord('0')); + exit; + end; + if ((c >= 'A') and (c <= 'F')) then begin + Result:= (ord(c) + 10 - ord('A')); + exit; + end; + if ((c >= 'a') and (c <= 'f')) then begin + Result:=ord(c) + 10 - ord('a'); + exit; + end; + Result:=-1; +end; + + +(** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. +*) +function JSONTokener.more: boolean; +begin + Result:=myIndex<=Len1{System.length(mySource)+1}; +end; + +function JSONTokener.next: char; +begin + if {$IFDEF BACK_OPT}myIndex<=Len1{$ELSE}more(){$ENDIF} then + begin + Result:=mySource[myIndex]; + Inc(myIndex); + end + else + Result:=chr(0); +end; + + + (** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws ParseException if the character does not match. + *) +function JSONTokener.next(c: char): char; +begin + Result:=next(); + if (Result <> c) then + raise syntaxError('Expected ' + c + ' and instead saw ' + Result + '.'); +end; + + +(** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @exception ParseException + * Substring bounds error if there are not + * n characters remaining in the source string. + *) +function JSONTokener.next(n: integer): string; +var + i,j: integer; +begin + i:=self.myIndex; + j:=i + n; + if (j > System.length(self.mySource)) then begin + raise syntaxError('Substring bounds error'); + end; + self.myIndex:=self.myIndex + n; + Result:=copy (self.mySource,i,n); //substring(i, j) +end; + + (** + * Get the next char in the string, skipping whitespace + * and comments (slashslash, slashstar, and hash). + * @throws ParseException + * @return A character, or 0 if there are no more characters. + *) +function JSONTokener.nextClean: char; +var + c: char; +begin + while true do + begin + {$IFDEF NEXT_OPT2} + if myIndex<=Len1 then + begin + Result:=mySource[myIndex]; + Inc(myIndex); + end + else begin + Result:=#0; + exit; + end; + {$ELSE} + Result:=next(); + {$ENDIF} + if (Result = '/') then + begin + case (next()) of + '/': begin + repeat + c:=next(); + until (not ((c <> #10) and (c <> #13) and (c <> #0))); + end ; + '*': + begin + while (true) do + begin + c:=next(); + if (c = #0) then + begin + raise syntaxError('Unclosed comment.'); + end; + if (c = '*') then + begin + if (next() = '/') then break; + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + end; + end; + end + else begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:='/'; + exit; + end; + end; + end + else if (Result = '#') then + begin + repeat + c:=next(); + until (not ((c <> #10) and (c <> #13) and (c <> #0))); + end + else if ((Result = #0) or (Result > ' ')) then + exit; + end; //while +end; + + +(** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @exception ParseException Unterminated string. + *) +function JSONTokener.nextString (quote: char): string; +var + c: char; + sb: string; + WCh:WideChar; +begin + sb:=''; + while (true) do + begin + c:=next(); + case (c) of + #0, #10, #13: + begin + //Ignore #10 and #13 inside a string. By creation_zy 2009-11-22 + if c=#0 then + raise syntaxError('Unterminated string') + else + continue; + end; + '\': + begin + c:=next(); + case (c) of + {'b': // ?o backspace = #8 + sb.append('\b'); + break;} + 'b': //By creation_zy 2009-08-20 + sb:=sb + #8; + 't': + sb:=sb + #9; + 'n': + sb:=sb + #10; + 'f': + sb:=sb + #12; + 'r': + sb:=sb + #13; + {case 'u': + sb.append((char)Integer.parseInt(next(4), 16)); + break; + case 'x': \cx The control character corresponding to x + sb.append((char) Integer.parseInt(next(2), 16)); + break;} + 'u': //By creation_zy 2009-08-20 + begin + PWord(@WCh)^:=Word(HexToInt(next(4))); + sb:=sb+WCh; + end; + else + sb:=sb + c + end; + end + else begin + if (c = quote) then + begin + Result:=sb; + exit; + end; + sb:=sb + c + end; + end; + end; +end; + +(** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param d A delimiter character. + * @return A string. + *) +function JSONTokener.nextTo(d: char): string; +var + sb: string; + c: char; +begin + //c:=#0; + sb:=''; + while (true) do + begin + c:=next(); + if ((c = d) or (c = #0) or (c = #10) or (c = #13)) then + begin + if (c <> #0) then + begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + end; + Result:=trim (sb); + exit; + end; + sb:=sb + c; + end; +end; + +(** + * Get the text up but not including one of the specified delimeter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. +*) +function JSONTokener.nextTo(const delimiters: string): char; +var + c: char; + sb: string; +begin + //c:=#0; + Result:=#0; //By creation_zy + sb:=''; + while (true) do + begin + c:=next(); + if ((pos (c,delimiters) > 0) or (c = #0) or + (c = #10) or (c = #13)) then + begin + if (c <> #0) then + begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + end; + sb:=trim(sb); + if (System.length(sb) > 0) then + Result:=sb[1]; + exit; + end; + sb:=sb + c; + end; +end; + +(** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, or String, or the JSONObject.NULL object. + * @exception ParseException The source does not conform to JSON syntax. + * + * @return An object. +*) +function JSONTokener.nextValue(parent: JSONBase): TZAbstractObject; // by yangyxd parent +var + c, b: char; + s , sb: string; + n:Integer; +begin + c:=nextClean(); + + case (c) of + '"', #39: begin + Result:=_String.create (nextString(c)); + exit; + end; + '{': begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:=JSONObject.create(self); + JSONObject(Result).Parent := parent; + exit; + end; + '[': begin + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:=JSONArray.create(self); + JSONArray(Result).Parent := parent; + exit; + end; + end; + + (* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + *) + + sb:=''; + b:=c; + while ((ord(c) >= ord(' ')) and (pos (c,',:]}/\\\"[{;=#') = 0)) do begin + sb:=sb + c; + c:=next(); + end; + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + + (* + * If it is true, false, or null, return the proper value. + *) + + s:=trim (sb); + n:=System.Length(s); + if n=0 then + raise syntaxError('Missing value.'); + if n in [4,5] then //2009-09-14 Length limit before AnsiLowerCase. By creation_zy + begin + sb:=AnsiLowerCase(s); + if (sb = 'true') then + begin + Result:= _Boolean._TRUE; + exit; + end; + + if (sb = 'false') then + begin + Result:=_Boolean._FALSE; + exit; + end; + if (sb = 'null') then + begin + Result:=JSONObject.NULL; + exit; + end; + end; + + (* + * If it might be a number, try converting it. We support the 0- and 0x- + * conventions. If a number cannot be produced, then the value will just + * be a string. Note that the 0-, 0x-, plus, and implied string + * conventions are non-standard. A JSON parser is free to accept + * non-JSON forms as long as it accepts all correct JSON forms. + *) + + if ( ((b >= '0') and (b <= '9')) or (b = '.') + or (b = '-') or (b = '+')) then + begin + if (b = '0') then begin + if ( (System.length(s) > 2) and + ((s[2] = 'x') or (s[2] = 'X') ) ) then + begin + try + Result:=_Integer.create(_Integer.parseInt(copy(s,3,System.length(s)),16)); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + end else begin + try + if (System.length(s) >= 2) and (s[2]='.') then //2009-09-14 By creation_zy + Result:=_Double.create(s) + else + Result:=_Integer.create(_Integer.parseInt(s,8)); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + end; + end; + if Pos('.',s)=0 then //2011-10-02 Bug fixed. By creation_zy + try + Result:=_Integer.create(s); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + + try + Result:=_Double.create(s); + exit; + Except + on e:Exception do begin + ///* Ignore the error */ + end; + end; + end; + Result:=_String.create(s); +end; + +(** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + *) +function JSONTokener.skipTo(_to: char): char; +var + c: char; + index: integer; +begin + index:=self.myIndex; + repeat + c:=next(); + if (c = #0) then + begin + self.myIndex:=index; + Result:=c; + exit; + end; + until c=_to; + {$IFDEF BACK_OPT}if myIndex>1 then Dec(myIndex);{$ELSE}back();{$ENDIF} + Result:=c; + exit; +end; + +(** + * Skip characters until past the requested string. + * If it is not found, we are left at the end of the source. + * @param to A string to skip past. + *) +procedure JSONTokener.skipPast(const _to: string); +begin + self.myIndex:=pos (_to, copy(mySource, self.myIndex, System.length(mySource))); + if (self.myIndex < 0) then begin + self.myIndex:=System.length(self.mySource)+1; + end else begin + self.myIndex:=self.myIndex + System.length(_to); + end; +end; + + + +(** + * Make a ParseException to signal a syntax error. + * + * @param message The error message. + * @return A ParseException object, suitable for throwing + *) +function JSONTokener.syntaxError(const _message: string): ParseException; +begin + Result:=ParseException.create (_message + toString()+' postion: ' //' prximo a: ' + + copy (toString(),self.myIndex,10), self.myIndex); +end; + +(** + * Make a printable string of this JSONTokener. + * + * @return " at character [this.myIndex] of [this.mySource]" + *) + + +function JSONTokener.toString: string; +begin + Result:=' at character ' + intToStr(myIndex) + ' of ' + mySource; +end; + + +(** + * Convert %hh sequences to single characters, and + * convert plus to space. + * @param s A string that may contain + * + (plus) and + * %hh sequences. + * @return The unescaped string. + *) +function JSONTokener.unescape(const s: string): string; +var + len, i,d,e: integer; + b: string; + c: char; +begin + len:=System.length(s); + b:=''; + i:=1; + while ( i <= len ) do begin + c:=s[i]; + if (c = '+') then begin + c:=' '; + end + else if ((c = '%') and ((i + 2) <= len)) then + begin + d:=dehexchar(s[i + 1]); + e:=dehexchar(s[i + 2]); + if ((d >= 0) and (e >= 0)) then + begin + c:=chr(d * 16 + e); + i:=i + 2; + end; + end; + b:=b + c; + i:=i + 1; + end; + Result:=b ; +end; + +{ JSONObject } + +(** +* Construct an empty JSONObject. +*) +constructor JSONObject.create; +begin + myHashMap:=TStringList.create; + inherited Create; +end; + + +(** + * Construct a JSONObject from a subset of another JSONObject. + * An array of strings is used to identify the keys that should be copied. + * Missing keys are ignored. + * @param jo A JSONObject. + * @param sa An array of strings. + *) +constructor JSONObject.create(jo: JSONObject; sa: array of string); +var + i: integer; +begin + create(); + for i:=low(sa) to high(sa) do + putOpt(sa[i], jo.opt(sa[i]).Clone); +end; + +(** + * Construct a JSONObject from a JSONTokener. + * @param x A JSONTokener object containing the source string. + * @throws ParseException if there is a syntax error in the source string. + *) +constructor JSONObject.create(x: JSONTokener); +begin + create ; + UpdateByTokener(x); +end; + +(** + * Construct a JSONObject from a Map. + * @param map A map object that can be used to initialize the contents of + * the JSONObject. + *) +constructor JSONObject.create(map: TStringList); +var + i: integer; +begin + myHashMap:=TStringlist.create; + for i:=0 to map.Count -1 do + myHashMap.AddObject(map[i],map.Objects[i]); +end; + +(** + * Construct a JSONObject from a string. + * This is the most commonly used JSONObject constructor. + * @param string A string beginning + * with { (left brace) and ending + * with } (right brace). + * @exception ParseException The string must be properly formatted. + *) +constructor JSONObject.create(const s: string); +var + token: JSOnTokener; +begin + if s='' then //Add by creation_zy 2008-10-21 + begin + create(); + exit; + end; + token:=JSONTokener.create(s); + try + create(token); + finally + token.free; + end; +end; + + +constructor JSONObject.CreateInArray(Ay: JSONArray); +begin + create; + if Ay<>nil then + Ay.put(Self); +end; + +(** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a + * JSONArray is stored under the key to hold all of the accumulated values. + * If there is already a JSONArray, then the new value is appended to it. + * In contrast, the put method replaces the previous value. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws NullPointerException if the key is null + *) +function JSONObject.accumulate(const key: string; value: TZAbstractObject): JSONObject; +var + a: JSONArray; + o: TZAbstractObject; +begin + o:=opt(key); + if (o = nil) then + put(key, value) + else if (o is JSONArray) then + begin + a:=JSONArray(o); + a.put(value); + end + else begin + a:=JSONArray.create; + a.put(o.Clone); + a.put(value); + put(key, a); + end; + Result:=self; +end; + + +(** + * Get the value object associated with a key. + * + * @param key A key string. + * @return The object associated with the key. + * @exception NoSuchElementException if the key is not found. + *) +function JSONObject.get(const key: string): TZAbstractObject; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o = nil) then + raise NoSuchElementException.create('JSONObject['+quote(key)+'] not found.'); + Result:=o; +end; + + +(** + * Get the boolean value associated with a key. + * + * @param key A key string. + * @return The truth. + * @exception NoSuchElementException if the key is not found. + * @exception ClassCastException + * if the value is not a Boolean or the String "true" or "false". + *) +function JSONObject.getBoolean(const key: string): boolean; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('false'))) then begin + Result:=false; + exit; + end + else if (o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('true'))) then begin + Result:=true; + exit; + end; + raise ClassCastException.create('JSONObject[' + + quote(key) + '] is not a Boolean.'); +end; + +function JSONObject.getDouble(const key: string): double; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is _Number) then + begin + Result:=_Number (o).doubleValue(); + exit; + end ; + if (o is _String) then + begin + Result:=StrToFloat (_String(o).toString(), getFormatSettings()); + exit; + end; + raise NumberFormatException.create('JSONObject['+quote(key)+'] is not a number.'); +end; + + +(** + * Get the int value associated with a key. + * + * @param key A key string. + * @return The integer value. + * @exception NoSuchElementException if the key is not found + * @exception NumberFormatException + * if the value cannot be converted to a number. + *) +function JSONObject.getInt(const key: string): integer; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is _Number) then + Result:= _Number(o).intValue() + else + Result:= Round(getDouble(key)); +end; + + +function JSONObject.GetInt64(const key: string): Int64; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is _Number) then + Result:= _Number(o).int64Value() + else + Result:= Round(getDouble(key)); +end; + +(** + * Get the JSONArray value associated with a key. + * + * @param key A key string. + * @return A JSONArray which is the value. + * @exception NoSuchElementException if the key is not found or + * if the value is not a JSONArray. + *) +function JSONObject.getJSONArray(const key: string): JSONArray; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is JSONArray) then + Result:=JSONArray(o) + else + raise NoSuchElementException.create('JSONObject[' + + quote(key) + '] is not a JSONArray.'); +end; + + +(** + * Get the JSONObject value associated with a key. + * + * @param key A key string. + * @return A JSONObject which is the value. + * @exception NoSuchElementException if the key is not found or + * if the value is not a JSONObject. + *) +function JSONObject.getJSONObject(const key: string): JSONObject; +var + o: TZAbstractObject; +begin + o:=get(key); + if (o is JSONObject) then + Result:=JSONObject(o) + else + raise NoSuchElementException.create('JSONObject[' + + quote(key) + '] is not a JSONObject.'); +end; + + +(** + * Get the string associated with a key. + * + * @param key A key string. + * @return A string which is the value. + * @exception NoSuchElementException if the key is not found. +*) +function JSONObject.getString(const key: string): string; +begin + Result:=get(key).toString(); +end; + + +(** + * Determine if the JSONObject contains a specific key. + * @param key A key string. + * @return true if the key exists in the JSONObject. + *) +function JSONObject.has(const key: string): boolean; +begin + Result:=myHashMap.IndexOf(key)>=0; +end; + +function JSONObject.IndexOfObject(aobj: TObject): Integer; +var + i: Integer; +begin + for i := 0 to myHashMap.Count - 1 do + if myHashMap.Objects[i] = aobj then begin + Result := I; + Exit; + end; + Result := -1; +end; + +(** + * Determine if the value associated with the key is null or if there is + * no value. + * @param key A key string. + * @return true if there is no value associated with the key or if + * the value is the JSONObject.NULL object. + *) +function JSONObject.isNull(const key: string): boolean; +begin + Result:=NULL.equals(opt(key)); +end; + +function JSONObject.keys: TStringList; +var + i: integer; +begin + Result:=TStringList.Create; + for i:=0 to myHashMap.Count -1 do + Result.add (myHashMap[i]); +end; + +function JSONObject.length: integer; +begin + Result:=myHashMap.Count; +end; + + +(** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + *) +function JSONObject.names: JSONArray; +var + i,c: integer; + k: TStringList; +begin + Result:=nil; + k:=keys; + try + c:=k.Count; + if c>0 then //2013-05-04 Fix memory leak bug found by K.o.s + begin + Result:=JSONArray.create; + for i:=0 to c-1 do + Result.put(_String.create(k[i])); + end; + finally + k.free; + end; +end; + +function JSONObject.NextSibling: JSONObject; +var + i: Integer; +begin + if not Assigned(Parent) then + Result := nil + else begin + i := Parent.IndexOfObject(Self) + 1; + if (i > 0) and (i < Parent.Count) then begin + if Parent.Child[i] is JSONObject then + Result := JSONObject(Parent.Child[i]) + else Result := nil; + end else + Result := nil; + end; +end; + +class function JSONObject.numberToString(n: _Number): string; +begin + if (n = nil) then + Result:='' + else if (n is _Integer) then + Result:=IntToStr(n.intValue) + else + Result:=FloatToStr(n.doubleValue, getFormatSettings()); +end; + + +(** + * Get an optional value associated with a key. + * @param key A key string. + * @return An object which is the value, or null if there is no value. + * @exception NullPointerException The key must not be null. + *) +function JSONObject.opt(const key: string): TZAbstractObject; +var + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then + Result:=nil + else + Result:=TZAbstractObject(myHashMap.Objects[i]); +end; + +function JSONObject.Opt2(key, key2: string): TZAbstractObject; +var + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then + i:=myHashMap.IndexOf(key2); + if i<0 then + Result:=nil + else + Result:=TZAbstractObject(myHashMap.Objects[i]); +end; + +(** + * Get an optional boolean associated with a key. + * It returns false if there is no such key, or if the value is not + * Boolean.TRUE or the String "true". + * + * @param key A key string. + * @return The truth. + *) +function JSONObject.optBoolean(const key: string): boolean; +begin + Result:=optBoolean(key, false); +end; + + +(** + * Get an optional boolean associated with a key. + * It returns the defaultValue if there is no such key, or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param key A key string. + * @param defaultValue The default. + * @return The truth. + *) +function JSONObject.optBoolean(const key: string; + defaultValue: boolean): boolean; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> nil) then + begin + if o.ClassType=_Boolean then //2009-03-06 By creation_zy + begin + Result:=_Boolean(o).fvalue; + exit; + end + else if //o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o).equalsIgnoreCase('false'))) then begin + Result:=false; + exit; + end + else if //o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o).equalsIgnoreCase('true'))) then begin + Result:=true; + exit; + end; + end; + Result:=defaultValue; +end; + + +(** + * Get an optional double associated with a key, + * or NaN if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A string which is the key. + * @return An object which is the value. + *) +function JSONObject.optDouble(const key: string): double; +begin + Result:=optDouble(key, _Double.NaN); +end; + + +(** + * Get an optional double associated with a key, or the + * defaultValue if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + *) +function JSONObject.optDouble(const key: string; defaultValue: double): double; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).doubleValue(); + exit; + end; + try + Result:=_Double.create(_String(o)).doubleValue(); + exit; + except + on e:Exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + +(** + * Get an optional int value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + *) +function JSONObject.optInt(const key: string): integer; +begin + Result:=optInt(key, 0); +end; + + +(** + * Get an optional int value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + *) +function JSONObject.optInt(const key: string; defaultValue: integer): integer; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> null) and ( o <> nil ) then //By creation_zy. Add compare to nil + begin + if (o is _Number) then + begin + Result:=(_Number(o)).intValue(); + exit; + end; + try + Result:=_Integer.parseInt(_String(o)); + except + on e:Exception do + begin + Result:=defaultValue; + end; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +function JSONObject.OptInt2(key, key2: String; DefaultValue: Integer): Integer; +var + o:TZAbstractObject; +begin + o:=Opt2(key,key2); + if o<>nil then + Result:=TZAbstractObject.getInt(o,DefaultValue) + else + Result:=DefaultValue; +end; + +function JSONObject.OptInt64(const key: string): int64; +begin + Result:=optInt64(key, 0); +end; + +function JSONObject.OptInt64(const key: string; defaultValue: int64): int64; +var + o: TZAbstractObject; +begin + o:=opt(key); + if (o <> null) and ( o <> nil ) then //By creation_zy. Add compare to nil + begin + if (o is _Number) then + begin + Result:=(_Number(o)).int64Value(); + exit; + end; + try + Result:=_Integer.parseInt64(_String(o)); + except + on e:Exception do + begin + Result:=defaultValue; + end; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +(** + * Get an optional JSONArray associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONArray. + * + * @param key A key string. + * @return A JSONArray which is the value. + *) +function JSONObject.optJSONArray(const key: string): JSONArray; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is JSONArray) then + Result:=JSONArray(o) + else + Result:=nil; +end; + + +(** + * Get an optional JSONObject associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONObject. + * + * @param key A key string. + * @return A JSONObject which is the value. + *) +function JSONObject.optJSONObject(const key: string): JSONObject; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is JSONObject) then + Result:=JSONObject(o) + else + Result:=nil; +end; + +{$IFDEF J_OBJECT} +function JSONObject.OptObject(const key: string; + defaultValue: TObject): TObject; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is _Object) then + Result:=_Object(o).AsObject + else + Result:=defaultValue; +end; + +function JSONObject.OptObject(const key: string): TObject; +var + o: TZAbstractObject ; +begin + o:=opt(key); + if (o is _Object) then + Result:=_Object(o).AsObject + else + Result:=nil; +end; +{$ENDIF} + +(** + * Get an optional string associated with a key. + * It returns an empty string if there is no such key. If the value is not + * a string and is not null, then it is coverted to a string. + * + * @param key A key string. + * @return A string which is the value. + *) +function JSONObject.optString(const key: string): string; +var + o: TZAbstractObject ; + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then + Result:='' + else begin + o:=TZAbstractObject(myHashMap.Objects[i]); + if (o <> nil) then + Result:=o.toString() + else + Result:=''; + end; +end; + +(** + * Get an optional string associated with a key. + * It returns the defaultValue if there is no such key. + * + * @param key A key string. + * @param defaultValue The default. + * @return A string which is the value. + *) +function JSONObject.optString(const key, defaultValue: string): string; +var + o: TZAbstractObject ; +begin + o:=Opt(key); + if (o <> nil) then + Result:=o.toString() + else + Result:=defaultValue; +end; + +function JSONObject.OptString2(key, key2: String; DefaultValue: String): String; +var + o:TZAbstractObject; +begin + o:=Opt2(key,key2); + if o<>nil then + Result:=o.toString() + else + Result:=DefaultValue; +end; + +(** + * Put a key/boolean pair in the JSONObject. + * + * @param key A key string. + * @param value A boolean which is the value. + * @return this. + *) +function JSONObject.put(const key: string; value: boolean): JSONObject; +begin + put(key, _Boolean.valueOf(value)); + Result:=self; +end; + +(** + * Put a key/double pair in the JSONObject. + * + * @param key A key string. + * @param value A double which is the value. + * @return this. + *) +function JSONObject.put(const key: string; value: double): JSONObject; +begin + put(key, _Double.create(value)); + Result:=self; +end; + + +(** + * Put a key/int pair in the JSONObject. + * + * @param key A key string. + * @param value An int which is the value. + * @return this. + *) +function JSONObject.put(const key: string; value: integer): JSONObject; +begin + put(key, _Integer.create(value)); + Result:=self; +end; + + +(** + * Put a key/value pair in the JSONObject. If the value is null, + * then the key will be removed from the JSONObject if it is present. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, String, or the + * JSONObject.NULL object. + * @return this. + * @exception NullPointerException The key must be non-null. + *) +function JSONObject.put(const key: string; value: TZAbstractObject): JSONObject; +var + temp: TObject; + i: integer; +begin + if (key = '') then + begin + raise NullPointerException.create('Null key.'); + end ; + if (value <> nil) then {$D+} + begin + i:=myHashMap.IndexOf(key); + if ( i >= 0) then + begin + temp:=myHashMap.Objects [i]; + myHashMap.Objects[i]:=value; + if (temp<>CNULL) and (temp<>CONST_FALSE) and (temp<>CONST_TRUE) then //Prevent to free const obj. By craetion_zy 2009-11-21 + temp.free; + end + else + myHashMap.AddObject(key, value); + end + else begin + temp:=remove(key); + if (temp<>nil) and (temp<>CNULL) and (temp<>CONST_FALSE) and (temp<>CONST_TRUE) then //Prevent to free const obj. By craetion_zy 2009-11-21 + temp.free; + end; + Result:=self; +end; + +function JSONObject.put(const key, value: string): JSONObject; +begin + put(key, _String.create(value)); + Result:=self; +end; + +{$IFDEF J_OBJECT} +function JSONObject.Put(const key: string; value: TObject): JSONObject; +begin + put(key, _Object.create(value)); + Result:=self; +end; +function JSONObject.Put(const key: string; value: int64): JSONObject; +begin + put(key, _Integer.create(value)); + Result:=self; +end; + +{$ENDIF} + +(** + * Put a key/value pair in the JSONObject, but only if the + * value is non-null. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, String, or the + * JSONObject.NULL object. + * @return this. + * @exception NullPointerException The key must be non-null. + *) +function JSONObject.putOpt(const key: string; value: TZAbstractObject): JSONObject; +begin + if (value <> nil) then + put(key, value); + Result:=self; +end; + + +(** + * Produce a string in double quotes with backslash sequences in all the + * right places. + * @param string A String + * @return A String correctly formatted for insertion in a JSON message. + *) +class function JSONObject.quote(const s: string): string; +var + b,c: char; + i, len: integer; + sb, t: string; +begin + if ((s = '') or (System.Length(s) = 0)) then + begin + Result:= '""'; + exit; + end; + + //b:=#0; + c:=#0; + len:=System.length(s); + //SetLength (s, len+4); + t:=''; + + sb:=sb +'"'; + i:=1; + while i<=len do + begin + b:=c; + c:=s[i]; + case (c) of + '\', '"': + begin + sb:=sb + '\'; + sb:=sb + c; + end; + '/': + begin + if (b = '<') then + begin + sb:=sb + '\'; + end; + sb:=sb + c; + end; + {#8, #9, #10, #12, #13: + begin + sb:=sb + c; + end;} + //Output special character smaller than space. By creation_zy 2009-11-22 + #0: sb:=sb + '\u0000'; + #1..#7: sb:=sb + '\u000'+Char(Byte('0')+Byte(c)); + #8: sb:=sb + '\b'; + #9: sb:=sb + '\t'; + #10: sb:=sb + '\n'; + #12: sb:=sb + '\f'; + #13: sb:=sb + '\r'; + else + begin + if (c < ' ') then + begin + t:='000' + _Integer.toHexString(c); + sb:=sb + '\u' + copy (t,System.length(t)-3,4); + end + else if UnicodeOutput and (c>#128) and (i#128) and (i + * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +function JSONObject.toString: string; +var + _keys: TStringList; + o, sb: string; + i :integer; +begin + _keys:=keys(); + try + sb:='{'; + + for i:=0 to _keys.count -1 do + begin + if (System.length(sb) > 1) then + begin + sb:= sb + ','; + end; + o:=_keys[i]; + if SimpleJSON and IsSimpString(o) then //By creation_zy + sb:=sb + o + else + sb:=sb + quote(o); + sb:=sb + ':'; + sb:= sb + valueToString(TZAbstractObject(myHashMap.Objects[myHashMap.IndexOf(o)])); + end; + sb:=sb + '}'; + Result:=sb; + finally + _keys.free; + end; +end; + + +(** + * Make a prettyprinted JSON external form string of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +procedure JSONObject.SaveToStream(stream: TStream); +var + _keys: TStringList; + o: string; + i, j :integer; +begin + _keys:=keys(); + try + WriteChar(stream, '{'); + j := 1; + for i:=0 to _keys.count -1 do + begin + if (j > 1) then + WriteChar(stream, ','); + o:=_keys[i]; + if SimpleJSON and IsSimpString(o) then //By creation_zy + WriteString(stream, o) + else + quoteToStream(stream, o); + WriteChar(stream, ':'); + ValueToStream(TZAbstractObject(myHashMap.Objects[myHashMap.IndexOf(o)]), stream); + Inc(j); + end; + WriteChar(stream, '}'); + finally + _keys.free; + end; +end; + +function JSONObject.toString2(indentFactor: integer): string; +begin + Result:=toString3(indentFactor, 0); +end; + +(** + * Make a prettyprinted JSON string of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +function JSONObject.toString3(indentFactor, indent: integer): string; +var + j , n , newindent: integer; + _keys: TStringList; + o, sb: string; +begin + //i:=0; + n:=length(); + if (n = 0) then begin + Result:='{}'; + exit; + end; + _keys:=keys(); + try + sb:=sb + '{'; + newindent:=indent + indentFactor; + if (n = 1) then + begin + o:=_keys[0]; + sb:= sb + quote(o); + sb:= sb + ': '; + sb:= sb + valueToString(TZAbstractObject(myHashMap + .Objects[myHashMap.IndexOf(o)]) + , indentFactor, indent); + end + else begin + for j:=0 to _keys.count -1 do + begin + o:=_keys[j]; + if (System.length(sb) > 1) then + begin + sb:=sb + ','+ #10; + end + else begin + sb:= sb + #10; + end; + sb:= sb + SpaceStr(newindent) + quote(o) + ': '; + sb:= sb + valueToString(TZAbstractObject(myHashMap.Objects[myHashMap.IndexOf(o)]) + , indentFactor, newindent); + end; + if (System.length(sb) > 1) then + begin + sb:=sb + #10; + sb:= sb + SpaceStr(indent); + end; + end; + sb:= sb + '}'; + Result:=sb; + finally + _keys.Free; //Memory leak fixed. By creation_zy 2009-08-03 + end; +end; + +class function JSONObject.NULL: _NULL; +begin + Result:=CNULL; +end; + +(** + * Make JSON string of an object value. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +class function JSONObject.valueToString(value: TZAbstractObject): string; +begin + if ((value = nil) or (value.equals(null))) then begin + Result:='null'; + exit; + end; + if (value is _Number) then begin + Result:=numberToString(_Number(value)); + exit; + end; + if ((value is _Boolean) or (value is JSONObject) or + (value is JSONArray)) then begin + Result:=value.toString(); + exit; + end; + Result:=quote(value.toString()); +end; + +class procedure JSONObject.ValueToStream(value: TZAbstractObject; + stream: TStream); +var + m: TStringStream; +begin + if ((value = nil) or (value.equals(null))) then begin + WriteString(stream, 'null'); + exit; + end; + if (value is _Number) then begin + WriteString(stream, numberToString(_Number(value))); + exit; + end; + if ((value is _Boolean) or (value is JSONObject) or + (value is JSONArray)) then begin + value.SaveToStream(stream); + exit; + end; + m := TStringStream.Create(''); + try + value.SaveToStream(m); + quoteToStream(stream, m.DataString); + //WriteString(stream, quote(m.DataString)); + finally + m.Free; + end; +end; + + + +(** + * Make a prettyprinted JSON string of an object value. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + *) +class function JSONObject.valueToString(value: TZAbstractObject; + indentFactor, indent: integer): string; +begin + if ((value = nil) or (value.equals(nil))) then begin + Result:='null'; + exit; + end; + if (value is _Number) then begin + Result:=numberToString(_Number(value)); + exit; + end; + if (value is _Boolean) then begin + Result:= value.toString(); + exit; + end; + if (value is JSONObject) then begin + Result:=((JSONObject(value)).toString3(indentFactor, indent)); + exit; + end; + if (value is JSONArray) then begin + Result:=((JSONArray(value)).toString3(indentFactor, indent)); + exit; + end; + Result:=quote(value.toString()); +end; + +procedure JSONObject.clean; +var + i: integer; + MyObj:TObject; +begin + for i:=Pred(myHashMap.Count) downto 0 do + begin + MyObj:=myHashMap.Objects[i]; + if (MyObj <> CONST_FALSE) and (MyObj <> CONST_TRUE) and (MyObj <> CNULL) then + MyObj.Free; + end; + myHashMap.Clear; +end; + + +(** +* Assign the values to other json Object. +* @param JSONObject objeto to assign Values +*) +procedure JSONObject.assignTo (json: JSONObject) ; +var + _keys: TStringList; + i: integer; +begin + _keys:=keys; + try + for i:=0 to _keys.Count -1 do + begin + json.put (_keys[i],get(_keys[i]).Clone); + end; + finally + _keys.free; + end; +end; + +function JSONObject.Clone: TZAbstractObject; +begin + Result:=JSONObject.create(self.toString()); +end; + +function JSONObject.GetPropValues(const Key: String): String; +begin + Result:=OptString(Key); +end; + +procedure JSONObject.SetPropValues(const Key: String; const Value: String); +begin + Put(Key, Value); +end; + +function JSONObject.GetCascadeValue(const Keys: array of String): String; +var + i:Integer; + TmpProp:JSONObject; +begin + Result:=''; + TmpProp:=Self; + for i:=Low(Keys) to High(Keys) do + begin + if i=High(Keys) then + begin + Result:=TmpProp.PropValues[Keys[i]]; + exit; + end; + TmpProp:=TmpProp.OptJSONObject(Keys[i]); + if TmpProp=nil then exit; + end; +end; + +function JSONObject.GetChild(Index: Integer): TZAbstractObject; +begin + Result := JSONObject(myHashMap.Objects[index]); +end; + +function JSONObject.GetCount: Integer; +begin + Result := myHashMap.Count; +end; + +function JSONObject.GetCascadeValEx(const Keys: array of String): String; +var + i:Integer; + TmpProp,p:JSONObject; +begin + Result:=''; + TmpProp:=Self; + for i:=Low(Keys) to High(Keys) do + begin + if i=High(Keys) then + begin + Result:=TmpProp.PropValues[Keys[i]]; + exit; + end; + p:=TmpProp.OptJSONObject(Keys[i]); + if p=nil then + begin + Result:=TmpProp.OptString(Keys[i]); + exit; + end; + TmpProp:=p; + end; +end; + +function JSONObject.GetCascadeValObj( + const Keys: array of String): TZAbstractObject; +var + i:Integer; + TmpProp:JSONObject; +begin + Result:=nil; + TmpProp:=Self; + for i:=Low(Keys) to High(Keys) do + begin + if i=High(Keys) then + begin + Result:=TmpProp.Opt(Keys[i]); + exit; + end; + TmpProp:=TmpProp.OptJSONObject(Keys[i]); + if TmpProp=nil then exit; + end; +end; + +procedure JSONObject.SetAsString(const Value: String); +var + token:JSOnTokener; +begin + Clean; + if System.Length(Value)<=2 then exit; + token:=JSONTokener.create(Value); + try + UpdateByTokener(token); + finally + token.free; + end; +end; + +function JSONObject.GetDiffFrom(Source: JSONObject; UseSrc: Boolean): JSONObject; +var + sl:TStrings; + i:Integer; + mstr:String; + z,sz:TZAbstractObject; +begin + Result:=JSONObject.Create; + if UseSrc then + sl:=Source.Keys + else + sl:=Keys; + with sl do + begin + for i:=0 to Pred(Count) do + begin + mstr:=Strings[i]; + if UseSrc then + begin + z:=Self.Opt(mstr); + sz:=Source.ValObjByIndex[i]; + if z=nil then + begin + Result.Put(mstr,sz.Clone); + continue; + end; + end + else begin + sz:=Source.Opt(mstr); + z:=Self.ValObjByIndex[i]; + if sz=nil then + begin + Result.Put(mstr,z.Clone); + continue; + end; + end; + if sz.ClassType=z.ClassType then + begin + if sz.toString=z.toString then continue; + if sz.ClassType=JSONObject then + begin + Result.Put(mstr,JSONObject(z).GetDiffFrom(JSONObject(sz),UseSrc)); + continue; + end; + end; + if UseSrc then + Result.Put(mstr,sz.Clone) + else if z<>nil then //Ӧ... + Result.Put(mstr,z.Clone); + end; + Free; + end; +end; + +procedure JSONObject.Delete(index: Integer); +begin + SafeFreeJObj(myHashMap.Objects[index]); + self.myHashMap.delete(index); +end; + +procedure JSONObject.RemoveByKeyHeader(const Header: String); +var + i:Integer; +begin + with Keys do + begin + for i:=Pred(Count) downto 0 do + begin + if Pos(Header,Strings[i])=1 then + CleanKey(Strings[i]); + end; + Free; + end; +end; + +function JSONObject.RemoveLastKey: TZAbstractObject; +var + i:Integer; +begin + with myHashMap do + begin + i:=length-1; + if i<0 then + begin + Result:=nil; + exit; + end; + Result:=TZAbstractObject(Objects[i]); + delete(i); + end; +end; + +function JSONObject.PropCount: Integer; +begin + Result:=myHashMap.Count; +end; + +function JSONObject.KeyByVal(const Value: String): String; +var + i:Integer; +begin + for i:=0 to Pred(myHashMap.Count) do + begin + with TZAbstractObject(myHashMap.Objects[i]) do + begin + if toString=Value then + begin + Result:=myHashMap[i]; + exit; + end; + end; + end; + Result:=''; +end; + +function JSONObject.PartExtract(KeyNames: TStrings; + DoRemove: Boolean): JSONObject; +var + i:Integer; + KeyName:String; +begin + Result:=nil; + if KeyNames=nil then exit; + Result:=JSONObject.Create; + for i:=Pred(Length) downto 0 do + begin + KeyName:=KeyByIndex[i]; + if KeyNames.IndexOf(KeyName)<0 then continue; + if DoRemove then + Result.Put(KeyName,Remove(KeyByIndex[i])) + else + Result.Put(KeyName,ValObjByIndex[i].Clone); + end; +end; + +function JSONObject.ExtractAll: JSONObject; +var + i:Integer; + KeyName:String; +begin + Result:=JSONObject.Create; + for i:=Pred(Length) downto 0 do + begin + KeyName:=KeyByIndex[i]; + Result.Put(KeyName,Remove(KeyByIndex[i])) + end; +end; + +function JSONObject.TryNewJSONArray(const Key: String): JSONArray; +begin + Result:=OptJSONArray(Key); + if Result=nil then + begin + Result:=JSONArray.create; + Result.Parent := Self; + Put(Key,Result); + end; +end; + +function JSONObject.TryNewJSONObject(const Key: String): JSONObject; +begin + Result:=OptJSONObject(Key); + if Result=nil then + begin + Result:=JSONObject.create; + Result.Parent := Self; + Put(Key,Result); + end; +end; + +procedure JSONObject.Assign(Source: JSONObject); +begin + if Source=nil then + Clean + else begin + AsString:=Source.AsString; + end; +end; + +function JSONObject.GetKeyByIndex(index: Integer): String; +begin + Result:=myHashMap[index]; +end; + +function JSONObject.GetObject(const key: string): TObject; +begin + Result:=OptObject(Key); +end; + +procedure JSONObject.SetCascadeValue(const Value: String; + const Keys: array of String); +begin + SetCascadeValueEx(Value,Keys,0); +end; + +procedure JSONObject.SetCascadeValueEx(const Value: String; + const Keys: array of String; StartIdx: Integer); +var + JObj:JSONObject; +begin + if High(Keys)CNULL) and (temp<>CONST_FALSE) and (temp<>CONST_TRUE) and (temp <> nil) then //Prevent to free const obj. By craetion_zy 2009-11-21 + temp.free; +end; + +function JSONObject.SetKey(idx: Integer; const Key: String): Boolean; +begin + Result:=myHashMap.IndexOf(Key)<0; + if not Result or (idx<0) or (idx>=myHashMap.Count) then exit; + myHashMap.Strings[idx]:=Key; +end; + +function JSONObject.GetValByIndex(index: Integer): String; +begin + Result:=TZAbstractObject(myHashMap.Objects[index]).toString; +end; + +function JSONObject.GetValObjByIndex(index: Integer): TZAbstractObject; +begin + Result:=TZAbstractObject(myHashMap.Objects[index]); +end; + +procedure JSONObject.CleanKey(const Key: String); +var + i:Integer; +begin + i:=myHashMap.IndexOf(key); + if i<0 then exit; + SafeFreeJObj(myHashMap.Objects[i]); + myHashMap.delete(i); +end; + +procedure JSONObject.UpdateByTokener(x: JSONTokener); +var + c: char; +begin + FName:=''; // by yangyxd + + if (x.nextClean() <> '{') then + raise x.syntaxError('A JSONObject must begin with "{"'); + while (true) do + begin + c:=x.nextClean(); + case (c) of + #0: + raise x.syntaxError('A JSONObject must end with "}"'); + '}': begin + exit; + end + else begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + //key:=x.nextValue().toString(); + with x.nextValue(self) do + begin + FName:=toString(); // by yangyxd + Free; //Fix memory leak. By creation_zy 2008-08-07 + end; + end + end; //fim do case + + (* + * The key is followed by ':'. We will also tolerate '=' or '=>'. + *) + + c:=x.nextClean(); + if (c = '=') then begin + if (x.next() <> '>') then begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + end; + end else if (c <> ':') then begin + raise x.syntaxError('Expected a ":" after a key'); + end; + self.myHashMap.AddObject(FName, x.nextValue(self)); // by yangyxd + + (* + * Pairs are separated by ','. We will also tolerate ';'. + *) + + case (x.nextClean()) of + ';', ',': begin + if (x.nextClean() = '}') then begin + exit; + end; + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + end; + '}': begin + exit; + end + else begin + raise x.syntaxError('Expected a "," or "}"'); + end + end; + end; //while +end; + +function JSONObject.UpSibling: JSONObject; +var + i: Integer; +begin + if not Assigned(Parent) then + Result := nil + else begin + i := Parent.IndexOfObject(Self) - 1; + if (i > -1) and (i < Parent.Count - 1) and (Parent.Child[i] is JSONObject) then begin + Result := JSONObject(Parent.Child[i]) + end else + Result := nil; + end; +end; + +{ _Boolean } + +function _Boolean.boolValue: Boolean; +begin + Result:=fvalue; +end; + +function _Boolean.Clone: TZAbstractObject; +begin + Result:=_Boolean.create(Self.fvalue); +end; + +constructor _Boolean.create(b: boolean); +begin + fvalue:=b; +end; + +function _Boolean.toString: string; +begin + if fvalue then + Result:='true' + else + Result:='false'; +end; + +class function _Boolean.valueOf(b: boolean): _Boolean; +begin + if (b) then + Result:=_TRUE + else + Result:=_FALSE; +end; + +class function _Boolean._FALSE: _Boolean; +begin + Result:=CONST_FALSE; +end; + +class function _Boolean._TRUE: _Boolean; +begin + Result:=CONST_TRUE; +end; + +{ _String } + +function _String.Clone: TZAbstractObject; +begin + Result:=_String.create (self.fvalue); +end; + +constructor _String.create(const s: string); +begin + fvalue:=s; +end; + + +function _String.equals(const Value: TZAbstractObject): Boolean; +begin + Result:=(value is _String) and (_String (value).fvalue = fvalue); +end; + +function _String.equalsIgnoreCase(const s: string): boolean; +begin + Result:=AnsiLowerCase (s) = AnsiLowerCase (fvalue); +end; + +function _String.GetAsString: String; +begin + Result:=fvalue; +end; + +procedure _String.SetAsString(const Value: String); +begin + fvalue:=Value; +end; + +function _String.toString: string; +begin + Result:=fvalue; +end; + +{ ParseException } + +constructor ParseException.create(_message: string; index: integer); +begin + inherited createFmt(_message+#10#13' erro no caracter: %d',[index]); +end; + +{ _Integer } + +constructor _Integer.create(i: integer); +begin + fvalue:=i; +end; + +function _Integer.Clone: TZAbstractObject; +begin + Result:=_Integer.create(self.fvalue); +end; + +constructor _Integer.create(const s: string); +begin + fvalue:=strToInt64(s); +end; + +constructor _Integer.create(i: int64); +begin + fvalue := i; +end; + +function _Integer.doubleValue: double; +begin + Result:=fvalue; +end; + +function _Integer.int64Value: Int64; +begin + Result := fvalue; +end; + +function _Integer.intValue: integer; +begin + Result:=fvalue; +end; + + + +class function _Integer.parseInt(const s: string; i: integer): integer; +begin + Result:=0; //By creation_zy + case i of + 10: Result:=strToInt(s); + 16: Result:=hexToInt(s); + 8: + begin + if s='0' then exit; //By creation_zy + newNotImplmentedFeature() ; + end; + else newNotImplmentedFeature() ; //By creation_zy + end; +end; + +class function _Integer.parseInt(s: _String): integer; +begin + Result:=_Integer.parseInt(s.toString, 10); +end; + +class function _Integer.parseInt64(s: _String): int64; +begin + Result:=_Integer.parseInt64(s.toString); +end; + +class function _Integer.parseInt64(const s: string): int64; +begin + Result := strToInt64(s); +end; + +class function _Integer.toHexString(c: char): string; +begin + Result:=IntToHex(ord(c),2); +end; + +function _Integer.toString: string; +begin + Result:=intToStr(fvalue); +end; + + +{ _Double } + +constructor _Double.create(const s: string); +begin + fvalue:=StrToFloat(s, getFormatSettings); +end; + +constructor _Double.create(s: _String); +begin + create (s.toString); +end; + + +function _Double.Clone: TZAbstractObject; +begin + Result:=_Double.create(Self.fvalue); +end; + +constructor _Double.create(d: double); +begin + fvalue:=d; +end; + +function _Double.doubleValue: double; +begin + Result:=fvalue; +end; + +function _Double.int64Value: Int64; +begin + Result := Trunc(fvalue); +end; + +function _Double.intValue: integer; +begin + Result:=trunc(fvalue); +end; + +class function _Double.NaN: double; +begin + Result:=3.6e-4951; +end; + +function _Double.toString: string; +begin + Result:=floatToStr(fvalue, getFormatSettings); +end; + +{ JSONArray } + +(** + * Construct a JSONArray from a JSONTokener. + * @param x A JSONTokener + * @exception ParseException A JSONArray must start with '[' + * @exception ParseException Expected a ',' or ']' + *) +constructor JSONArray.create(x: JSONTokener); +var + Ch:Char; +begin + create; + if (x.nextClean() <> '[') then + raise x.syntaxError('A JSONArray must start with "["'); + //if (x.nextClean() = ']') then exit; + //{$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + Ch:=x.nextClean(); + if Ch=']' then exit; + while true do + begin + if (Ch = ',') then begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + myArrayList.add(nil); + end + else begin + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + myArrayList.add(x.nextValue(self)); + end; + case x.nextClean() of + ';',',': + begin + if (x.nextClean() = ']') then exit; + {$IFDEF BACK_OPT}if x.myIndex>1 then Dec(x.myIndex);{$ELSE}x.back();{$ENDIF} + end; + ']': exit; + else raise x.syntaxError('Expected a "," or "]"'); + end; + Ch:=x.nextClean(); + end; +end; + +destructor JSONObject.destroy; +var + i :integer; + MyObj:TObject; +begin + for i:=Pred(myHashMap.Count) downto 0 do + begin + MyObj:=myHashMap.Objects[i]; + SafeFreeJObj(MyObj); + end; + myHashMap.Free; + inherited; +end; + +(** + * Construct a JSONArray from a Collection. + * @param collection A Collection. + *) +constructor JSONArray.create(collection: TList); +var + i: integer; +begin + inherited Create; + myArrayList:=TList.create (); + for i:=0 to collection.count -1 do begin + myArrayList.add (collection[i]); + end; +end; + +(** + * Construct an empty JSONArray. +*) +constructor JSONArray.create; +begin + inherited Create; + myArrayList:=TList.create; +end; + + +(** + * Construct a JSONArray from a source string. + * @param string A string that begins with + * [ (left bracket) + * and ends with ] (right bracket). + * @exception ParseException The string must conform to JSON syntax. + *) +constructor JSONArray.create(const s: string); +var + token:JSOnTokener; +begin + token:=JSONTokener.create(s); + try + create(token); + finally + token.free; + end; +end; + +destructor JSONArray.destroy; +var + i: integer; +begin + for i:=Pred(myArrayList.Count) downto 0 do + SafeFreeJObj(myArrayList[i]); + myArrayList.Free; + inherited; +end; + +procedure JSONArray.Assign(Source: JSONArray); +begin + Clean; + appendJSONArray(Source); +end; + +procedure JSONArray.Clean; +var + i: integer; +begin + for i:=Pred(myArrayList.Count) downto 0 do + SafeFreeJObj(myArrayList[i]); + myArrayList.Clear; //2009-12-10 By creation_zy +end; + +function JSONArray.Clone: TZAbstractObject; +begin + Result:=JSONArray.create(Self.toString); +end; + +function JSONArray.appendJSONArray(value: JSONArray): Integer; +var + i:Integer; +begin + if value=nil then + begin + Result:=0; + exit; + end; + Result:=value.length; + for i:=0 to Pred(Result) do + put(value.get(i).Clone); +end; + +(** + * Get the object value associated with an index. + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @exception NoSuchElementException + *) +function JSONArray.get(index: integer): TZAbstractObject; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o = nil) then + raise NoSuchElementException.create('JSONArray[' + intToStr(index) + + '] not found.'); + Result:=o; +end; + + +(** + * Get the boolean value associated with an index. + * The string values "true" and "false" are converted to boolean. + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + * @exception NoSuchElementException if the index is not found + * @exception ClassCastException + *) +function JSONArray.getBoolean(index: integer): boolean; +var + o: TZAbstractObject; +begin + o:=get(index); + if ((o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('false')))) then begin + Result:=false; + exit; + end else if ((o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('true')))) then begin + Result:=true; + exit; + end; + raise ClassCastException.create('JSONArray[' + intToStr(index) + + '] not a Boolean.'); +end; + +function JSONArray.GetChild(Index: Integer): TZAbstractObject; +begin + Result:=opt(index); +end; + +function JSONArray.GetCount: Integer; +begin + Result := myArrayList.Count; +end; + +(** + * Get the double value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @exception NoSuchElementException if the key is not found + * @exception NumberFormatException + * if the value cannot be converted to a number. + *) +function JSONArray.getDouble(index: integer): double; +var + o: TZAbstractObject; + d: _Double; +begin + o:=get(index); + if (o is _Number) then + begin + Result:=(_Number(o)).doubleValue(); + exit; + end; + if (o is _String) then + begin + d:= _Double.create(_String(o)); + try + Result:=d.doubleValue(); + exit; + finally + d.Free; + end; + end; + raise NumberFormatException.create('JSONObject[' + + intToStr(index) + '] is not a number.'); +end; + + +(** + * Get the int value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @exception NoSuchElementException if the key is not found + * @exception NumberFormatException + * if the value cannot be converted to a number. + *) +function JSONArray.getInt(index: integer): integer; +var + o: TZAbstractObject; +begin + o:=get(index); + if (o is _Number) then + Result:=_Number(o).intValue() + else + Result:=trunc(getDouble(index)); +end; + + +function JSONArray.getInt64(index: integer): int64; +var + o: TZAbstractObject; +begin + o:=get(index); + if (o is _Number) then + Result:=_Number(o).int64Value() + else + Result:=trunc(getDouble(index)); +end; + +(** + * Get the JSONArray associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @exception NoSuchElementException if the index is not found or if the + * value is not a JSONArray + *) +function JSONArray.getJSONArray(index: integer): JSONArray; +var + o: TZAbstractObject; +begin + o:=get(index); + if (o is JSONArray) then + begin + Result:=JSONArray(o); + exit; + end; + raise NoSuchElementException.create('JSONArray[' + intToStr(index) + + '] is not a JSONArray.'); +end; + + +(** + * Get the JSONObject associated with an index. + * @param index subscript + * @return A JSONObject value. + * @exception NoSuchElementException if the index is not found or if the + * value is not a JSONObject + *) +function JSONArray.getJSONObject(index: integer): JSONObject; +var + o: TZAbstractObject; + s: string; +begin + o:=get(index); + if (o is JSONObject) then + Result:=JSONObject(o) + else begin + if o <> nil then + s:=o.ClassName + else + s:='nil'; + raise NoSuchElementException.create('JSONArray[' + intToStr(index) + + '] is not a JSONObject is ' + s); + end; +end; + +(** + * Get the string associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A string value. + * @exception NoSuchElementException + *) +function JSONArray.getString(index: integer): string; +begin + Result:=get(index).toString(); +end; + +function JSONArray.IndexOfObject(aobj: TObject): Integer; +var i: Integer; +begin + for i := 0 to myArrayList.Count - 1 do + if opt(i) = aobj then begin + Result := I; + Exit; + end; + Result := -1; +end; + +(** + * Determine if the value is null. + * @param index The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + *) + +function JSONArray.isNull(index: integer): boolean; +var + o: TZAbstractObject; +begin + o:=opt(index); + Result:=(o = nil) or (o.equals(nil)); +end; + +(** + * Make a string from the contents of this JSONArray. The separator string + * is inserted between each element. + * Warning: This method assumes that the data structure is acyclical. + * @param separator A string that will be inserted between the elements. + * @return a string. + *) +function JSONArray.join(separator: string): string; +var + len, i: integer; + sb: string ; +begin + len:=length(); + sb:=''; + for i:=0 to len -1 do + begin + if (i > 0) then + sb:=sb + separator; + sb:= sb + JSONObject.valueToString(TZAbstractObject(myArrayList[i])); + end; + Result:=sb; +end; + +function JSONArray.LastItem: TZAbstractObject; +var + Len:Integer; +begin + Len:=length(); + if Len=0 then + Result:=nil + else + Result:=TZAbstractObject(TZAbstractObject(myArrayList[Len-1])); +end; + +(** + * Get the length of the JSONArray. + * + * @return The length (or size). + *) +function JSONArray.length: integer; +begin + Result:=myArrayList.Count; +end; + + (** + * Get the optional object value associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return An object value, or null if there is no + * object at that index. + *) +function JSONArray.opt(index: integer): TZAbstractObject; +begin + if ((index < 0) or (index >= length()) ) then + Result:=nil + else + Result:=TZAbstractObject (myArrayList[index]); +end; + +(** + * Get the optional boolean value associated with an index. + * It returns false if there is no value at that index, + * or if the value is not Boolean.TRUE or the String "true". + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + *) +function JSONArray.optBoolean(index: integer): boolean; +begin + Result:=optBoolean(index, false); +end; + +(** + * Get the optional boolean value associated with an index. + * It returns the defaultValue if there is no value at that index or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. + * @return The truth. + *) +function JSONArray.optBoolean(index: integer; + defaultValue: boolean): boolean; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + begin + if ((o.equals(_Boolean._FALSE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('false')))) then begin + Result:=false; + exit; + end + else if ((o.equals(_Boolean._TRUE) or + ((o is _String) and + (_String(o)).equalsIgnoreCase('true')))) then begin + Result:=true; + exit; + end; + end; + Result:=defaultValue; +end; + + +(** + * Get the optional double value associated with an index. + * NaN is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + *) +function JSONArray.optDouble(index: integer): double; +begin + Result:=optDouble(index, _Double.NaN); +end; + +(** + * Get the optional double value associated with an index. + * The defaultValue is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * + * @param index subscript + * @param defaultValue The default value. + * @return The value. + *) +function JSONArray.optDouble(index: integer; defaultValue :double): double; +var + o: TZAbstractObject; + d: _Double; +begin + o:=opt(index); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).doubleValue(); + exit; + end; + try + d:=_Double.create (_String (o)); + Result:=d.doubleValue ; + d.Free; + exit; + except + on e:Exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + +(** + * Get the optional int value associated with an index. + * Zero is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + *) +function JSONArray.optInt(index: integer): integer; +begin + Result:=optInt(index, 0); +end; + + +(** + * Get the optional int value associated with an index. + * The defaultValue is returned if the index is not found, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + *) +function JSONArray.optInt(index, defaultValue: integer): integer; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).intValue(); + exit; //By creation_zy + end; + try + Result:=_Integer.parseInt(_String(o)); + exit; + except + on e: exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + + +function JSONArray.OptInt64(index: integer): int64; +begin + Result := OptInt64(index, 0); +end; + +function JSONArray.OptInt64(index: integer; defaultValue: int64): int64; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + begin + if (o is _Number) then + begin + Result:=(_Number(o)).int64Value(); + exit; //By creation_zy + end; + try + Result:=_Integer.parseInt64(_String(o)); + exit; + except + on e: exception do + begin + Result:=defaultValue; + exit; + end; + end; + end; + Result:=defaultValue; +end; + +(** + * Get the optional JSONArray associated with an index. + * @param index subscript + * @return A JSONArray value, or null if the index has no value, + * or if the value is not a JSONArray. + *) +function JSONArray.optJSONArray(index: integer): JSONArray; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o is JSONArray) then + Result:=JSONArray(o) + else + Result:=nil; +end; + +(** + * Get the optional JSONObject associated with an index. + * Null is returned if the key is not found, or null if the index has + * no value, or if the value is not a JSONObject. + * + * @param index The index must be between 0 and length() - 1. + * @return A JSONObject value. + *) +function JSONArray.optJSONObject(index: integer): JSONObject; +var + o: TZAbstractObject; +begin + o:=opt(index); + if o is JSONObject then + Result:=JSONObject(o) + else + Result:=nil; +end; + +{$IFDEF J_OBJECT} +function JSONArray.optObject(index: integer): TObject; +var + o: TZAbstractObject; +begin + o:=opt(index); + if o is _Object then + Result:=_Object(o).fvalue + else + Result:=nil; +end; +{$ENDIF} + +(** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value + * is not a string and is not null, then it is coverted to a string. + * + * @param index The index must be between 0 and length() - 1. + * @return A String value. + *) +function JSONArray.optString(index: integer): string; +begin + Result:=optString(index, ''); +end; + +(** + * Get the optional string associated with an index. + * The defaultValue is returned if the key is not found. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return A String value. + *) +function JSONArray.optString(index: integer; defaultValue: string): string; +var + o: TZAbstractObject; +begin + o:=opt(index); + if (o <> nil) then + Result:=o.toString() + else + Result:=defaultValue; +end; + +(** + * Append a boolean value. + * + * @param value A boolean value. + * @return this. + *) +function JSONArray.put(value: boolean): JSONArray; +begin + put(_Boolean.valueOf(value)); + Result:= self; +end; + +(** + * Append a double value. + * + * @param value A double value. + * @return this. + *) +function JSONArray.put(value: double): JSONArray; +begin + put(_Double.create(value)); + Result:=self; +end; + +(** + * Append an int value. + * + * @param value An int value. + * @return this. + *) +function JSONArray.put(value: integer): JSONArray; +begin + put(_Integer.create(value)); + Result:=self; +end; + + +function JSONArray.put(value: string): JSONArray; +begin + put (_String.create (value)); + Result:=self; +end; + +{$IFDEF J_OBJECT} +function JSONArray.put ( value: TObject): JSONArray; +begin + put (_Object.create (value)); + Result:=self; +end; +{$ENDIF} + +(** + * Append an object value. + * @param value An object value. The value should be a + * Boolean, Double, Integer, JSONArray, JSObject, or String, or the + * JSONObject.NULL object. + * @return this. + *) +function JSONArray.put(value: TZAbstractObject): JSONArray; +begin + myArrayList.add(value); + Result:=self; +end; + +(** + * Put or replace a boolean value in the JSONArray. + * @param index subscript The subscript. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param value A boolean value. + * @return this. + * @exception NoSuchElementException The index must not be negative. + *) +function JSONArray.put(index: integer; value: boolean): JSONArray; +begin + put(index, _Boolean.valueOf(value)); + Result:=self; +end; + +function JSONArray.put(index, value: integer): JSONArray; +begin + put(index, _Integer.create(value)); + Result:=self; +end; + + +function JSONArray.put(index: integer; value: double): JSONArray; +begin + put(index, _Double.create(value)); + Result:=self; +end; + +function JSONArray.put(index: integer; value: string): JSONArray; +begin + put (index,_String.create (value)); + Result:=self; +end; + +(** + * Put or replace an object value in the JSONArray. + * @param index The subscript. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param value An object value. + * @return this. + * @exception NoSuchElementException The index must not be negative. + * @exception NullPointerException The index must not be null. + *) +function JSONArray.put(index: integer; value: TZAbstractObject): JSONArray; +begin + if (index < 0) then + raise NoSuchElementException.create('JSONArray['+intToStr(index)+'] not found.') + else if (value = nil) then + raise NullPointerException.create('') + else if (index < length()) then + myArrayList[index]:=value + else begin + while (index<>length()) do put(nil); + put(value); + end; + Result:=self; +end; + +{$IFDEF J_OBJECT} +function JSONArray.put(index: integer; value: TObject): JSONArray; +begin + put (index,_Object.create(value)); + Result:=self; +end; + +function JSONArray.put(index: integer; value: int64): JSONArray; +begin + put(index, _Integer.create(value)); + Result:=self; +end; + +procedure JSONArray.SetChild(Index: Integer; const Value: TZAbstractObject); +begin + put(index, Value); +end; + +{$ENDIF} + +(** + * Produce a JSONObject by combining a JSONArray of names with the values + * of this JSONArray. + * @param names A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + *) +function JSONArray.toJSONObject(names :JSONArray): JSONObject; +var + i: integer; +begin + if ((names = nil) or (names.length() = 0) or (length() = 0)) then + begin + Result:=nil; + exit; //By creation_zy + end; + Result:=JSONObject.create(); + for i:=0 to names.length() do + Result.put(names.getString(i), self.opt(i)); +end; + + +(** + * Make an JSON external form string of this JSONArray. For compactness, no + * unnecessary whitespace is added. + * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable + * representation of the array. + *) +function JSONArray.toString: string; +begin + Result:='[' + join(',') + ']'; +end; + +(** + * Make a prettyprinted JSON string of this JSONArray. + * Warning: This method assumes that the data structure is non-cyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with [ (left bracket) and ending + * with ] (right bracket). + *) +function JSONArray.toString2(indentFactor: integer): string; +begin + Result:=toString3(indentFactor, 0); +end; + +(** + * Make a prettyprinted string of this JSONArray. + * Warning: This method assumes that the data structure is non-cyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indention of the top level. + * @return a printable, displayable, transmittable + * representation of the array. + *) +function JSONArray.toList: TList; +begin + Result:=TList.create ; + Result.Assign(myArrayList,laCopy); +end; + +function JSONArray.toString3(indentFactor, indent: integer): string; +var + len, i, newindent: integer; + sb: string; +begin + len:=length(); + if (len = 0) then + begin + Result:='[]'; + exit; + end; + sb:='['; + if (len = 1) then + begin + sb:=sb + JSONObject + .valueToString(TZAbstractObject( myArrayList[0]),indentFactor, indent); + end + else begin + newindent:=indent + indentFactor; + sb:=sb + #10 ; + for i:=0 to len -1 do + begin + if i > 0 then + sb:=sb +',' + #10; + sb:=sb + SpaceStr(newindent) + (JSONObject.valueToString(TZAbstractObject(myArrayList[i]), + indentFactor, newindent)); + end; + sb:=sb + #10 + SpaceStr(indent); + end; + sb:=sb + ']'; + Result:=sb; +end; + + +{ _NULL } + +function _NULL.Clone: TZAbstractObject; +begin + Result:=CNULL; +end; + +function _NULL.Equals(const Value: TZAbstractObject): Boolean; +begin + if (value = nil) then + Result:=true + else + Result:=(value is _NULL); +end; + +function _NULL.toString: string; +begin + Result:='null'; +end; + + +{ TZAbstractObject } + +class procedure TZAbstractObject.WriteChar(avOut: TStream; const avData: Char); +begin + avOut.WriteBuffer(avData, SizeOf(Char)); +end; + +class procedure TZAbstractObject.WriteString(avOut: TStream; const avData: string); +var + l: Cardinal; +begin + l := Length(avData); + if l > 0 then + avOut.WriteBuffer(avData[1], l); +end; + +class procedure TZAbstractObject.WriteText(avOut: TStream; const avData: string; + len: Integer); +begin + if len > 0 then + avOut.WriteBuffer(avData[1], len); +end; + +function TZAbstractObject.Clone: TZAbstractObject; +begin + Result:=nil; + newNotImplmentedFeature(); +end; + +function TZAbstractObject.Equals(const Value: TZAbstractObject): Boolean; +begin + Result:=(value <> nil) and (value = self); +end; + +procedure TZAbstractObject.Free; +begin + SafeFreeJObj(Self); +end; + +class function TZAbstractObject.getBoolean(o: TZAbstractObject; DefaultValue: Boolean): Boolean; +begin + if (o<>CNULL) and (o<>nil) then + begin + if o.ClassType=_Boolean then //2009-03-06 By creation_zy + begin + Result:=_Boolean(o).fvalue; + exit; + end + else if ((o is _String) and (_String(o).equalsIgnoreCase('false'))) then + begin + Result:=false; + exit; + end + else if ((o is _String) and (_String(o).equalsIgnoreCase('true'))) then + begin + Result:=true; + exit; + end; + end; + Result:=DefaultValue; +end; + +class function TZAbstractObject.getDouble(o: TZAbstractObject; DefaultValue: Double): Double; +begin + if (o<>CNULL) and ( o <> nil ) then + begin + if (o is _Number) then + begin + Result:= _Number(o).doubleValue(); + exit; + end; + if o.ClassType=_String then + Result:=StrToFloatDef(o.toString,DefaultValue) + else + Result:=defaultValue; + end + else //By creation_zy + Result:=defaultValue; +end; + +class function TZAbstractObject.getInt(o: TZAbstractObject; DefaultValue: Integer): Integer; +begin + if (o<>CNULL) and ( o <> nil ) then + begin + if (o is _Number) then + begin + Result:=_Number(o).intValue(); + exit; + end; + if o.ClassType<>_String then + Result:=defaultValue + else + try + Result:=_Integer.parseInt(_String(o)); + except + Result:=defaultValue; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +class function TZAbstractObject.getInt64(o: TZAbstractObject; + DefaultValue: Int64): Int64; +begin + if (o<>CNULL) and ( o <> nil ) then + begin + if (o is _Number) then + begin + Result:=_Number(o).int64Value(); + exit; + end; + if o.ClassType<>_String then + Result:=defaultValue + else + try + Result:=_Integer.parseInt64(_String(o)); + except + Result:=defaultValue; + end; + end + else //By creation_zy + Result:=defaultValue; +end; + +function TZAbstractObject.Hash: LongInt; +begin + Result:=integer(addr(self)); +end; + +function TZAbstractObject.InstanceOf( + const Value: TZAbstractObject): Boolean; +begin + Result:=value is TZAbstractObject; +end; + +procedure TZAbstractObject.SaveToStream(stream: TStream); +begin + WriteString(stream, Format('%s <%p>', [ClassName, addr(Self)])); +end; + +function TZAbstractObject.toJSONArray: JSONArray; +begin + if Self is JSONArray then + Result := JSONArray(Self) + else + Result := nil; +end; + +function TZAbstractObject.toJSONObject: JSONObject; +begin + if Self is JSONObject then + Result := JSONObject(Self) + else + Result := nil; +end; + +function TZAbstractObject.ToString: string; +begin + Result:=Format('%s <%p>', [ClassName, addr(Self)]); +end; + +{$IFDEF J_OBJECT} +{ _Object } + +function _Object.Clone: TZAbstractObject; +begin + Result:=_Object.Create(fvalue); +end; + +constructor _Object.Create(value: TObject); +begin + fvalue:=value; +end; + +function _Object.Equals(const Value: TZAbstractObject): Boolean; +begin + Result:=(Value is _Object) and (_Object(Value).AsObject=AsObject); +end; + +procedure _Object.SetAsObject(const Value: TObject); +begin + fvalue:=Value; +end; + +function _Object.toString: string; +begin + if fvalue=nil then + Result:='' + else + Result:=fvalue.ClassName+'::'+IntToHex(Integer(fvalue),8); +end; +{$ENDIF} + +{ JSONBase } + +constructor JSONBase.Create; +begin + FParent := nil; +end; + +function JSONBase.GetChild(Index: Integer): TZAbstractObject; +begin + Result := nil; +end; + +function JSONBase.GetCount: Integer; +begin + Result := 0; +end; + +function JSONBase.IndexOfObject(aobj: TObject): Integer; +begin + Result := -1; +end; + +procedure JSONBase.SetChild(Index: Integer; const Value: TZAbstractObject); +begin +end; + +initialization + CONST_FALSE:=_Boolean.Create(false); + CONST_TRUE:=_Boolean.Create(true); + CNULL:=_NULL.Create; + +finalization + TObject(CONST_FALSE).Free; + TObject(CONST_TRUE).Free; + TObject(CNULL).Free; + +end. diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uLkJSON.pas" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uLkJSON.pas" new file mode 100644 index 0000000..05928f6 --- /dev/null +++ "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/uLkJSON.pas" @@ -0,0 +1,2626 @@ +{ + LkJSON v1.07 + + 06 november 2009 + +* Copyright (c) 2006,2007,2008,2009 Leonid Koninin +* leon_kon@users.sourceforge.net +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY Leonid Koninin ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Leonid Koninin BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + changes: + + v1.07 06/11/2009 * fixed a bug in js_string - thanks to Andrew G. Khodotov + * fixed error with double-slashes - thanks to anonymous user + * fixed a BOM bug in parser, thanks to jasper_dale + v1.06 13/03/2009 * fixed a bug in string parsing routine + * looked routine from the Adrian M. Jones, and get some + ideas from it; thanks a lot, Adrian! + * checked error reported by phpop and fix it in the string + routine; also, thanks for advice. + v1.05 26/01/2009 + added port to D2009 by Daniele Teti, thanx a lot! really, + i haven't the 2009 version, so i can't play with it. I was + add USE_D2009 directive below, disabled by default + * fixed two small bugs in parsing object: errors with empty + object and list; thanx to RSDN's delphi forum members + * fixed "[2229135] Value deletion is broken" tracker + issue, thanx to anonymous sender provided code for + tree version + * fixed js_string according to "[1917047] (much) faster + js_string Parse" tracker issue by Joao Inacio; a lot of + thanx, great speedup! + + v1.04 05/04/2008 + a declaration of Field property moved from TlkJSONobject + to TlkJSONbase; thanx for idea to Andrey Lukyanov; this + improve objects use, look the bottom of SAMPLE2.DPR + * fixed field name in TlkJSONobject to WideString + v1.03 14/03/2008 + added a code for generating readable JSON text, sended to + me by Kusnassriyanto Saiful Bahri, thanx to him! + * from this version, library distributed with BSD + license, more pleasure for commercial programmers :) + * was rewritten internal storing of objects, repacing + hash tables with balanced trees (AA tree, by classic + author's variant). On mine machine, with enabled fastmm, + tree variant is about 30% slower in from-zero creation, + but about 50% faster in parsing; also deletion of + objects will be much faster than a hash-one. + Hashes (old-style) can be switched on by enabling + USE_HASH directive below + v1.02 14/09/2007 * fix mistypes in diffrent places; thanx for reports + to Aleksandr Fedorov and Tobias Wrede + v1.01 18/05/2007 * fix small bug in new text generation routine, check + library for leaks by fastmm4; thanx for idea and comments + for Glynn Owen + v1.00 12/05/2007 * some fixes in new code (mistypes, mistypes...) + * also many fixes by ideas of Henri Gourvest - big thanx + for him again; he send me code for thread-safe initializing + of hash table, some FPC-compatible issues (not tested by + myself) and better code for localization in latest + delphi versions; very, very big thanx! + * rewritten procedure of json text generating, with wich + work of it speeds up 4-5 times (on test) its good for + a large objects + * started a large work for making source code self-doc + (not autodoc!) + v0.99 10/05/2007 + add functions to list and object: + function getInt(idx: Integer): Integer; + function getString(idx: Integer): String; + function getWideString(idx: Integer):WideString; + function getDouble(idx: Integer): Double; + function getBoolean(idx: Integer): Boolean; + + add overloaded functions to object: + function getDouble(nm: String): Double; overload; + function getInt(nm: String): Integer; overload; + function getString(nm: String): String; overload; + function getWideString(nm: String): WideString; overload; + function getBoolean(nm: String): Boolean; overload; + * changed storing mech of TlkJSONcustomlist descendants from + dynamic array to TList; this gives us great speedup with + lesser changes; thanx for idea to Henri Gourvest + * also reworked hashtable to work with TList, so it also + increase speed of work + v0.98 09/05/2007 * fix small bug in work with WideStrings(UTF8), thanx to + IVO GELOV to description and sources + v0.97 10/04/2007 + add capabilities to work with KOL delphi projects; for + this will define KOL variable in begin of text; of course, + in this case object TlkJSONstreamed is not compiled. + v0.96 03/30/2007 + add TlkJSONFuncEnum and method ForEach in all + TlkJSONcustomlist descendants + + add property UseHash(r/o) to TlkJSONobject, and parameter + UseHash:Boolean to object constructors; set it to false + allow to disable using of hash-table, what can increase + speed of work in case of objects with low number of + methods(fields); [by default it is true] + + added conditional compile directive DOTNET for use in .Net + based delphi versions; remove dot in declaration below + (thanx for idea and sample code to Tim Radford) + + added property HashOf to TlkHashTable to allow use of + users hash functions; on enter is widestring, on exit is + cardinal (32 bit unsigned). Original HashOf renamed to + DefaultHashOf + * hash table object of TlkJSONobject wrapped by property called + HashTable + * fixed some minor bugs + v0.95 03/29/2007 + add object TlkJSONstreamed what descendant of TlkJSON and + able to load/save JSON objects from/to streams/files. + * fixed small bug in generating of unicode strings representation + v0.94 03/27/2007 + add properties NameOf and FieldByIndex to TlkJSONobject + * fix small error in parsing unicode chars + * small changes in hashing code (try to speed up) + v0.93 03/05/2007 + add overloaded functions to list and object + + add enum type TlkJSONtypes + + add functions: SelfType:TlkJSONtypes and + SelfTypeName: String to every TlkJSONbase child + * fix mistype 'IndefOfName' to 'IndexOfName' + * fix mistype 'IndefOfObject' to 'IndexOfObject' + v0.92 03/02/2007 + add some fix to TlkJSON.ParseText to fix bug with parsing + objects - object methods not always added properly + to hash array (thanx to Chris Matheson) + ... +} + +unit uLkJSON; + +{$IFDEF fpc} + {$MODE objfpc} + {$H+} + {.$DEFINE HAVE_FORMATSETTING} +{$ELSE} + {$IF RTLVersion > 14.00} + {$DEFINE HAVE_FORMATSETTING} + {$IF RTLVersion > 19.00} + {$DEFINE USE_D2009} + {$IFEND} + {$IFEND} +{$ENDIF} + +interface + +{.$DEFINE USE_D2009} +{.$DEFINE KOL} +{.$define DOTNET} +{$DEFINE THREADSAFE} +{$DEFINE NEW_STYLE_GENERATE} +{.$DEFINE USE_HASH} +{.$DEFINE TCB_EXT} + +uses windows, + SysUtils, +{$IFNDEF KOL} + classes, +{$ELSE} + kol, +{$ENDIF} + variants; + +type + TlkJSONtypes = (jsBase, jsNumber, jsString, jsBoolean, jsNull, + jsList, jsObject); + +{$IFDEF DOTNET} + + TlkJSONdotnetclass = class + public + constructor Create; + destructor Destroy; override; + procedure AfterConstruction; virtual; + procedure BeforeDestruction; virtual; + end; + +{$ENDIF DOTNET} + + TlkJSONbase = class{$IFDEF DOTNET}(TlkJSONdotnetclass){$ENDIF} + protected + function GetValue: variant; virtual; + procedure SetValue(const AValue: variant); virtual; + function GetChild(idx: Integer): TlkJSONbase; virtual; + procedure SetChild(idx: Integer; const AValue: TlkJSONbase); + virtual; + function GetCount: Integer; virtual; + function GetField(AName: Variant):TlkJSONbase; virtual; + public + property Field[AName: Variant]: TlkJSONbase read GetField; + property Count: Integer read GetCount; + property Child[idx: Integer]: TlkJSONbase read GetChild write SetChild; + property Value: variant read GetValue write SetValue; + class function SelfType: TlkJSONtypes; virtual; + class function SelfTypeName: string; virtual; + end; + + TlkJSONnumber = class(TlkJSONbase) + protected + FValue: extended; + function GetValue: Variant; override; + procedure SetValue(const AValue: Variant); override; + public + procedure AfterConstruction; override; + class function Generate(AValue: extended = 0): TlkJSONnumber; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONstring = class(TlkJSONbase) + protected + FValue: WideString; + function GetValue: Variant; override; + procedure SetValue(const AValue: Variant); override; + public + procedure AfterConstruction; override; + class function Generate(const wsValue: WideString = ''): + TlkJSONstring; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONboolean = class(TlkJSONbase) + protected + FValue: Boolean; + function GetValue: Variant; override; + procedure SetValue(const AValue: Variant); override; + public + procedure AfterConstruction; override; + class function Generate(AValue: Boolean = true): TlkJSONboolean; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONnull = class(TlkJSONbase) + protected + function GetValue: Variant; override; + function Generate: TlkJSONnull; + public + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONFuncEnum = procedure(ElName: string; Elem: TlkJSONbase; + data: pointer; var Continue: Boolean) of object; + + TlkJSONcustomlist = class(TlkJSONbase) + protected +// FValue: array of TlkJSONbase; + fList: TList; + function GetCount: Integer; override; + function GetChild(idx: Integer): TlkJSONbase; override; + procedure SetChild(idx: Integer; const AValue: TlkJSONbase); + override; + function ForEachElement(idx: Integer; var nm: string): + TlkJSONbase; virtual; + + function GetField(AName: Variant):TlkJSONbase; override; + + function _Add(obj: TlkJSONbase): Integer; virtual; + procedure _Delete(iIndex: Integer); virtual; + function _IndexOf(obj: TlkJSONbase): Integer; virtual; + public + procedure ForEach(fnCallBack: TlkJSONFuncEnum; pUserData: + pointer); + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + + function getInt(idx: Integer): Integer; virtual; + function getString(idx: Integer): string; virtual; + function getWideString(idx: Integer): WideString; virtual; + function getDouble(idx: Integer): Double; virtual; + function getBoolean(idx: Integer): Boolean; virtual; + end; + + TlkJSONlist = class(TlkJSONcustomlist) + protected + public + function Add(obj: TlkJSONbase): Integer; overload; + + function Add(aboolean: Boolean): Integer; overload; + function Add(nmb: double): Integer; overload; + function Add(s: string): Integer; overload; + function Add(const ws: WideString): Integer; overload; + function Add(inmb: Integer): Integer; overload; + + procedure Delete(idx: Integer); + function IndexOf(obj: TlkJSONbase): Integer; + class function Generate: TlkJSONlist; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + end; + + TlkJSONobjectmethod = class(TlkJSONbase) + protected + FValue: TlkJSONbase; + FName: WideString; + procedure SetName(const AValue: WideString); + public + property ObjValue: TlkJSONbase read FValue; + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + property Name: WideString read FName write SetName; + class function Generate(const aname: WideString; aobj: TlkJSONbase): + TlkJSONobjectmethod; + end; + +{$IFDEF USE_HASH} + PlkHashItem = ^TlkHashItem; + TlkHashItem = packed record + hash: cardinal; + index: Integer; + end; + + TlkHashFunction = function(const ws: WideString): cardinal of + object; + + TlkHashTable = class + private + FParent: TObject; // TCB:parent for check chaining op. + FHashFunction: TlkHashFunction; + procedure SetHashFunction(const AValue: TlkHashFunction); + protected + a_x: array[0..255] of TList; + procedure hswap(j, k, l: Integer); + function InTable(const ws: WideString; var i, j, k: cardinal): + Boolean; + public + function counters: string; + + function DefaultHashOf(const ws: WideString): cardinal; + function SimpleHashOf(const ws: WideString): cardinal; + + property HashOf: TlkHashFunction read FHashFunction write + SetHashFunction; + + function IndexOf(const ws: WideString): Integer; + + procedure AddPair(const ws: WideString; idx: Integer); + procedure Delete(const ws: WideString); + + constructor Create; + destructor Destroy; override; + end; + +{$ELSE} + +// implementation based on "Arne Andersson, Balanced Search Trees Made Simpler" + + PlkBalNode = ^TlkBalNode; + TlkBalNode = packed record + left,right: PlkBalNode; + level: byte; + key: Integer; + nm: WideString; + end; + + TlkBalTree = class + protected + fdeleted,flast,fbottom,froot: PlkBalNode; + procedure skew(var t:PlkBalNode); + procedure split(var t:PlkBalNode); + public + function counters: string; + + procedure Clear; + + function Insert(const ws: WideString; x: Integer): Boolean; + function Delete(const ws: WideString): Boolean; + + function IndexOf(const ws: WideString): Integer; + + constructor Create; + destructor Destroy; override; + end; +{$ENDIF USE_HASH} + + TlkJSONobject = class(TlkJSONcustomlist) + protected +{$IFDEF USE_HASH} + ht: TlkHashTable; +{$ELSE} + ht: TlkBalTree; +{$ENDIF USE_HASH} + FUseHash: Boolean; + function GetFieldByIndex(idx: Integer): TlkJSONbase; + function GetNameOf(idx: Integer): WideString; + procedure SetFieldByIndex(idx: Integer; const AValue: TlkJSONbase); +{$IFDEF USE_HASH} + function GetHashTable: TlkHashTable; +{$ELSE} + function GetHashTable: TlkBalTree; +{$ENDIF USE_HASH} + function ForEachElement(idx: Integer; var nm: string): TlkJSONbase; + override; + function GetField(AName: Variant):TlkJSONbase; override; + public + property UseHash: Boolean read FUseHash; +{$IFDEF USE_HASH} + property HashTable: TlkHashTable read GetHashTable; +{$ELSE} + property HashTable: TlkBalTree read GetHashTable; +{$ENDIF USE_HASH} + + function Add(const aname: WideString; aobj: TlkJSONbase): Integer; + overload; + + function OldGetField(nm: WideString): TlkJSONbase; + procedure OldSetField(nm: WideString; const AValue: TlkJSONbase); + + function Add(const aname: WideString; aboolean: Boolean): Integer; overload; + function Add(const aname: WideString; nmb: double): Integer; overload; + function Add(const aname: WideString; s: string): Integer; overload; + function Add(const aname: WideString; const ws: WideString): Integer; + overload; + function Add(const aname: WideString; inmb: Integer): Integer; overload; + + procedure Delete(idx: Integer); + function IndexOfName(const aname: WideString): Integer; + function IndexOfObject(aobj: TlkJSONbase): Integer; + property Field[nm: WideString]: TlkJSONbase read OldGetField + write OldSetField; default; + + constructor Create(bUseHash: Boolean = true); + destructor Destroy; override; + + class function Generate(AUseHash: Boolean = true): TlkJSONobject; + class function SelfType: TlkJSONtypes; override; + class function SelfTypeName: string; override; + + property FieldByIndex[idx: Integer]: TlkJSONbase read GetFieldByIndex + write SetFieldByIndex; + property NameOf[idx: Integer]: WideString read GetNameOf; + + function getDouble(idx: Integer): Double; overload; override; + function getInt(idx: Integer): Integer; overload; override; + function getString(idx: Integer): string; overload; override; + function getWideString(idx: Integer): WideString; overload; override; + function getBoolean(idx: Integer): Boolean; overload; override; + + function {$ifdef TCB_EXT}getDoubleFromName{$else}getDouble{$endif} + (nm: string): Double; overload; + function {$ifdef TCB_EXT}getIntFromName{$else}getInt{$endif} + (nm: string): Integer; overload; + function {$ifdef TCB_EXT}getStringFromName{$else}getString{$endif} + (nm: string): string; overload; + function {$ifdef TCB_EXT}getWideStringFromName{$else}getWideString{$endif} + (nm: string): WideString; overload; + function {$ifdef TCB_EXT}getBooleanFromName{$else}getBoolean{$endif} + (nm: string): Boolean; overload; + end; + + TlkJSON = class + public + class function ParseText(const txt: string): TlkJSONbase; + class function GenerateText(obj: TlkJSONbase): string; + end; + +{$IFNDEF KOL} + TlkJSONstreamed = class(TlkJSON) + class function LoadFromStream(src: TStream): TlkJSONbase; + class procedure SaveToStream(obj: TlkJSONbase; dst: TStream); + class function LoadFromFile(srcname: string): TlkJSONbase; + class procedure SaveToFile(obj: TlkJSONbase; dstname: string); + end; +{$ENDIF} + +function GenerateReadableText(vObj: TlkJSONbase; var vLevel: + Integer): string; + +implementation + +uses math,strutils; + +type + ElkIntException = class(Exception) + public + idx: Integer; + constructor Create(idx: Integer; msg: string); + end; + +// author of next two functions is Kusnassriyanto Saiful Bahri + +function Indent(vTab: Integer): string; +begin + result := DupeString(' ', vTab); +end; + +function GenerateReadableText(vObj: TlkJSONbase; var vLevel: + Integer): string; +var + i: Integer; + vStr: string; + xs: TlkJSONstring; +begin + vLevel := vLevel + 1; + if vObj is TlkJSONObject then + begin + vStr := ''; + for i := 0 to TlkJSONobject(vObj).Count - 1 do + begin + if vStr <> '' then + begin + vStr := vStr + ','#13#10; + end; + vStr := vStr + Indent(vLevel) + + GenerateReadableText(TlkJSONobject(vObj).Child[i], vLevel); + end; + if vStr <> '' then + begin + vStr := '{'#13#10 + vStr + #13#10 + Indent(vLevel - 1) + '}'; + end + else + begin + vStr := '{}'; + end; + result := vStr; + end + else if vObj is TlkJSONList then + begin + vStr := ''; + for i := 0 to TlkJSONList(vObj).Count - 1 do + begin + if vStr <> '' then + begin + vStr := vStr + ','#13#10; + end; + vStr := vStr + Indent(vLevel) + + GenerateReadableText(TlkJSONList(vObj).Child[i], vLevel); + end; + if vStr <> '' then + begin + vStr := '['#13#10 + vStr + #13#10 + Indent(vLevel - 1) + ']'; + end + else + begin + vStr := '[]'; + end; + result := vStr; + end + else if vObj is TlkJSONobjectmethod then + begin + vStr := ''; + xs := TlkJSONstring.Create; + try + xs.Value := TlkJSONobjectMethod(vObj).Name; + vStr := GenerateReadableText(xs, vLevel); + vLevel := vLevel - 1; + vStr := vStr + ':' + GenerateReadableText(TlkJSONbase( + TlkJSONobjectmethod(vObj).ObjValue), vLevel); + //vStr := vStr + ':' + GenerateReadableText(TlkJSONbase(vObj), vLevel); + vLevel := vLevel + 1; + result := vStr; + finally + xs.Free; + end; + end + else + begin + if vObj is TlkJSONobjectmethod then + begin + if TlkJSONobjectMethod(vObj).Name <> '' then + begin + end; + end; + result := TlkJSON.GenerateText(vObj); + end; + vLevel := vLevel - 1; +end; + +// author of this routine is IVO GELOV + +function code2utf(iNumber: Integer): UTF8String; +begin + if iNumber < 128 then Result := chr(iNumber) + else if iNumber < 2048 then + Result := chr((iNumber shr 6) + 192) + chr((iNumber and 63) + 128) + else if iNumber < 65536 then + Result := chr((iNumber shr 12) + 224) + chr(((iNumber shr 6) and + 63) + 128) + chr((iNumber and 63) + 128) + else if iNumber < 2097152 then + Result := chr((iNumber shr 18) + 240) + chr(((iNumber shr 12) and + 63) + 128) + chr(((iNumber shr 6) and 63) + 128) + + chr((iNumber and 63) + 128); +end; + +{ TlkJSONbase } + +function TlkJSONbase.GetChild(idx: Integer): TlkJSONbase; +begin + result := nil; +end; + +function TlkJSONbase.GetCount: Integer; +begin + result := 0; +end; + +function TlkJSONbase.GetField(AName: Variant):TlkJSONbase; +begin + result := self; +end; + +function TlkJSONbase.GetValue: variant; +begin + result := variants.Null; +end; + +class function TlkJSONbase.SelfType: TlkJSONtypes; +begin + result := jsBase; +end; + +class function TlkJSONbase.SelfTypeName: string; +begin + result := 'jsBase'; +end; + +procedure TlkJSONbase.SetChild(idx: Integer; const AValue: + TlkJSONbase); +begin + +end; + +procedure TlkJSONbase.SetValue(const AValue: variant); +begin + +end; + +{ TlkJSONnumber } + +procedure TlkJSONnumber.AfterConstruction; +begin + inherited; + FValue := 0; +end; + +class function TlkJSONnumber.Generate(AValue: extended): + TlkJSONnumber; +begin + result := TlkJSONnumber.Create; + result.FValue := AValue; +end; + +function TlkJSONnumber.GetValue: Variant; +begin + result := FValue; +end; + +class function TlkJSONnumber.SelfType: TlkJSONtypes; +begin + result := jsNumber; +end; + +class function TlkJSONnumber.SelfTypeName: string; +begin + result := 'jsNumber'; +end; + +procedure TlkJSONnumber.SetValue(const AValue: Variant); +begin + FValue := VarAsType(AValue, varDouble); +end; + +{ TlkJSONstring } + +procedure TlkJSONstring.AfterConstruction; +begin + inherited; + FValue := ''; +end; + +class function TlkJSONstring.Generate(const wsValue: WideString): + TlkJSONstring; +begin + result := TlkJSONstring.Create; + result.FValue := wsValue; +end; + +function TlkJSONstring.GetValue: Variant; +begin + result := FValue; +end; + +class function TlkJSONstring.SelfType: TlkJSONtypes; +begin + result := jsString; +end; + +class function TlkJSONstring.SelfTypeName: string; +begin + result := 'jsString'; +end; + +procedure TlkJSONstring.SetValue(const AValue: Variant); +begin + FValue := VarToWideStr(AValue); +end; + +{ TlkJSONboolean } + +procedure TlkJSONboolean.AfterConstruction; +begin + FValue := false; +end; + +class function TlkJSONboolean.Generate(AValue: Boolean): + TlkJSONboolean; +begin + result := TlkJSONboolean.Create; + result.Value := AValue; +end; + +function TlkJSONboolean.GetValue: Variant; +begin + result := FValue; +end; + +class function TlkJSONboolean.SelfType: TlkJSONtypes; +begin + Result := jsBoolean; +end; + +class function TlkJSONboolean.SelfTypeName: string; +begin + Result := 'jsBoolean'; +end; + +procedure TlkJSONboolean.SetValue(const AValue: Variant); +begin + FValue := boolean(AValue); +end; + +{ TlkJSONnull } + +function TlkJSONnull.Generate: TlkJSONnull; +begin + result := TlkJSONnull.Create; +end; + +function TlkJSONnull.GetValue: Variant; +begin + result := variants.Null; +end; + +class function TlkJSONnull.SelfType: TlkJSONtypes; +begin + result := jsNull; +end; + +class function TlkJSONnull.SelfTypeName: string; +begin + result := 'jsNull'; +end; + +{ TlkJSONcustomlist } + +function TlkJSONcustomlist._Add(obj: TlkJSONbase): Integer; +begin + if not Assigned(obj) then + begin + result := -1; + exit; + end; + result := fList.Add(obj); +end; + +procedure TlkJSONcustomlist.AfterConstruction; +begin + inherited; + fList := TList.Create; +end; + +procedure TlkJSONcustomlist.BeforeDestruction; +var + i: Integer; +begin + for i := (Count - 1) downto 0 do _Delete(i); + fList.Free; + inherited; +end; + +// renamed + +procedure TlkJSONcustomlist._Delete(iIndex: Integer); +var + idx: Integer; +begin + if not ((iIndex < 0) or (iIndex >= Count)) then + begin + if fList.Items[iIndex] <> nil then + TlkJSONbase(fList.Items[iIndex]).Free; + idx := pred(fList.Count); + if iIndex= Count) then + begin + result := nil; + end + else + begin + result := fList.Items[idx]; + end; +end; + +function TlkJSONcustomlist.GetCount: Integer; +begin + result := fList.Count; +end; + +function TlkJSONcustomlist._IndexOf(obj: TlkJSONbase): Integer; +begin + result := fList.IndexOf(obj); +end; + +procedure TlkJSONcustomlist.SetChild(idx: Integer; const AValue: + TlkJSONbase); +begin + if not ((idx < 0) or (idx >= Count)) then + begin + if fList.Items[idx] <> nil then + TlkJSONbase(fList.Items[idx]).Free; + fList.Items[idx] := AValue; + end; +end; + +procedure TlkJSONcustomlist.ForEach(fnCallBack: TlkJSONFuncEnum; + pUserData: + pointer); +var + iCount: Integer; + IsContinue: Boolean; + anJSON: TlkJSONbase; + wsObject: string; +begin + if not assigned(fnCallBack) then exit; + IsContinue := true; + for iCount := 0 to GetCount - 1 do + begin + anJSON := ForEachElement(iCount, wsObject); + if assigned(anJSON) then + fnCallBack(wsObject, anJSON, pUserData, IsContinue); + if not IsContinue then break; + end; +end; + +///---- renamed to here + +function TlkJSONcustomlist.GetField(AName: Variant):TlkJSONbase; +var + index: Integer; +begin + if VarIsNumeric(AName) then + begin + index := integer(AName); + result := GetChild(index); + end + else + begin + result := inherited GetField(AName); + end; +end; + +function TlkJSONcustomlist.ForEachElement(idx: Integer; var nm: + string): TlkJSONbase; +begin + nm := inttostr(idx); + result := GetChild(idx); +end; + +function TlkJSONcustomlist.getDouble(idx: Integer): Double; +var + jn: TlkJSONnumber; +begin + jn := Child[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := jn.Value; +end; + +function TlkJSONcustomlist.getInt(idx: Integer): Integer; +var + jn: TlkJSONnumber; +begin + jn := Child[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := round(int(jn.Value)); +end; + +function TlkJSONcustomlist.getString(idx: Integer): string; +var + js: TlkJSONstring; +begin + js := Child[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := VarToStr(js.Value); +end; + +function TlkJSONcustomlist.getWideString(idx: Integer): WideString; +var + js: TlkJSONstring; +begin + js := Child[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := VarToWideStr(js.Value); +end; + +function TlkJSONcustomlist.getBoolean(idx: Integer): Boolean; +var + jb: TlkJSONboolean; +begin + jb := Child[idx] as TlkJSONboolean; + if not assigned(jb) then result := false + else result := jb.Value; +end; + +{ TlkJSONobjectmethod } + +procedure TlkJSONobjectmethod.AfterConstruction; +begin + inherited; + FValue := nil; + FName := ''; +end; + +procedure TlkJSONobjectmethod.BeforeDestruction; +begin + FName := ''; + if FValue <> nil then + begin + FValue.Free; + FValue := nil; + end; + inherited; +end; + +class function TlkJSONobjectmethod.Generate(const aname: WideString; + aobj: TlkJSONbase): TlkJSONobjectmethod; +begin + result := TlkJSONobjectmethod.Create; + result.FName := aname; + result.FValue := aobj; +end; + +procedure TlkJSONobjectmethod.SetName(const AValue: WideString); +begin + FName := AValue; +end; + +{ TlkJSONlist } + +function TlkJSONlist.Add(obj: TlkJSONbase): Integer; +begin + result := _Add(obj); +end; + +function TlkJSONlist.Add(nmb: double): Integer; +begin + Result := self.Add(TlkJSONnumber.Generate(nmb)); +end; + +function TlkJSONlist.Add(aboolean: Boolean): Integer; +begin + Result := self.Add(TlkJSONboolean.Generate(aboolean)); +end; + +function TlkJSONlist.Add(inmb: Integer): Integer; +begin + Result := self.Add(TlkJSONnumber.Generate(inmb)); +end; + +function TlkJSONlist.Add(const ws: WideString): Integer; +begin + Result := self.Add(TlkJSONstring.Generate(ws)); +end; + +function TlkJSONlist.Add(s: string): Integer; +begin + Result := self.Add(TlkJSONstring.Generate(s)); +end; + +procedure TlkJSONlist.Delete(idx: Integer); +begin + _Delete(idx); +end; + +class function TlkJSONlist.Generate: TlkJSONlist; +begin + result := TlkJSONlist.Create; +end; + +function TlkJSONlist.IndexOf(obj: TlkJSONbase): Integer; +begin + result := _IndexOf(obj); +end; + +class function TlkJSONlist.SelfType: TlkJSONtypes; +begin + result := jsList; +end; + +class function TlkJSONlist.SelfTypeName: string; +begin + result := 'jsList'; +end; + +{ TlkJSONobject } + +function TlkJSONobject.Add(const aname: WideString; aobj: + TlkJSONbase): + Integer; +var + mth: TlkJSONobjectmethod; +begin + if not assigned(aobj) then + begin + result := -1; + exit; + end; + mth := TlkJSONobjectmethod.Create; + mth.FName := aname; + mth.FValue := aobj; + result := self._Add(mth); + if FUseHash then +{$IFDEF USE_HASH} + ht.AddPair(aname, result); +{$ELSE} + ht.Insert(aname, result); +{$ENDIF USE_HASH} +end; + +procedure TlkJSONobject.Delete(idx: Integer); +var + i,j,k:cardinal; + mth: TlkJSONobjectmethod; +begin + if (idx >= 0) and (idx < Count) then + begin +// mth := FValue[idx] as TlkJSONobjectmethod; + mth := TlkJSONobjectmethod(fList.Items[idx]); + if FUseHash then + begin + ht.Delete(mth.FName); + end; + end; + _Delete(idx); +{$ifdef USE_HASH} + if (idx -1 then + begin +// mth := TlkJSONobjectmethod(FValue[i]); + mth := TlkJSONobjectmethod(fList.Items[i]); + mth.FValue := AValue; + end; +end; + +function TlkJSONobject.Add(const aname: WideString; nmb: double): + Integer; +begin + Result := self.Add(aname, TlkJSONnumber.Generate(nmb)); +end; + +function TlkJSONobject.Add(const aname: WideString; aboolean: Boolean): + Integer; +begin + Result := self.Add(aname, TlkJSONboolean.Generate(aboolean)); +end; + +function TlkJSONobject.Add(const aname: WideString; s: string): + Integer; +begin + Result := self.Add(aname, TlkJSONstring.Generate(s)); +end; + +function TlkJSONobject.Add(const aname: WideString; inmb: Integer): + Integer; +begin + Result := self.Add(aname, TlkJSONnumber.Generate(inmb)); +end; + +function TlkJSONobject.Add(const aname, ws: WideString): Integer; +begin + Result := self.Add(aname, TlkJSONstring.Generate(ws)); +end; + +class function TlkJSONobject.SelfType: TlkJSONtypes; +begin + Result := jsObject; +end; + +class function TlkJSONobject.SelfTypeName: string; +begin + Result := 'jsObject'; +end; + +function TlkJSONobject.GetFieldByIndex(idx: Integer): TlkJSONbase; +var + nm: WideString; +begin + nm := GetNameOf(idx); + if nm <> '' then + begin + result := Field[nm]; + end + else + begin + result := nil; + end; +end; + +function TlkJSONobject.GetNameOf(idx: Integer): WideString; +var + mth: TlkJSONobjectmethod; +begin + if (idx < 0) or (idx >= Count) then + begin + result := ''; + end + else + begin + mth := Child[idx] as TlkJSONobjectmethod; + result := mth.Name; + end; +end; + +procedure TlkJSONobject.SetFieldByIndex(idx: Integer; + const AValue: TlkJSONbase); +var + nm: WideString; +begin + nm := GetNameOf(idx); + if nm <> '' then + begin + Field[nm] := AValue; + end; +end; + +function TlkJSONobject.ForEachElement(idx: Integer; + var nm: string): TlkJSONbase; +begin + nm := GetNameOf(idx); + result := GetFieldByIndex(idx); +end; + +function TlkJSONobject.GetField(AName: Variant):TlkJSONbase; +begin + if VarIsStr(AName) then + result := OldGetField(VarToWideStr(AName)) + else + result := inherited GetField(AName); +end; + +{$IFDEF USE_HASH} +function TlkJSONobject.GetHashTable: TlkHashTable; +{$ELSE} +function TlkJSONobject.GetHashTable: TlkBalTree; +{$ENDIF USE_HASH} +begin + result := ht; +end; + +constructor TlkJSONobject.Create(bUseHash: Boolean); +begin + inherited Create; + FUseHash := bUseHash; +{$IFDEF USE_HASH} + ht := TlkHashTable.Create; + ht.FParent := self; +{$ELSE} + ht := TlkBalTree.Create; +{$ENDIF} +end; + +destructor TlkJSONobject.Destroy; +begin + if assigned(ht) then FreeAndNil(ht); + inherited; +end; + +function TlkJSONobject.getDouble(idx: Integer): Double; +var + jn: TlkJSONnumber; +begin + jn := FieldByIndex[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := jn.Value; +end; + +function TlkJSONobject.getInt(idx: Integer): Integer; +var + jn: TlkJSONnumber; +begin + jn := FieldByIndex[idx] as TlkJSONnumber; + if not assigned(jn) then result := 0 + else result := round(int(jn.Value)); +end; + +function TlkJSONobject.getString(idx: Integer): string; +var + js: TlkJSONstring; +begin + js := FieldByIndex[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := vartostr(js.Value); +end; + +function TlkJSONobject.getWideString(idx: Integer): WideString; +var + js: TlkJSONstring; +begin + js := FieldByIndex[idx] as TlkJSONstring; + if not assigned(js) then result := '' + else result := VarToWideStr(js.Value); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getDoubleFromName(nm: string): Double; +{$else} +function TlkJSONobject.getDouble(nm: string): Double; +{$endif} +begin + result := getDouble(IndexOfName(nm)); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getIntFromName(nm: string): Integer; +{$else} +function TlkJSONobject.getInt(nm: string): Integer; +{$endif} +begin + result := getInt(IndexOfName(nm)); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getStringFromName(nm: string): string; +{$else} +function TlkJSONobject.getString(nm: string): string; +{$endif} +begin + result := getString(IndexOfName(nm)); +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getWideStringFromName(nm: string): WideString; +{$else} +function TlkJSONobject.getWideString(nm: string): WideString; +{$endif} +begin + result := getWideString(IndexOfName(nm)); +end; + +function TlkJSONobject.getBoolean(idx: Integer): Boolean; +var + jb: TlkJSONboolean; +begin + jb := FieldByIndex[idx] as TlkJSONboolean; + if not assigned(jb) then result := false + else result := jb.Value; +end; + +{$ifdef TCB_EXT} +function TlkJSONobject.getBooleanFromName(nm: string): Boolean; +{$else} +function TlkJSONobject.getBoolean(nm: string): Boolean; +{$endif} +begin + result := getBoolean(IndexOfName(nm)); +end; + +{ TlkJSON } + +class function TlkJSON.GenerateText(obj: TlkJSONbase): string; +var +{$IFDEF HAVE_FORMATSETTING} + fs: TFormatSettings; +{$ENDIF} + pt1, pt0, pt2: PChar; + ptsz: cardinal; + +{$IFNDEF NEW_STYLE_GENERATE} + + function gn_base(obj: TlkJSONbase): string; + var + ws: string; + i, j: Integer; + xs: TlkJSONstring; + begin + result := ''; + if not assigned(obj) then exit; + if obj is TlkJSONnumber then + begin +{$IFDEF HAVE_FORMATSETTING} + result := FloatToStr(TlkJSONnumber(obj).FValue, fs); +{$ELSE} + result := FloatToStr(TlkJSONnumber(obj).FValue); + i := pos(DecimalSeparator, result); + if (DecimalSeparator <> '.') and (i > 0) then + result[i] := '.'; +{$ENDIF} + end + else if obj is TlkJSONstring then + begin + ws := UTF8Encode(TlkJSONstring(obj).FValue); + i := 1; + result := '"'; + while i <= length(ws) do + begin + case ws[i] of + '/', '\', '"': result := result + '\' + ws[i]; + #8: result := result + '\b'; + #9: result := result + '\t'; + #10: result := result + '\n'; + #13: result := result + '\r'; + #12: result := result + '\f'; + else + if ord(ws[i]) < 32 then + result := result + '\u' + inttohex(ord(ws[i]), 4) + else + result := result + ws[i]; + end; + inc(i); + end; + result := result + '"'; + end + else if obj is TlkJSONboolean then + begin + if TlkJSONboolean(obj).FValue then + result := 'true' + else + result := 'false'; + end + else if obj is TlkJSONnull then + begin + result := 'null'; + end + else if obj is TlkJSONlist then + begin + result := '['; + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then result := result + ','; + result := result + gn_base(TlkJSONlist(obj).Child[i]); + end; + result := result + ']'; + end + else if obj is TlkJSONobjectmethod then + begin + try + xs := TlkJSONstring.Create; + xs.FValue := TlkJSONobjectmethod(obj).FName; + result := gn_base(TlkJSONbase(xs)) + ':'; + result := result + + gn_base(TlkJSONbase(TlkJSONobjectmethod(obj).FValue)); + finally + if assigned(xs) then FreeAndNil(xs); + end; + end + else if obj is TlkJSONobject then + begin + result := '{'; + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then result := result + ','; + result := result + gn_base(TlkJSONobject(obj).Child[i]); + end; + result := result + '}'; + end; + end; +{$ELSE} + + procedure get_more_memory; + var + delta: cardinal; + begin + delta := 50000; + if pt0 = nil then + begin + pt0 := AllocMem(delta); + ptsz := 0; + pt1 := pt0; + end + else + begin + ReallocMem(pt0, ptsz + delta); + pt1 := pointer(cardinal(pt0) + ptsz); + end; + ptsz := ptsz + delta; + pt2 := pointer(cardinal(pt1) + delta); + end; + + procedure mem_ch(ch: char); + begin + if pt1 >= pt2 then get_more_memory; + pt1^ := ch; + inc(pt1); + end; + + procedure mem_write(rs: string); + var + i: Integer; + begin + for i := 1 to length(rs) do + begin + if pt1 >= pt2 then get_more_memory; + pt1^ := rs[i]; + inc(pt1); + end; + end; + + procedure gn_base(obj: TlkJSONbase); + var + ws: string; + i, j: Integer; + xs: TlkJSONstring; + begin + if not assigned(obj) then exit; + if obj is TlkJSONnumber then + begin +{$IFDEF HAVE_FORMATSETTING} + mem_write(FloatToStr(TlkJSONnumber(obj).FValue, fs)); +{$ELSE} + ws := FloatToStr(TlkJSONnumber(obj).FValue); + i := pos(DecimalSeparator, ws); + if (DecimalSeparator <> '.') and (i > 0) then ws[i] := '.'; + mem_write(ws); +{$ENDIF} + end + else if obj is TlkJSONstring then + begin + ws := UTF8Encode(TlkJSONstring(obj).FValue); + i := 1; + mem_ch('"'); + while i <= length(ws) do + begin + case ws[i] of + '/', '\', '"': + begin + mem_ch('\'); + mem_ch(ws[i]); + end; + #8: mem_write('\b'); + #9: mem_write('\t'); + #10: mem_write('\n'); + #13: mem_write('\r'); + #12: mem_write('\f'); + else + if ord(ws[i]) < 32 then + mem_write('\u' + inttohex(ord(ws[i]), 4)) + else + mem_ch(ws[i]); + end; + inc(i); + end; + mem_ch('"'); + end + else if obj is TlkJSONboolean then + begin + if TlkJSONboolean(obj).FValue then + mem_write('true') + else + mem_write('false'); + end + else if obj is TlkJSONnull then + begin + mem_write('null'); + end + else if obj is TlkJSONlist then + begin + mem_ch('['); + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then mem_ch(','); + gn_base(TlkJSONlist(obj).Child[i]); + end; + mem_ch(']'); + end + else if obj is TlkJSONobjectmethod then + begin + try + xs := TlkJSONstring.Create; + xs.FValue := TlkJSONobjectmethod(obj).FName; + gn_base(TlkJSONbase(xs)); + mem_ch(':'); + gn_base(TlkJSONbase(TlkJSONobjectmethod(obj).FValue)); + finally + if assigned(xs) then FreeAndNil(xs); + end; + end + else if obj is TlkJSONobject then + begin + mem_ch('{'); + j := TlkJSONobject(obj).Count - 1; + for i := 0 to j do + begin + if i > 0 then mem_ch(','); + gn_base(TlkJSONobject(obj).Child[i]); + end; + mem_ch('}'); + end; + end; +{$ENDIF NEW_STYLE_GENERATE} + +begin +{$IFDEF HAVE_FORMATSETTING} + GetLocaleFormatSettings(GetThreadLocale, fs); + fs.DecimalSeparator := '.'; +{$ENDIF} +{$IFDEF NEW_STYLE_GENERATE} + pt0 := nil; + get_more_memory; + gn_base(obj); + mem_ch(#0); + result := string(pt0); + freemem(pt0); +{$ELSE} + result := gn_base(obj); +{$ENDIF} +end; + +class function TlkJSON.ParseText(const txt: string): TlkJSONbase; +{$IFDEF HAVE_FORMATSETTING} +var + fs: TFormatSettings; +{$ENDIF} + + function js_base(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; forward; + + function xe(idx: Integer): Boolean; + {$IFDEF FPC}inline; + {$ENDIF} + begin + result := idx <= length(txt); + end; + + procedure skip_spc(var idx: Integer); + {$IFDEF FPC}inline; + {$ENDIF} + begin + while (xe(idx)) and (ord(txt[idx]) < 33) do + inc(idx); + end; + + procedure add_child(var o, c: TlkJSONbase); + var + i: Integer; + begin + if o = nil then + begin + o := c; + end + else + begin + if o is TlkJSONobjectmethod then + begin + TlkJSONobjectmethod(o).FValue := c; + end + else if o is TlkJSONlist then + begin + TlkJSONlist(o)._Add(c); + end + else if o is TlkJSONobject then + begin + i := TlkJSONobject(o)._Add(c); + if TlkJSONobject(o).UseHash then +{$IFDEF USE_HASH} + TlkJSONobject(o).ht.AddPair(TlkJSONobjectmethod(c).Name, i); +{$ELSE} + TlkJSONobject(o).ht.Insert(TlkJSONobjectmethod(c).Name, i); +{$ENDIF USE_HASH} + end; + end; + end; + + function js_boolean(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONboolean; + begin + skip_spc(idx); + if copy(txt, idx, 4) = 'true' then + begin + result := true; + ridx := idx + 4; + js := TlkJSONboolean.Create; + js.FValue := true; + add_child(o, TlkJSONbase(js)); + end + else if copy(txt, idx, 5) = 'false' then + begin + result := true; + ridx := idx + 5; + js := TlkJSONboolean.Create; + js.FValue := false; + add_child(o, TlkJSONbase(js)); + end + else + begin + result := false; + end; + end; + + function js_null(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONnull; + begin + skip_spc(idx); + if copy(txt, idx, 4) = 'null' then + begin + result := true; + ridx := idx + 4; + js := TlkJSONnull.Create; + add_child(o, TlkJSONbase(js)); + end + else + begin + result := false; + end; + end; + + function js_integer(idx: Integer; var ridx: Integer): Boolean; + begin + result := false; + while (xe(idx)) and (txt[idx] in ['0'..'9']) do + begin + result := true; + inc(idx); + end; + if result then ridx := idx; + end; + + function js_number(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONnumber; + ws: string; + {$IFNDEF HAVE_FORMATSETTING} + i: Integer; + {$ENDIF} + begin + skip_spc(idx); + result := xe(idx); + if not result then exit; + if txt[idx] in ['+', '-'] then + begin + inc(idx); + result := xe(idx); + end; + if not result then exit; + result := js_integer(idx, idx); + if not result then exit; + if (xe(idx)) and (txt[idx] = '.') then + begin + inc(idx); + result := js_integer(idx, idx); + if not result then exit; + end; + if (xe(idx)) and (txt[idx] in ['e', 'E']) then + begin + inc(idx); + if (xe(idx)) and (txt[idx] in ['+', '-']) then inc(idx); + result := js_integer(idx, idx); + if not result then exit; + end; + if not result then exit; + js := TlkJSONnumber.Create; + ws := copy(txt, ridx, idx - ridx); +{$IFDEF HAVE_FORMATSETTING} + js.FValue := StrToFloat(ws, fs); +{$ELSE} + i := pos('.', ws); + if (DecimalSeparator <> '.') and (i > 0) then + ws[pos('.', ws)] := DecimalSeparator; + js.FValue := StrToFloat(ws); +{$ENDIF} + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + +{ + +} + function js_string(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + + function strSpecialChars(const s: string): string; + var + i, j : integer; + begin + i := Pos('\', s); + if (i = 0) then + Result := s + else + begin + Result := Copy(s, 1, i-1); + j := i; + repeat + if (s[j] = '\') then + begin + inc(j); + case s[j] of + '\': Result := Result + '\'; + '"': Result := Result + '"'; + '''': Result := Result + ''''; + '/': Result := Result + '/'; + 'b': Result := Result + #8; + 'f': Result := Result + #12; + 'n': Result := Result + #10; + 'r': Result := Result + #13; + 't': Result := Result + #9; + 'u': + begin + Result := Result + code2utf(strtoint('$' + copy(s, j + 1, 4))); + inc(j, 4); + end; + end; + end + else + Result := Result + s[j]; + inc(j); + until j > length(s); + end; + end; + + var + js: TlkJSONstring; + fin: Boolean; + ws: String; + i,j,widx: Integer; + begin + skip_spc(idx); + + result := xe(idx) and (txt[idx] = '"'); + if not result then exit; + + inc(idx); + widx := idx; + + fin:=false; + REPEAT + i := 0; + j := 0; + while (widx<=length(txt)) and (j=0) do + begin + if (i=0) and (txt[widx]='\') then i:=widx; + if (j=0) and (txt[widx]='"') then j:=widx; + inc(widx); + end; +// incorrect string!!! + if j=0 then + begin + result := false; + exit; + end; +// if we have no slashed chars in string + if (i=0) or (j0 and j>=i - skip slashed char + else + begin + widx:=i+2; + end; + UNTIL fin; + + ws := strSpecialChars(ws); + inc(idx); + + js := TlkJSONstring.Create; +{$ifdef USE_D2009} + js.FValue := UTF8ToString(ws); +{$else} + js.FValue := UTF8Decode(ws); +{$endif} + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + + function js_list(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONlist; + begin + result := false; + try + js := TlkJSONlist.Create; + skip_spc(idx); + result := xe(idx); + if not result then exit; + result := txt[idx] = '['; + if not result then exit; + inc(idx); + while js_base(idx, idx, TlkJSONbase(js)) do + begin + skip_spc(idx); + if (xe(idx)) and (txt[idx] = ',') then inc(idx); + end; + skip_spc(idx); + result := (xe(idx)) and (txt[idx] = ']'); + if not result then exit; + inc(idx); + finally + if not result then + begin + js.Free; + end + else + begin + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + end; + end; + + function js_method(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + mth: TlkJSONobjectmethod; + ws: TlkJSONstring; + begin + result := false; + try + ws := nil; + mth := TlkJSONobjectmethod.Create; + skip_spc(idx); + result := xe(idx); + if not result then exit; + result := js_string(idx, idx, TlkJSONbase(ws)); + if not result then exit; + skip_spc(idx); + result := xe(idx) and (txt[idx] = ':'); + if not result then exit; + inc(idx); + mth.FName := ws.FValue; + result := js_base(idx, idx, TlkJSONbase(mth)); + finally + if ws <> nil then ws.Free; + if result then + begin + add_child(o, TlkJSONbase(mth)); + ridx := idx; + end + else + begin + mth.Free; + end; + end; + end; + + function js_object(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + var + js: TlkJSONobject; + begin + result := false; + try + js := TlkJSONobject.Create; + skip_spc(idx); + result := xe(idx); + if not result then exit; + result := txt[idx] = '{'; + if not result then exit; + inc(idx); + while js_method(idx, idx, TlkJSONbase(js)) do + begin + skip_spc(idx); + if (xe(idx)) and (txt[idx] = ',') then inc(idx); + end; + skip_spc(idx); + result := (xe(idx)) and (txt[idx] = '}'); + if not result then exit; + inc(idx); + finally + if not result then + begin + js.Free; + end + else + begin + add_child(o, TlkJSONbase(js)); + ridx := idx; + end; + end; + end; + + function js_base(idx: Integer; var ridx: Integer; var o: + TlkJSONbase): Boolean; + begin + skip_spc(idx); + result := js_boolean(idx, idx, o); + if not result then result := js_null(idx, idx, o); + if not result then result := js_number(idx, idx, o); + if not result then result := js_string(idx, idx, o); + if not result then result := js_list(idx, idx, o); + if not result then result := js_object(idx, idx, o); + if result then ridx := idx; + end; + +var + idx: Integer; +begin +{$IFDEF HAVE_FORMATSETTING} + GetLocaleFormatSettings(GetThreadLocale, fs); + fs.DecimalSeparator := '.'; +{$ENDIF} + + result := nil; + if txt = '' then exit; + try + idx := 1; + // skip a BOM utf8 marker + if copy(txt,idx,3)=#239#187#191 then + begin + inc(idx,3); + // if there are only a BOM - exit; + if idx>length(txt) then exit; + end; + if not js_base(idx, idx, result) then FreeAndNil(result); + except + if assigned(result) then FreeAndNil(result); + end; +end; + +{ ElkIntException } + +constructor ElkIntException.Create(idx: Integer; msg: string); +begin + self.idx := idx; + inherited Create(msg); +end; + +{ TlkHashTable } + +{$IFDEF USE_HASH} +procedure TlkHashTable.AddPair(const ws: WideString; idx: Integer); +var + i, j, k: cardinal; + p: PlkHashItem; + find: boolean; +begin + find := false; + if InTable(ws, i, j, k) then + begin +// if string is already in table, changing index + if TlkJSONobject(FParent).GetNameOf(PlkHashItem(a_x[j].Items[k])^.index) = ws then + begin + PlkHashItem(a_x[j].Items[k])^.index := idx; + find := true; + end; + end; + if find = false then + begin + GetMem(p,sizeof(TlkHashItem)); + k := a_x[j].Add(p); + p^.hash := i; + p^.index := idx; + while (k>0) and (PlkHashItem(a_x[j].Items[k])^.hash < PlkHashItem(a_x[j].Items[k-1])^.hash) do + begin + a_x[j].Exchange(k,k-1); + dec(k); + end; + end; +end; + +function TlkHashTable.counters: string; +var + i, j: Integer; + ws: string; +begin + ws := ''; + for i := 0 to 15 do + begin + for j := 0 to 15 do +// ws := ws + format('%.3d ', [length(a_h[i * 16 + j])]); + ws := ws + format('%.3d ', [a_x[i * 16 + j].Count]); + ws := ws + #13#10; + end; + result := ws; +end; + +procedure TlkHashTable.Delete(const ws: WideString); +var + i, j, k: cardinal; +begin + if InTable(ws, i, j, k) then + begin +// while k < high(a_h[j]) do +// begin +// hswap(j, k, k + 1); +// inc(k); +// end; +// SetLength(a_h[j], k); + FreeMem(a_x[j].Items[k]); + a_x[j].Delete(k); + end; +end; + +{$IFDEF THREADSAFE} +const + rnd_table: array[0..255] of byte = + (216, 191, 234, 201, 12, 163, 190, 205, 128, 199, 210, 17, 52, 43, + 38, 149, 40, 207, 186, 89, 92, 179, 142, 93, 208, 215, 162, + 161, 132, 59, 246, 37, 120, 223, 138, 233, 172, 195, 94, 237, 32, + 231, 114, 49, 212, 75, 198, 181, 200, 239, 90, 121, 252, 211, + 46, 125, 112, 247, 66, 193, 36, 91, 150, 69, 24, 255, 42, 9, 76, + 227, 254, 13, 192, 7, 18, 81, 116, 107, 102, 213, 104, 15, 250, + 153, 156, 243, 206, 157, 16, 23, 226, 225, 196, 123, 54, 101, + 184, 31, 202, 41, 236, 3, 158, 45, 96, 39, 178, 113, 20, 139, 6, + 245, 8, 47, 154, 185, 60, 19, 110, 189, 176, 55, 130, 1, 100, + 155, 214, 133, 88, 63, 106, 73, 140, 35, 62, 77, 0, 71, 82, 145, + 180, + 171, 166, 21, 168, 79, 58, 217, 220, 51, 14, 221, 80, 87, 34, 33, + 4, 187, 118, 165, 248, 95, 10, 105, 44, 67, 222, 109, 160, 103, + 242, 177, 84, 203, 70, 53, 72, 111, 218, 249, 124, 83, 174, 253, + 240, 119, 194, 65, 164, 219, 22, 197, 152, 127, 170, 137, 204, + 99, 126, 141, 64, 135, 146, 209, 244, 235, 230, 85, 232, 143, + 122, 25, 28, 115, 78, 29, 144, 151, 98, 97, 68, 251, 182, 229, + 56, + 159, 74, 169, 108, 131, 30, 173, 224, 167, 50, 241, 148, 11, 134, + 117, 136, 175, 26, 57, 188, 147, 238, 61, 48, 183, 2, 129, + 228, 27, 86, 5); +{$ELSE} +var + rnd_table: array[0..255] of byte; +{$ENDIF} + +function TlkHashTable.DefaultHashOf(const ws: WideString): cardinal; +{$IFDEF DOTNET} +var + i, j: Integer; + x1, x2, x3, x4: byte; +begin + result := 0; +// result := 0; + x1 := 0; + x2 := 1; + for i := 1 to length(ws) do + begin + j := ord(ws[i]); +// first version of hashing + x1 := (x1 + j) {and $FF}; + x2 := (x2 + 1 + (j shr 8)) {and $FF}; + x3 := rnd_table[x1]; + x4 := rnd_table[x3]; + result := ((x1 * x4) + (x2 * x3)) xor result; + end; +end; +{$ELSE} +var + x1, x2, x3, x4: byte; + p: PWideChar; +begin + result := 0; + x1 := 0; + x2 := 1; + p := PWideChar(ws); + while p^ <> #0 do + begin + inc(x1, ord(p^)) {and $FF}; + inc(x2, 1 + (ord(p^) shr 8)) {and $FF}; + x3 := rnd_table[x1]; + x4 := rnd_table[x3]; + result := ((x1 * x4) + (x2 * x3)) xor result; + inc(p); + end; +end; +{$ENDIF} + +procedure TlkHashTable.hswap(j, k, l: Integer); +//var +// h: TlkHashItem; +begin +// h := a_h[j, k]; +// a_h[j, k] := a_h[j, l]; +// a_h[j, l] := h; + a_x[j].Exchange(k, l); +end; + +function TlkHashTable.IndexOf(const ws: WideString): Integer; +var + i, j, k: Cardinal; +begin + if not InTable(ws, i, j, k) then + begin + result := -1; + end + else + begin +// result := a_h[j, k].index; + result := PlkHashItem(a_x[j].Items[k])^.index; + end; +end; + +function TlkHashTable.InTable(const ws: WideString; var i, j, k: + cardinal): + Boolean; +var + l, wu, wl: Integer; + x: Cardinal; + fin: Boolean; +begin + i := HashOf(ws); + j := i and $FF; + result := false; +{using "binary" search always, because array is sorted} + if a_x[j].Count-1 >= 0 then + begin + wl := 0; + wu := a_x[j].Count-1; + repeat + fin := true; + if PlkHashItem(a_x[j].Items[wl])^.hash = i then + begin + k := wl; + result := true; + end + else if PlkHashItem(a_x[j].Items[wu])^.hash = i then + begin + k := wu; + result := true; + end + else if (wu - wl) > 1 then + begin + fin := false; + x := (wl + wu) shr 1; + if PlkHashItem(a_x[j].Items[x])^.hash > i then + begin + wu := x; + end + else + begin + wl := x; + end; + end; + until fin; + end; + +// verify k index in chain + if result = true then + begin + while (k > 0) and (PlkHashItem(a_x[j].Items[k])^.hash = PlkHashItem(a_x[j].Items[k-1])^.hash) do dec(k); + repeat + fin := true; + if TlkJSONobject(FParent).GetNameOf(PlkHashItem(a_x[j].Items[k])^.index) <> ws then + begin + if k < a_x[j].Count-1 then + begin + inc(k); + fin := false; + end + else + begin + result := false; + end; + end + else + begin + result := true; + end; + until fin; + end; +end; + +{$IFNDEF THREADSAFE} + +procedure init_rnd; +var + x0: Integer; + i: Integer; +begin + x0 := 5; + for i := 0 to 255 do + begin + x0 := (x0 * 29 + 71) and $FF; + rnd_table[i] := x0; + end; +end; +{$ENDIF} + +procedure TlkHashTable.SetHashFunction(const AValue: + TlkHashFunction); +begin + FHashFunction := AValue; +end; + +constructor TlkHashTable.Create; +var + i: Integer; +begin + inherited; +// for i := 0 to 255 do SetLength(a_h[i], 0); + for i := 0 to 255 do a_x[i] := TList.Create; + HashOf := {$IFDEF FPC}@{$ENDIF}DefaultHashOf; +end; + +destructor TlkHashTable.Destroy; +var + i, j: Integer; +begin +// for i := 0 to 255 do SetLength(a_h[i], 0); + for i := 0 to 255 do + begin + for j := 0 to a_x[i].Count - 1 do Freemem(a_x[i].Items[j]); + a_x[i].Free; + end; + inherited; +end; + +function TlkHashTable.SimpleHashOf(const ws: WideString): cardinal; +var + i: Integer; +begin + result := length(ws); + for i := 1 to length(ws) do result := result + ord(ws[i]); +end; +{$ENDIF USE_HASH} + +{ TlkJSONstreamed } +{$IFNDEF KOL} + +class function TlkJSONstreamed.LoadFromFile(srcname: string): + TlkJSONbase; +var + fs: TFileStream; +begin + result := nil; + if not FileExists(srcname) then exit; + try + fs := TFileStream.Create(srcname, fmOpenRead); + result := LoadFromStream(fs); + finally + if Assigned(fs) then FreeAndNil(fs); + end; +end; + +class function TlkJSONstreamed.LoadFromStream(src: TStream): + TlkJSONbase; +var + ws: string; + len: int64; +begin + result := nil; + if not assigned(src) then exit; + len := src.Size - src.Position; + SetLength(ws, len); + src.Read(pchar(ws)^, len); + result := ParseText(ws); +end; + +class procedure TlkJSONstreamed.SaveToFile(obj: TlkJSONbase; + dstname: string); +var + fs: TFileStream; +begin + if not assigned(obj) then exit; + try + fs := TFileStream.Create(dstname, fmCreate); + SaveToStream(obj, fs); + finally + if Assigned(fs) then FreeAndNil(fs); + end; +end; + +class procedure TlkJSONstreamed.SaveToStream(obj: TlkJSONbase; + dst: TStream); +var + ws: string; +begin + if not assigned(obj) then exit; + if not assigned(dst) then exit; + ws := GenerateText(obj); + dst.Write(pchar(ws)^, length(ws)); +end; + +{$ENDIF} + +{ TlkJSONdotnetclass } + +{$IFDEF DOTNET} + +procedure TlkJSONdotnetclass.AfterConstruction; +begin + +end; + +procedure TlkJSONdotnetclass.BeforeDestruction; +begin + +end; + +constructor TlkJSONdotnetclass.Create; +begin + inherited; + AfterConstruction; +end; + +destructor TlkJSONdotnetclass.Destroy; +begin + BeforeDestruction; + inherited; +end; +{$ENDIF DOTNET} + +{ TlkBalTree } + +{$IFNDEF USE_HASH} +procedure TlkBalTree.Clear; + + procedure rec(t: PlkBalNode); + begin + if t.left<>fbottom then rec(t.left); + if t.right<>fbottom then rec(t.right); + t.nm := ''; + dispose(t); + end; + +begin + if froot<>fbottom then rec(froot); + froot := fbottom; + fdeleted := fbottom; +end; + +function TlkBalTree.counters: string; +begin + result := format('Balanced tree root node level is %d',[froot.level]); +end; + +constructor TlkBalTree.Create; +begin + inherited Create; + new(fbottom); + fbottom.left := fbottom; + fbottom.right := fbottom; + fbottom.level := 0; + fdeleted := fbottom; + froot := fbottom; +end; + +function TlkBalTree.Delete(const ws: WideString): Boolean; + + procedure UpdateKeys(t: PlkBalNode; idx: integer); + begin + if t <> fbottom then begin + if t.key > idx then + t.key := t.key - 1; + UpdateKeys(t.left, idx); + UpdateKeys(t.right, idx); + end; + end; + + function del(var t: PlkBalNode): Boolean; + begin + result := false; + if t<>fbottom then begin + flast := t; + if ws fbottom) and (ws = fdeleted.nm) then begin + UpdateKeys(froot, fdeleted.key); + fdeleted.key := t.key; + fdeleted.nm := t.nm; + t := t.right; + flast.nm := ''; + dispose(flast); + result := true; + end + else if (t.left.level < (t.level - 1)) or (t.right.level < (t.level - 1)) then begin + t.level := t.level - 1; + if t.right.level > t.level then + t.right.level := t.level; + skew(t); + skew(t.right); + skew(t.right.right); + split(t); + split(t.right); + end; + end; + end; + +{ +// mine version, buggy, see tracker message +// [ 2229135 ] Value deletion is broken by "Nobody/Anonymous - nobody" + + function del(var t: PlkBalNode): Boolean; + begin + result := false; + if t<>fbottom then + begin + flast := t; + if wsfbottom) and (ws = t.nm) then + begin + fdeleted.key := t.key; + fdeleted.nm := t.nm; + t := t.right; + flast.nm := ''; + dispose(flast); + result := true; + end + else if (t.left.level<(t.level-1)) or (t.right.level<(t.level-1)) then + begin + t.level := t.level-1; + if t.right.level>t.level then t.right.level := t.level; + skew(t); + skew(t.right); + skew(t.right.right); + split(t); + split(t.right); + end; + end; + end; +} + +begin + result := del(froot); +end; + +destructor TlkBalTree.Destroy; +begin + Clear; + dispose(fbottom); + inherited; +end; + +function TlkBalTree.IndexOf(const ws: WideString): Integer; +var + tk: PlkBalNode; +begin + result := -1; + tk := froot; + while (result=-1) and (tk<>fbottom) do + begin + if tk.nm = ws then result := tk.key + else if ws t.nm then + result := ins(t.right) + else result := false; + skew(t); + split(t); + end; + end; + +begin + result := ins(froot); +end; + +procedure TlkBalTree.skew(var t: PlkBalNode); +var + temp: PlkBalNode; +begin + if t.left.level = t.level then + begin + temp := t; + t := t.left; + temp.left := t.right; + t.right := temp; + end; +end; + +procedure TlkBalTree.split(var t: PlkBalNode); +var + temp: PlkBalNode; +begin + if t.right.right.level = t.level then + begin + temp := t; + t := t.right; + temp.right := t.left; + t.left := temp; + t.level := t.level+1; + end; +end; +{$ENDIF USE_HASH} + +initialization +{$IFNDEF THREADSAFE} +{$IFDEF USE_HASH} + init_rnd; +{$ENDIF USE_HASH} +{$ENDIF THREADSAFE} +end. + diff --git a/demo/YxdJson/JsonDebug/JSONDebug.dpr b/demo/YxdJson/JsonDebug/JSONDebug.dpr new file mode 100644 index 0000000..3d8fc08 --- /dev/null +++ b/demo/YxdJson/JsonDebug/JSONDebug.dpr @@ -0,0 +1,14 @@ +program JSONDebug; + +uses + Vcl.Forms, + Unit1 in 'Unit1.pas' {Form1}; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/demo/YxdJson/JsonDebug/JSONDebug.dproj b/demo/YxdJson/JsonDebug/JSONDebug.dproj new file mode 100644 index 0000000..d5d8b58 --- /dev/null +++ b/demo/YxdJson/JsonDebug/JSONDebug.dproj @@ -0,0 +1,149 @@ + + + {C099C549-0431-4243-A1D8-D61B406735D6} + 15.4 + VCL + JSONDebug.dpr + True + Debug + Win32 + 1 + Application + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + JSONDebug + $(BDS)\bin\delphi_PROJECTICON.ico + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + $(BDS)\bin\default_app.manifest + cxSchedulerTreeBrowserRS20;frxe20;dxSkinOffice2007SilverRS20;cxGridRS20;dxFireDACServerModeRS20;dxPSdxLCLnkRS20;dxPScxExtCommonRS20;cxPageControlRS20;FireDACPgDriver;fgx;DBXInterBaseDriver;DataSnapServer;DataSnapCommon;NxInspectorDsgn_dxe6;dxSkinsdxBarPainterRS20;dxSkinSharpRS20;NxInspectorRun_dxe6;pfManager;DbxCommonDriver;dxLayoutControlRS20;vclimg;dxSkinSilverRS20;dxSkinsdxNavBarPainterRS20;dbxcds;DatasnapConnectorsFreePascal;NxCommonDsgn_dxe6;dxPSCoreRS20;vcldb;EhLibADODataDrivers200;pfCore;dxSkinOffice2013WhiteRS20;dxSkinMcSkinRS20;CustomIPTransport;dsnap;IndyIPServer;dxSkinCoffeeRS20;dxSkinGlassOceansRS20;IndyCore;dxSkinOffice2010SilverRS20;dxComnRS20;CloudService;dxFlowChartRS20;FmxTeeUI;FireDACIBDriver;cxTreeListdxBarPopupMenuRS20;dxDBXServerModeRS20;dxSkinOffice2007PinkRS20;dxSkinSpringTimeRS20;dxPsPrVwAdvRS20;dxSkiniMaginaryRS20;dxSkinDevExpressDarkStyleRS20;NxDBGridRun_dxe6;cxSchedulerGridRS20;dxtrmdRS20;NxCollectionDsgn_dxe6;dsnapxml;FireDACDb2Driver;EhLib200;dxSkinMoneyTwinsRS20;dxSkinOffice2007GreenRS20;dxPScxTLLnkRS20;NxSheetRun_dxe6;cxPivotGridOLAPRS20;dxPSdxFCLnkRS20;bindcompfmx;cxPageControldxBarPopupMenuRS20;frx20;vcldbx;FireDACODBCDriver;RESTBackendComponents;dbrtl;FireDACCommon;bindcomp;inetdb;ThdTimerXE6;DBXOdbcDriver;vclFireDAC;cxSpreadSheetRS20;xmlrtl;NxGridDsgn_dxe6;ibxpress;dxPScxSSLnkRS20;dxSkinOffice2007BlackRS20;FireDACCommonDriver;bindengine;vclactnband;FMXTee;soaprtl;dxGDIPlusRS20;bindcompvcl;vclie;NxCollectionRun_dxe6;cxVerticalGridRS20;cxSchedulerRS20;dxSkinBlackRS20;TMSFMXPackPkgDEDXE6;FireDACMSSQLDriver;DBXInformixDriver;dxSkinSummer2008RS20;Intraweb;cxBarEditItemRS20;NxCommonRun_dxe6;DataSnapServerMidas;FMX.ListView.NeAppearancePackage;DBXFirebirdDriver;dsnapcon;inet;dxBarRS20;cxDataRS20;dxSkinDarkSideRS20;FireDACMySQLDriver;soapmidas;vclx;dxPScxVGridLnkRS20;dxSkinLondonLiquidSkyRS20;dxCoreRS20;DBXSybaseASADriver;RESTComponents;dxPSPrVwRibbonRS20;EhLibDBXDataDrivers200;dbexpress;dxPSLnksRS20;IndyIPClient;dxSpellCheckerRS20;dxBarExtItemsRS20;IcsCommonDXE6Run;dxdbtrRS20;FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;SampleListViewMultiDetailAppearancePackage;fmx;dxSkinVS2010RS20;dxPScxPCProdRS20;IndySystem;dxSkinXmas2008BlueRS20;TeeDB;tethering;vclib;inetdbbde;DataSnapClient;dxTabbedMDIRS20;dxmdsRS20;DataSnapProviderClient;DBXSybaseASEDriver;dxdborRS20;crcontrols200;dxPSdxDBTVLnkRS20;MetropolisUILiveTile;dxPScxSchedulerLnkRS20;dxSkinCaramelRS20;dxSkinLiquidSkyRS20;vcldsnap;IcsVclDXE6Run;dxSkinDevExpressStyleRS20;fmxFireDAC;dacvcl200;DBXDb2Driver;DBXOracleDriver;dxSkinOffice2010BlueRS20;dcldxSkinsCoreRS20;vclribbon;cxExportRS20;dxServerModeRS20;dxSkinscxSchedulerPainterRS20;FMToastPackage;EhLibBDEDataDrivers200;fmxase;vcl;dacfmx200;NxAddonsRun_dxe6;DBXMSSQLDriver;IndyIPCommon;CodeSiteExpressPkg;dxSkinBlueRS20;dxSkinsdxDLPainterRS20;DataSnapFireDAC;FireDACDBXDriver;dxBarExtDBItemsRS20;dxSkinOffice2010BlackRS20;soapserver;DPFAndroidPackagesXE6;inetdbxpress;dxADOServerModeRS20;dxSkinBlueprintRS20;dxSkinFoggyRS20;dxSkinSharpPlusRS20;frxTee20;FireDACInfxDriver;cxPivotGridRS20;EhLibIBXDataDrivers200;adortl;frxDB20;clInetSuitedXE6;dxRibbonRS20;FireDACASADriver;dxSkinHighContrastRS20;dxSkinTheAsphaltWorldRS20;dxBarDBNavRS20;dxSkinscxPCPainterRS20;rtl;DbxClientDriver;dxNavBarRS20;SampleListViewRatingsAppearancePackage;dac200;dxDockingRS20;Tee;dxSkinOffice2007BlueRS20;dxSkinsdxRibbonPainterRS20;dxSkinValentineRS20;IcsFmxDXE6Run;DataSnapNativeClient;svnui;IndyProtocols;DBXMySQLDriver;dxPScxCommonRS20;dxSkinSevenClassicRS20;dxSkinPumpkinRS20;bindcompdbx;TeeUI;TMSFMXPackPkgDXE6;unidacvcl200;unidacfmx200;FireDACADSDriver;vcltouch;dxSkinDarkRoomRS20;dxSkinStardustRS20;unidac200;cxEditorsRS20;NxDBGridDsgn_dxe6;dxorgcRS20;dxPSdxDBOCLnkRS20;VCLRESTComponents;FireDAC;VclSmp;dxSkinsCoreRS20;XThreadTimer;DataSnapConnectors;dxSkinSevenRS20;NxGridRun_dxe6;cxLibraryRS20;fmxobj;svn;dxSkinLilianRS20;dxSkinWhiteprintRS20;FireDACOracleDriver;fmxdae;dxPScxPivotGridLnkRS20;dxWizardControlRS20;bdertl;dxThemeRS20;dxPSdxOCLnkRS20;cxTreeListRS20;FireDACMSAccDriver;DataSnapIndy10ServerTransport;dxTileControlRS20;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1033 + true + + + cxSchedulerTreeBrowserRS20;dxSkinOffice2007SilverRS20;cxGridRS20;dxFireDACServerModeRS20;dxPSdxLCLnkRS20;dxPScxExtCommonRS20;cxPageControlRS20;FireDACPgDriver;DBXInterBaseDriver;DataSnapServer;DataSnapCommon;NxInspectorDsgn_dxe6;dxSkinsdxBarPainterRS20;dxSkinSharpRS20;NxInspectorRun_dxe6;DbxCommonDriver;dxLayoutControlRS20;vclimg;dxSkinSilverRS20;dxSkinsdxNavBarPainterRS20;dbxcds;DatasnapConnectorsFreePascal;NxCommonDsgn_dxe6;dxPSCoreRS20;vcldb;EhLibADODataDrivers200;dxSkinOffice2013WhiteRS20;dxSkinMcSkinRS20;CustomIPTransport;dsnap;IndyIPServer;dxSkinCoffeeRS20;dxSkinGlassOceansRS20;IndyCore;dxSkinOffice2010SilverRS20;dxComnRS20;CloudService;dxFlowChartRS20;FmxTeeUI;FireDACIBDriver;cxTreeListdxBarPopupMenuRS20;dxDBXServerModeRS20;dxSkinOffice2007PinkRS20;dxSkinSpringTimeRS20;dxPsPrVwAdvRS20;dxSkiniMaginaryRS20;dxSkinDevExpressDarkStyleRS20;NxDBGridRun_dxe6;cxSchedulerGridRS20;dxtrmdRS20;NxCollectionDsgn_dxe6;dsnapxml;FireDACDb2Driver;EhLib200;dxSkinMoneyTwinsRS20;dxSkinOffice2007GreenRS20;dxPScxTLLnkRS20;NxSheetRun_dxe6;cxPivotGridOLAPRS20;dxPSdxFCLnkRS20;bindcompfmx;cxPageControldxBarPopupMenuRS20;FireDACODBCDriver;RESTBackendComponents;dbrtl;FireDACCommon;bindcomp;inetdb;DBXOdbcDriver;vclFireDAC;cxSpreadSheetRS20;xmlrtl;NxGridDsgn_dxe6;ibxpress;dxPScxSSLnkRS20;dxSkinOffice2007BlackRS20;FireDACCommonDriver;bindengine;vclactnband;FMXTee;soaprtl;dxGDIPlusRS20;bindcompvcl;vclie;NxCollectionRun_dxe6;cxVerticalGridRS20;cxSchedulerRS20;dxSkinBlackRS20;TMSFMXPackPkgDEDXE6;FireDACMSSQLDriver;DBXInformixDriver;dxSkinSummer2008RS20;Intraweb;cxBarEditItemRS20;NxCommonRun_dxe6;DataSnapServerMidas;DBXFirebirdDriver;dsnapcon;inet;dxBarRS20;cxDataRS20;dxSkinDarkSideRS20;FireDACMySQLDriver;soapmidas;vclx;dxPScxVGridLnkRS20;dxSkinLondonLiquidSkyRS20;dxCoreRS20;DBXSybaseASADriver;RESTComponents;dxPSPrVwRibbonRS20;EhLibDBXDataDrivers200;dbexpress;dxPSLnksRS20;IndyIPClient;dxSpellCheckerRS20;dxBarExtItemsRS20;IcsCommonDXE6Run;dxdbtrRS20;FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;fmx;dxSkinVS2010RS20;dxPScxPCProdRS20;IndySystem;dxSkinXmas2008BlueRS20;TeeDB;tethering;vclib;DataSnapClient;dxTabbedMDIRS20;dxmdsRS20;DataSnapProviderClient;DBXSybaseASEDriver;dxdborRS20;crcontrols200;dxPSdxDBTVLnkRS20;MetropolisUILiveTile;dxPScxSchedulerLnkRS20;dxSkinCaramelRS20;dxSkinLiquidSkyRS20;vcldsnap;IcsVclDXE6Run;dxSkinDevExpressStyleRS20;fmxFireDAC;dacvcl200;DBXDb2Driver;DBXOracleDriver;dxSkinOffice2010BlueRS20;dcldxSkinsCoreRS20;vclribbon;cxExportRS20;dxServerModeRS20;dxSkinscxSchedulerPainterRS20;FMToastPackage;EhLibBDEDataDrivers200;fmxase;vcl;dacfmx200;NxAddonsRun_dxe6;DBXMSSQLDriver;IndyIPCommon;dxSkinBlueRS20;dxSkinsdxDLPainterRS20;DataSnapFireDAC;FireDACDBXDriver;dxBarExtDBItemsRS20;dxSkinOffice2010BlackRS20;soapserver;inetdbxpress;dxADOServerModeRS20;dxSkinBlueprintRS20;dxSkinFoggyRS20;dxSkinSharpPlusRS20;FireDACInfxDriver;cxPivotGridRS20;EhLibIBXDataDrivers200;adortl;dxRibbonRS20;FireDACASADriver;dxSkinHighContrastRS20;dxSkinTheAsphaltWorldRS20;dxBarDBNavRS20;dxSkinscxPCPainterRS20;rtl;DbxClientDriver;dxNavBarRS20;dac200;dxDockingRS20;Tee;dxSkinOffice2007BlueRS20;dxSkinsdxRibbonPainterRS20;dxSkinValentineRS20;IcsFmxDXE6Run;DataSnapNativeClient;IndyProtocols;DBXMySQLDriver;dxPScxCommonRS20;dxSkinSevenClassicRS20;dxSkinPumpkinRS20;bindcompdbx;TeeUI;TMSFMXPackPkgDXE6;unidacvcl200;unidacfmx200;FireDACADSDriver;vcltouch;dxSkinDarkRoomRS20;dxSkinStardustRS20;unidac200;cxEditorsRS20;NxDBGridDsgn_dxe6;dxorgcRS20;dxPSdxDBOCLnkRS20;VCLRESTComponents;FireDAC;VclSmp;dxSkinsCoreRS20;DataSnapConnectors;dxSkinSevenRS20;NxGridRun_dxe6;cxLibraryRS20;fmxobj;dxSkinLilianRS20;dxSkinWhiteprintRS20;FireDACOracleDriver;fmxdae;dxPScxPivotGridLnkRS20;dxWizardControlRS20;dxThemeRS20;dxPSdxOCLnkRS20;cxTreeListRS20;FireDACMSAccDriver;DataSnapIndy10ServerTransport;dxTileControlRS20;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + ..\..\..\dcu + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\;..\..\source\;$(DCC_UnitSearchPath) + 1033 + true + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + ..\..\..\dcu + ..\..\..\dcu + true + ..\..\..\bin + ..\..\..\source\;..\..\..\qdac\;..\..\source\;$(DCC_UnitSearchPath) + 1033 + ..\..\..\bin + + + + MainSource + + +

Form1
+ dfm +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + + + + + JSONDebug.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + + + + + True + False + + + 12 + + + + diff --git a/demo/YxdJson/JsonDebug/JSONDebug.res b/demo/YxdJson/JsonDebug/JSONDebug.res new file mode 100644 index 0000000000000000000000000000000000000000..d6cf6329cd078224d25f1d767092617dce629de1 GIT binary patch literal 62704 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'Msg', Msg); + ShowMessage(Json.ToString); + finally + Json.Free; + end; +end; + +procedure TForm1.ToRecordClick(Sender: TObject); +var + json:JSONObject; + AppMsg:TAppMsg; + t: Cardinal; + i: Integer; +begin + json:=JSONObject.Create; + json.Parse(mmo1.Text); + //json.ToRecord(AppMsg); + t := GetTickCount; + for I := 0 to 100000 do + json.ToRecord(AppMsg); + t := GetTickCount - t; + ShowMessage(AppMsg.Msg_Info); + ShowMessage(Format('%d , ʱ %dms', [i, t])); + json.Free; +end; + +end. diff --git a/demo/YxdJson/YxdJson_VS_QJson/ProjectGroup1.groupproj b/demo/YxdJson/YxdJson_VS_QJson/ProjectGroup1.groupproj new file mode 100644 index 0000000..9159bbf --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/ProjectGroup1.groupproj @@ -0,0 +1,36 @@ + + + {EEFE8642-A2A9-42FF-B5CD-1F523920AC8A} + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo.12.2.dproj b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.12.2.dproj new file mode 100644 index 0000000..4599ce3 --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.12.2.dproj @@ -0,0 +1,110 @@ + + + {1eaa4b4e-765f-4384-bd68-58870deb106d} + jsondemo.dpr + Debug + DCC32 + jsondemo.exe + 12.2 + True + Debug + Win32 + Application + VCL + + + true + + + true + Base + true + + + true + Base + true + + + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;$(DCC_UnitAlias) + + + 7.0 + False + False + 0 + RELEASE;$(DCC_Define) + + + 7.0 + DEBUG;$(DCC_Define) + ..\..\..\..\Source;$(DCC_UnitSearchPath) + ..\..\..\..\Source;$(DCC_ResourcePath) + ..\..\..\..\Source;$(DCC_ObjPath) + ..\..\..\..\Source;$(DCC_IncludePath) + + + Delphi.Personality.12 + VCLApplication + + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 2052 + 936 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + jsondemo.dpr + + + + True + + + 12 + + + + MainSource + + +
Form1
+
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + +
diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo.bdsproj b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.bdsproj new file mode 100644 index 0000000..8ee0132 --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.bdsproj @@ -0,0 +1,186 @@ + + + + + + + + + + + + jsondemo.dpr + + + 7.0 + + + 8 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + False + False + False + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + + 0 + 0 + False + 1 + False + False + False + 16384 + 1048576 + 4194304 + + + + + + + + ..\..\..\..\Source + + + + False + + + + + + False + + + True + False + + + False + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + TeeChart Pro 2012 Components + TeeTree 2 Components + + + + diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo.cfg b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.cfg new file mode 100644 index 0000000..ac26ed7 --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.cfg @@ -0,0 +1,39 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-LE"C:\Documents and Settings\All Users\Documents\RAD Studio\5.0\Bpl" +-LN"C:\Documents and Settings\All Users\Documents\RAD Studio\5.0\Dcp" +-U"..\..\..\..\Source" +-O"..\..\..\..\Source" +-I"..\..\..\..\Source" +-R"..\..\..\..\Source" diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo.dpr b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.dpr new file mode 100644 index 0000000..821fab7 --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.dpr @@ -0,0 +1,15 @@ +program jsondemo_2007; + +uses + Forms, + main in 'main.pas' {Form1}; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.Title := 'QJSON Demo and Test'; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo.dproj b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.dproj new file mode 100644 index 0000000..7ec3daf --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.dproj @@ -0,0 +1,433 @@ + + + {1eaa4b4e-765f-4384-bd68-58870deb106d} + jsondemo.dpr + Debug + DCC32 + jsondemo.exe + 15.4 + True + Debug + Application + VCL + 1 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + 2052 + Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + jsondemo + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + $(BDS)\bin\default_app.manifest + jsondemo_Icon.ico + 1033 + + + jsondemo_Icon.ico + + + 7.0 + 0 + False + 0 + RELEASE;$(DCC_Define) + + + ..\..\..\dcu + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + true + ..\..\..\source\;..\..\..\qdac\;$(DCC_UnitSearchPath) + 1033 + + + 7.0 + DEBUG;$(DCC_Define) + ..\..\..\..\Source;$(DCC_UnitSearchPath) + ..\..\..\..\Source;$(DCC_ResourcePath) + ..\..\..\..\Source;$(DCC_ObjPath) + ..\..\..\..\Source;$(DCC_IncludePath) + + + ..\..\..\dcu + ..\..\..\bin + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\ + 1033 + true + true + + + Delphi.Personality.12 + VCLApplication + + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 2052 + 936 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + jsondemo.dpr + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + False + + + + + jsondemo.exe + true + + + + + 1 + .dylib + + + 0 + .bpl + + + 1 + .dylib + + + 1 + .dylib + + + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + + + 1 + + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\x86 + 1 + + + + + 1 + + + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + res\drawable-xhdpi + 1 + + + + + 1 + + + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 0 + + + + + + 1 + + + Contents\MacOS + 0 + + + + + classes + 1 + + + + + + 1 + + + 1 + + + + + + res\drawable + 1 + + + + + Contents\Resources + 1 + + + + + + 1 + + + 1 + + + + + 1 + + + library\lib\armeabi-v7a + 1 + + + 0 + + + 1 + + + 1 + + + + + library\lib\armeabi + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + + + res\drawable-ldpi + 1 + + + + + 1 + + + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + 1 + + + + + + + + + + + 12 + + + + MainSource + + +
Form1
+
+ + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + + +
diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo.res b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.res new file mode 100644 index 0000000000000000000000000000000000000000..caf200bf65188b45a03669002ad049c6401743cc GIT binary patch literal 92500 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 + + {1eaa4b4e-765f-4384-bd68-58870deb106d} + jsondemo.dpr + Debug + AnyCPU + DCC32 + ..\..\..\bin\jsondemo.exe + + + 7.0 + False + False + 0 + RELEASE + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + + + 7.0 + DEBUG + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\source\;..\..\..\qdac\ + ..\..\..\bin + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\dcu + ..\..\..\bin + ..\..\..\dcu + + + Delphi.Personality + VCLApplication + +FalseTrueFalseTrueFalse1000FalseFalseFalseFalseFalse103312521.0.0.01.0.0.0jsondemo.dpr + + + + + MainSource + + +

Form1 + + + + + \ No newline at end of file diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.res b/demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.res new file mode 100644 index 0000000000000000000000000000000000000000..9591d434674db3d72259f7677f3dc89d0ebd8651 GIT binary patch literal 92780 zcmV)uK$gD%0000W00030{{R60{{R300000000000000000000?9{~U$00030{{sO3 z{{a910000G5CjMS000000001qP)NvvzanXPEA3+R0S|>m`%;Y`1B0emJeKE zfMxq(Y+@Q_=Q8-a4Z?K!{f29v^O>&U;2#VS)he_!C84{s1Dsl!{sOL3gYJ$tXig@; zb!!lb1fjd78JgoE`1&K;KKx&Q>ApX|+}Gsldil$^+y`)Voqvr^rasf%p4@Wa*l701 z=`onkR00IF&gzzE2;r9tcKsXS9pbwvO1(>$UfA`xK zj7-h!o0yr~-yDxxD+iZBQz8hh(GYA|zp{1D(b0c;+pTN=`f^{-tLyyhsseZ&tnWSX z!dus`>c4%*{!^JFN5^4ge9FD?npM9Sk3~AB=H_8GlYt^y_(Hx6nOqSH#R^nw4pd#2 zh1q0TC&4z@f?GZd1=YqUT`)+n@j1h?*aPre26_fUeY4^Hm!2|A101ye6%=q6AEZ?T zDG9@ZYPAM6du`1mMam*A?F*U6-IgVq0kvL1`eqm8 z%gD2+LKPnlDF=LE&2jlN98y5^PW3w}A^HsVF6e1gLAGV)A_2eMl#D`eXA=zfc7#ri zPJizQFCO@lf$lbIu(uCZ546Iq*R2liK0112)5_jMm-}A5S=Lnr@G7p){_n5*-*w*| zy?YLxbY9wj%Dwy6_5Z!UyY;7bA2>CC{LCbroS1{T`Ao&{^9|Xy9jTyz)llkD#Gq8J zpjAh~Cd*A$-4qauN6~_#fcw#s`z;h^8?A1T#Wxo6v*jmoH}Ul*TYhEfNs3K+-sXR@ zz$?!{0C>uPJOI)-B;+W!{C>e+gvDM1{Z1YM9b>*wWaU%9kfT-+P_EUWP$thrK(3Hy z55+;R#567N;c?LiTd15&+rY5Fhf2cDSF6=yZOt)%Z)X<_b|qo+y5-5EBa{E}z!Tg5 z_?GL}nVVL0TVMV5GiQGPH{Vsc-1qX0x~?jKOT4!2Ip!}GYUaaF?+~$wz3HdkaqIp2 z4xi2)IWbXl)DS8VZC1Hd^OM$Ol1;t-}Eu!oe{9^g{rJ z-bNwvE31wcm94&s0_C%8j>-L~6i9vrTX$erT`B-+o!JjC)bFlC;to8-bJBz5H8bJK>a z%HR^Ns|w%}uNU^6cn>Oo-bbI?HCrkbu35ce;MXQ+G6F3~Hj}Nr&KCElYbYD zgm&J3)A|-XvNLbJVcnN6_dR|it*Z*)hg^U6^+&sJ+Oq1J$DcivJ9zAjxc$b}pDR~t z1N#r3u8dF3M5>NxA~7>fiq|c6gMBE>B>drU0AgVuT6#Z3(F%r8Fb#gK$#N_d0zm&` zVaNZFd(L422^-3tWl6!L&!+d8AQgm#3dCgLRfeAqme6}rJdp@{5Q0oM$n1fO*1M1| zN~OU7gJ1xWzJ$O&`d%S548O)zipNC{fR2rXojeP2`5AOH45cIlD-8B5ltLl}kVR!v zER`XbFGC)`w}{GtlmvYjy_&oU3Ni5XQoZfX`8C6Re*Df;k8RucJ6qNd8TZ`0HoA4k zp`E|_uDi!B_a3;C*Hs1Z3f5FA*R$uyY4d?cUrZ17wf@S+b%Ve9{H~Mb4+0;;&ts~8*md?pKdG9H0=B!t3_f=@z-f?<=OkieP}+;a)dS9e9m-%`*Sd6yo% zOuzXtB*4!N27(+C*tX5!A7KUbIX*vm7Je_a$8G^@0aOgAKuklZ*dL_TCSi7^k^u!U z%G7|tVG6nOf`C6)o)^XZHGB`H7%`s5*HrN}Wr8L7y)K6iQjy3sgCW64JXQh=SByCbESGKKN*$+2w9185-f8y^?oSFK^ z`|jG3ShH%t?QDvTUhchdrLC(9V2SImzVe{i*VAe5JaD@8JwI{l{riufv3~H}zI3@# zy(f`~te>0DK@Ma4R5}N=>Ep2ov?SsXCpVtr`G9oqX+zUb9||kG?=>k5{6AOaLKNrYCU@DDc9=;= zD7j@2sOPd!^TYD9(37CkdjbKU!~-xih=ipAAjLq7U2s?aWNd%|J5YFljNKV8XjnoC zHM`>~3m~6YzYFF5^VpM;bWhk_r?=G<0GKaU*lW-Vpi-%@ zCqc@C{!R}913fMRex6MaTZSx*7nB{y7K$*REl7`mLBuMgGevy9f=bcmwBkT_J9M@- z!@;8?e{j>a!{bLsrgr?7cfVuD<=!(_!n&#e7Fl0=VC$L#M^4S7+y1G(-u4eX{o)ZA z8=GJzTrd=7kj}<-r4?|lb!U8P4T)~3RGNlBKg@@dKlZ4*p@E{bDk0^Q! zv2Yk90^lf+&*1nPL_K9!UyVY3q30X_E*AOyLhm{I^=E&N5@dxGl7%nTsjRis0=dc0xA#;I~@Eh3O!0S zK)nOYx|`wVEi2%}=*<7l6)K-vzoIYsbNAkK;BxPw*Ymon02W#Q`oMF~9X~xWeE7tf z2=2#_>zdM4Ct?n|^DT)GbhRd-IT-`m=48oedzi6w`gLMRkqEFc&R zqU$a5^N}3Xi7xiQms}j;^J2^UMGv+oQOhua|{v@bnhvi z_xst!H-x0-I?8G@=!KgyNERO77YvfX+w3tIjBpza-pO!Wy708)H8|&?@o*)FvrJBq z-j8L<-wjtP2K*262ONiS5ISv&`}3#>2s0?5+h22P%8jt(_2iE|MNO#J+h-tM28o&VDf!+rmLxv$9UX-E3IhLh;+Z>6q;K!mqbkjo+i-nkPPutQ}L>P|54dTX| zOxocbzE3jYe741?Jdu4sr`3S*J{@v50PiyTHVplTD|{&hXL7bdJwZUALK!%bNvRN( zjwBG!sYwn&4Icz1meI-+e&FEyY*S`VnDGdN7gS+BUx0b^3NraB1F`ejJfNe%WFx9U zSUJ!|uiM(wk$hs)s)3P#?v5{B?#uFeSZ{#c^~%@7&%Ttnearf{|J(PsmHK;I{^88% zZ1b}_55maQEQA692rvNWV+6k~nSjpbIBqD5i(}Zz(*>kdoeL5uqr5x{d5}OnLhtTa6e=z;4;=}n3RChQ443&2bSd+t8cfb*U~9h9Y;@(B~Fda+`ex4z+XT5 z+`jalqbCwnZAbAX2{$qGn&Uy}Y;Qu#A5k=16h{WG4OtaOIW|sd5xXwH@8PtZqR~oZ z-O!*s$HI;x_OO+wa@~fKT)E_n%_}bIKmQV-RhiB_$S3$L)w$F2X2UgL@h7d?;4uiD z?VvgYR0d^70*9o9P5EKWZIERQg1v`X4BIAVsG70>CHyysUHH0S$OnDh?df;E{l>(R zV`tvq-`)1)@UotCB;=dC+}G>1UT-=D@YU};>wo+0oBn8YV)~x%Kes11GnZYR&z4~> zoq;mClCfw6EqxMN(Zx^1!c5hrDm>b;TnPu#?IZ!UELnPMb2%*W^KDQvFPCD^F~P^x zLN8-`c87`WW-hewvN%Wb^9_A_j4N3g)P zbyKOgtL>48p4{=VpZ~F2-&C~*Z#o6=tw&$@Fz%KAc6?;2`^3nUF)=e|k${DR0cdNA zL2EJwNmK$6ir;n4oqc^>Tw{5}8+Q3ss)~zm)K3afb(Njz727KZ*p<9`=@BTG*f{$( zxUewUcctGZ1p=C~=3oH{$&gGzz9vGK9~;_5Xee2dpQEYH{G3o&ilgbts1#HQvkoxm z^QkB)&-{TrD*8O6&?6vXk%MZaqrJr$?&%ameeGlT`ak=LyRZAg<-S_4@tt|&ufO}o zqiySk`hWbNzwtzA<#7MUQSeufjL#Tkg{g`z&bj*-bhjiR9u6=%?^%+2NccON|&-*@cP&E0LSzx(i0yQ^i#U6an{cuTaR#qU6OzoVrYk`X^w<4KQP z;s7<~#%s_?7S@>BntSj)c<8@bC-HkFw(OSb{c0-EPk`tCYIdIbAn(W73cz!VFAxrB zfKh&|gcuD4JR0@Gy5RdSx`a{x%mp&8(v^A}30Ux5jZ1s`qCo)&^||^x7BUkTl9XhI zR4M>()2cFM0s%C;QR@bxR&a3?cSEwJrv_3%a4}M`3}aI%n8L7NW+?Nij#uGfhL2X`F-I+S;Phkca?3%{8w@ii`O63@!JPqlQkjG(~ ziijh`%gQNu{6EEDbQNMR9dY3auNSZgd7KP$ibn2&g7^&O*@%W?HY64xTxd`Lu6Ldb z)PPa|(4cU-=&L6yn9%j8e&jl2KhZky#$qhj)FXON%>i{wZ0K0u@TvjnJ(4A<&(H9(C9()nCrBFr&{wc@ z)`4pks3Q@lWql9|23Q%;<)H9@xEVD(j@fh;#;50CVlIoyAP4gp8nh+Duxh9m`Z`<3 ztCix%_8lAh%Fo_+o5&VRl{>FneI=u^3)UM=0dz%!q0RsDou^>sK*yK2A2`}_W+DYG z@dP9=&TmSDV7R*p20Gh0UB@M^hOCmSVjM`c)O=3z)aiGsZ8sI5x4_ygvb>O8fae)_ z$v)Mc$56R{L%F3=P$W2Ldj0yOKxik}gT%CdtvmdvUmL>;P|Z6v09QY@C9n!bg90#= zPhy-$BweYnG?URgZH@3Nqillob||c(YbsNBV&P{Rb>a8iez8#Rn#K}!u+oUa&;?O~ z8>~=)7F82c?FE%myMlRz;0xHS=*K2z;mF7o%%LZcMsIO;J`3$_QMhLH!0gD_?0bIp zzS|*JD(ylAFn+1_?B!l>C{*%j!r6R_6WxTmQrhu+r(Zv;deopJz97_{N zzglr%d?p2BbMsItqPtJsZHSYuT-iGE2HY1<=B5|lfWlSou&LR8IwdHrsTNXU*vih= zn{OC8WoBs0TDQg=SF2FP6?G6&J0{dykKA4A6A+C=*mV~e#@MQ-fW>@XS zsGwKmsB^XL$Bj*Ve4><2r_$*1&BGkVf7xP1T20l;dg0~KD7A9g zmw=)RW%{&rH@pZRl)#)-?FS9cud=8PcL$aodboiHb^eWpxql!v388qFIs;M4GJq~E z9Xk~kal=sR2;}q0?{|gQG0152r!TO$KnIl|3%~1noJ4BaLG=y}ck!vI+i8QNPCT$)Y5uiuhgkmoXaIDTddy@62}nV91q1_M3q5DNwlt{Lk7 z-jBY0OC}kP-v8R|_DjF6Xa(^2^ZS3%G{iq`-+LT(9Xy3ryn^w57&_YHuy(K))-3O3 zPJg*lMyo_Fs|BeHF~ZVtYIGX4e2&p)D$wz)Fc@AjPUA&-Ihk~?sip?^4Edbq{Tp6t zPW9GTj6s)$!{H_%ejjxQmi_$$+!5H~PB_$3n;MRUsfS?D#|j`A3Mgb-VhLPP;5e!d zzYYNMV=)XslNZ=6ml*_B1eT=`c-JfHp>D?XdTQ!PjXS6fTB+(lHkZRt%0VSk<0c-( zqU`ijQ@|vBysh%|!czpGuD|fY8|`2;<`GWK1HyOV<;ASfkey<%PPX2r@q8{m z)v2c+S*Dgm3=$ZpHsg6v6=D!wP?ueDLrww$q3FdbEO1RvQ)#7RxD9!Ac_Na(u*fo@ zE_~X}cN+jbeR8H!n(QU;-%WO*$>x&aeQ+{g^0onnrTQp zegYH?g^|>`5U?QbHv7oG3`8H;P{36hio5*YH*RUSb1@|UZHL^by?T-jW+BxIZJ&ejB{B|Fd! zB@4vqDzdoL1JIN`1se4ZH}3S1J=I90VZS%JyB_x*bSv+rq%njHc{Js=A?L5Z-_R*M zdcv%u`G#iSxzfUNze8@~QA0tg5IBn-SQgGqreJ1vp1mK7h&rYOJ0)Y%I+scGGL{gH z%~dXdd63s-DuNT8k1P8yQm5uM=sj*+(+9Com^%^CN7P*Ei&%!;hfbj)I0B>78HnJ0 zN}%_(YGr@v$g$%;H##x>-T(W=PdKl9hrjmgic$a=!~f2++xOk}y(eC5DGB%1nN%L# z%Ob29?xilVu&TcUt!IQ0YYg=vjPdgEkr~*w=Qx}mn?*OZ%)RF=*{4ccEh4A5C&EJ*pOt-eGejZp5ZsXPXXd8wI5p(DT1 z;rVRAM-4gFt{i}yH!p(~{p~0`L3ZgPsFmk24n2Hw9A4ai65X~*&iNC_BUB7aXRf1R zQ#8PI%GIyCi+Z9icv%7IX`70~1D>+r3_qj)CR@D*h(Z*&NO=Py-73K2k(+wDweG*wfYo%X{0QyQ2xhks!aVBy$FgqBI%Wbhp-l{ZMgIJA>;@2dnK^-e0_|%1$x)IkEz*#`2 zA+HftK}v#;M9?XPXxQ>OAG%gP$~w^Nf+4VMQx@J>va2tJF197H4z7`K<-CC^d1a2e zPPXI<*z6s-aux#0Vh#oD%;X##8<~U&R0K0K^N_=kjYg?a<{`ulSZ!5x2})^+dM2TP zzX5gHly`r$0?^935#!PKoRlnGBf7?f$xvu^{hC48uzCQMcT5&E(Y3?_7@tbRGcW9g zU58FH#^J+{@Y(kF!1n#YKYZpluZXV1uWJSHr+@n`tEaQ&HgsD*i&p>oBge;ODh37V z#?7l>%jRJet|SVH+~+i?VtjfQwr$@Jhfj>d)LfPYit_)u?ezsIy^U5x=t@BFQbwjn zDlxo#yte#2P@|^eP81Xp*`{O!mi2eSa6n-b z1d|%~cqnk4L?0;1v=2J3_@rm5iZRcJ7%=>x|{e@8*_1;MB09gvdnQ}4rIkb58c13}ps z#gM%K>2obLf{d;db@VZW?1w-?O@mHlW}(64VY4Ba znoGg+yAHz(FCBr2sT8?sj^FS527bct_h5+URNayP`O#l4Uv62t(0bh}fKPtmYi1%A zzCIcafAZAm%)N)v@{@%cTGk0SU9$?V-LL}MTVqftIclP<3#UdW;rX2h;UK;?i!Nj+ zAO~=`OJKhmhO73GnuDhmfU9Y+iuoo?F7T0If{cxIAq!DV+gh8Ur?VNlyIP^My@e|d zlTkmE$#tyI#N3((ECR@W`_wI%$8?cU6FZTBCl}rNN*QW2d8a^q^^0JV^{$riC&r_c z-zR~k0wKHrJ_0<+qo86JR$3|?;Z8b|Ehy8QdO(Q!uX5QX!%r}AsDkHM74qDv19BDL zQo)~Pe18dFN#G@Q%_!0FlV?B^h zc+`k-N{>p?lEvU>I#+=d-p6V5=uVGL!AZQo(=!=##fzZ(3mAq8MngcOafgL{2Q;}r z^8>YGXD#@%$HG*L@De#Ly^6{^0xO5Q;o8kZXn=Y!6bj&ZpjQ&MVR9x5J9i(3tve5~ z2V>z4Ff8luI@{v5sEL0pm#R;G;Sb*Ty6o)wx>Nuk|Ff?cKA%18v&~Ny%e7xTaD0@< zVlV6OhFh**3)gHIf>u0yh&>*5qVd2v87Ez9P`eOR_JPPg6_^%Sk~78O^K+Slv|;4;u^olQCn3-oHDkj zk41s5qQ$G0O89#PLts^k386@;A~VH7L@T*X*QCJA(`b5 zqu}Esh8{r_e};o->4Ti5p?-n8Chu{~J>4!4J**3gGhL2Q3vADlreL4Ee= ztSljcHFwGN??%H`5cOxgDH+v<~FvQas<^(+ca?EQj9i7S#T-VJL?QuC8yR zQeaCp4#AH^fd-1($z{jT!KRopmxn@To`sz_>jC`Y00=efPbey@?n59va9>XXGW0|s z&KM}ZfEIS4vly&g(rQ0YVAiiY>9Y5`ZJ(-c3S8=uPIfifGSdi0T3P^P$4_*@yo1=q0o??poO{UKBc5lA-0A(4zi6cuNHdMMJUbHD7Y1df`3tO!X&Z?LEp z7^yc)))S1JnS*@?j>G8q4CGMoisd37D>q<0@5XK<27#yC&#tc2m5d$~tF(wLV3c@8 z8N-vlu6DTLy0tL8yaxgp(otHM+QE?rwR`_@`0gXm!CWQ_wJPC2{$j{){Ue^!@BH~E ze?5P>W$WBs|2kSD<1@j&uGZ2YeEzGS&lW1bGC4b+BtHG}-cGpZwk@!FxR>M28a3|3 zBQK-X+;{K<>^XQ6X3$NfjcKEs$*`HOM1iH{?gf~h6>KyLhHB!Wj1vnCWf7X2V=#yw z$>3l&w6?{W$U`hW0_C`KEp-9XWqQ;JT<6pN}oGU3a7L?*!nv z3g-ZsH}|;yO6jmyj!2;#q^)PgM;fu>|a zF&j-;#_URTpSu5X2{QLwG-VA(P0k@qrSow7#26etdK%`_S)LW3Nn6@;tKWN9)gLH6 z2Yru+bIXNo*0|^#rbS8R;yKEsJ{d5 zee2DzYPgTlQ3x=Og^v|be}lTf-UmiJO^5iJ;9(xUvw153g+K(X za2V_mbyNg}`_3S&qr@A+G<{w=&sDBnoeI8` z)2xAJ*9pbkGdO)#Ts_AMfUIzhh1=yxy=WP0judv~y3_B> zGE~Jw4EVY7bG&F~L9M^4OYp3o!3eudry2EnCEcF;tZF>&uA@?GIy8}KKXtqyy#@o1 z*F@`FCCW7latVchE*ODXe3T6z6!85e#{@eXfp{|!qoNQ;&mcgv5+flVj>?!ma~vEm zHA{AxXD{IWrzRdJPfucaFa?tnvyejr!2~2jc@&2BUUUSddo<`0V_gTbFiE6X43c+Y zxb+mOki%do6kNS(5Z108gjPIup*$l}P`maXgD1D`gF~p8Xs8K|g7X>HZ{u0~#b4h4 z{@Uf1yQNCTtGY&}GcCfceR%)Dlb`rM-*}p6;V94stQ_cpcigcBWBz_f8>l9U!kZwP6fC|@s7&`q@V1}Z0~RDxQ*z{gI-$OL9`sXmcfxYd~4lnyngq_h*s zC5_9fRrvE*!0CAue#4RfrcgnkCt>h=s=R?Mew`FPN&yPj^UuBWGrX2snzcttPBIZZ zt7pm_J|C1#ak(wacUdu5pb8P>Yb=!;(S$$6{^Q&}1p@RP4tU2><7bI}V?l*60uw42 zJC|)g6#Y?{_l2QAu|6IbO;bv?B%r;$1>#LH@Do3u6q9KxpwDPv2Lij_R6LcM&A_1} zr{MJI32p~oE~D}>Y=s4>d#u8^vnM3W`@gW~fNsiS@_E)cMG6XEA>~pP6=n%;UcU-9 zt{dcuQIwYf13J0@^h6~JsNXWIe) zr?>7p^85e#ou}2DxGMB_cf!4QZGnxbRf$wXQ*tR!JUf?RkmdBqILNsXy6I)zc^6_~ zAAUI3u|Lmh{G2*{gcxyMJzcPJcmQMm2!5{16@~TLeJXSy01AbVEIwK9S|$x5n*m!? znK{RJ1REfohEi6$rjT|b_n;?13cw@*CJ&%mmP&#Iy;7CoLe*i6!Kszy^Hr$;cs7~K zP&HeBO&^xtfpUFSQmD$M&;@YqLBHZO56kV6zH?!i4b&m zbwEc)8+rpWbysII$T&u4JD4(U`CtzB%E5z2U~GI^W@5lp)MlBzS$b%wU=z}Jsv?Sh zXoaHjCejN7y&Vh&Coqhu;I${_B=s8DgU0HC$G5{t3{!}cLe-s?A%01?##g`ihab4C zwF8$`0XVMs^pnpY_^od~{(Nj2Zx{hhZEZ>T(YtSejjIQlWyVN3y7&290ru}b$}S!; z0cG)sb&gznDRxZ^Y0&JNZd?DiwuBY~R~D!3%IrmSbhN_o@;*qSD;f!h7`u{guIaG? z9aSo8qMHwy3|Pe?ibj?5^`r=#$m%QMcU4M|a08=M zW(DCAKLC{lrxj7zxU6ubyDi;+$t92gy({s6hRY*h;q~yIH|j+^+)A4$)20O7h|Oa# zKw+!j@h9~fps%h`{1cjb}6Z- z;-1^DgKO6fLntVjVHDq|(d|EaT!dc5@z(FP8Cr>#ijqr%Yl>9lDj}d_RDJPH z#r~6r-N5_vHgUsl_T<6vLLj!7wxL1-OWHbe`%w^0y8h%95C&iq@dt&Ty#t#Sg5mc` zw4cH@QVjHdLwN1Tq#>4A1;d_>1UX45R8bk{10l%y!{`yjAQy~634gDeesC;5H$Q2L z2cabyf!3C02I5RK6x0iX3!qfpqh<+K@Vd>-reS*P2}ikMrgu&h?$Qu7evluj45_t^$-Oy)!m8g>L@@ z%16a3h&eBv_k1TP8X{b6^%lf=vY3W?!$o3I=tVcK7qxgO9FRi5c`e7wr89jM-B~x6 zL&3$(M8Zm_J}LvBDN%8*EM)eG%iMh7sJNUcyi`zv!Y$Cs0}FKtoP3c#i>$qey`~cD{JnHd)ir4Bbff|9v&sS8hL_a&Glt58Dr&6~XhS{E zK&B-HUwQ{VQ~*Bq5colgf**cX7VKGoD|P@<;Rt#J2`B{c zfr`M9V}ZFFQd4UadW_AGNT8P%4Y7iEXtFTlHC!gI96SH&ZjhIyZ z`8=wtkA3mezy8SOki*NO06uj8KV0A0*8K3X(eVVeMe-LsLyZlnIMoXcq}xVfO*v^w@AVJ#pw>?E4J6@b zFpB^-L#DdiVj0W|T4slG`KSQO1&s4uc2Tu*VD7%7sJ$f2C27T7bln{a5pozVP-cKW zhd2SO0MPmuORNwK4iOv~+dDE&7AmDE+vdi>f87h$QTYkTy3;S4rO{EwZ8WDQfh*Y$! za2-jIA`mx&;RJ;R#b69F;W+*tK@R~v0`ffY`*?D5a}s(7P-)X}7xWZ>n|vrd#3Jaz z$LB+=y?G2Z$HpdLW;P?MB|%MgZn!3O{-^6v!xtAML!FPq_!H@tR#Z@f%ler3WGF>M zosynw%<*#ewFmw#$%h6x_5trf{bmdFa(1!u7nR1&YN z>x5gU_7V_9E@fwDCp2NK-_#su_prtrL;CTKp7?_T>t@mb7*~>ICaSKfoLa6jyNkq`5fShfk5TqlC`Jj$>m4IK=fbGF}y453=GZA168D=+vh=D z_vy*F3xH0su%($^jYW^=GzMwFQX1dsDz>1Z^5~jFKnlQAR!=ukl~Ea96%r^*0)*rh zaD9M3h~5E)b%YQ20_cMK*lXY`XxLJb&^hxpz|f#3!Br}2rsJvN=VUM(NJU#AooIr5 zFb-upPrOznM2Tb=k{Ckvw0CgP6`=}VUjtR!VdHnZ>{aHt(r{v88okQ0x)zq|(73Qt zQnylBbf1};N(|iTmb-p>oOX{vc0_>Z;ad= zjw|YgGn9IBWiFqD6xBDn?*%aNP6Z6<4l`*-2`=FQZmr4y9tk+P`bHK#g+hUgcwG7% z{5LTG$(<*VkGut_I*PsLdakD~O%Tcsn`(v-t@F0XGIV)sg95n7^|@$gpau=#Dr})1 zM${`KOx?4fkvPLIwp2(Wy#nDgnb)te0#X20AOOHvfvv&_%S)${a<^PL=+mgk^d}uh z6&)uZ+^ZtjM8J3DgN?*8FNgg0AKA8X_%pxrORq`Yz-y)ezV-0)ZKo$^fB)q8^lwZ~ zWx^DL-Lhph+M_ zs1p@RBoap9Z$US$S&=^+S&PqczhD%fnR_VkA~VNA&GY39Iqy$0^BmrI4kPzu?emP- zb9hk7v*1&9z(p6Ik$pn+i)F?FSg;&T)&bq#!)tD#({uG?*SWW<5yx0~EwmyqFM3@r z^mz?Ued_GtvtF+V%jbE&e3z?w{d3u^Au}x!C?v({(UnlB zQBkDRV4$!IR0K{Dg&rUDIRY)dWE7%{Ta~dc?-xfbxC4##>xNEMH5%9Rkymu(&e^wh z5%1{SP2D&MWT9dedRXD2pCc8T5K>9%VX0onZ`<^kh=dVvwt!ijs02tEkXJys0VxDJ zS4;8=Tvd1^iBMI(7iIjcvK@p%AO@L43(PflKq(N1s`@@k8MbvaL0^9tTUQc74hbAp z>tM?EX!Oxp49ljcXCaI3xaJd_RS>xNa!v3mMip)#OxI%!QjJPWYZBT!q`@Ivf-(Vz zj*r5>fA48Hd1i){cyl~DgFpW8^B@1!KVOP${jlpbQ2@{FI`q5G?>_O{FYYl&aKYEs6Z|@5)ilf{-WZhyAh(ig_Ty1V zHZ?&s9A>3L#3Pm$$Kv?Yaw=@O-E;=6c~17)BUhI4{i>Z70X+^0KE>`tIpy#m2Tpnp z|BZ1zS^Q#&0X}qX80^zkd!{-bU7E{`I^AEd(QWtQbrlkTd0qv;icwLQFM3-W3W@j? zXDa}s-X}nJ*ikT}LhBc%!_F?Y(NKg?W5oD1nue_iTMXdp`=K}mJdDGVJp}}H8ls+v zlr16hjBD~gpJ@H`J2D}%x}K`RTIdI@03fs_MI4c-;UkK zKlk*Dhm(^t^Du}P;BIvLZ@+0R-_#1Hwggn@2FzukfC8oa>Vdb>>s?+OJ9RAuoKgNi z15y!;`QwQM-w3W=BWs{jQh0+b6lx=%;aHthau}midXROy%Doc+$ZPtP?B>l{SB5yx*}F?h8D<^lzvQ&)@JO6^Rz z7gGQ_j@56F3Q8WU3{s2+r^Q|P@$$G8Cs-;ov*EQ0ztP=Um|-Z(cj?|x&p^{J6@ai+ z3o`T)Ouvs~b9x;i0mR0088fLV&=m~_zBV6v z(DQ!PSMk{$hvC6Tx5C71n%JXTQGMM1_n-Z*4_*QhTx?ssI_u12Du{90T}MYIzW4P9 zw!!Gc40N?6;rflM;9Wm{tEzaD8?lBrDm$NJwx8wGMLdd@E7Yubq2L(K%W~ss+0I)N zH#~dQ;BtZ#5+W0Y@xn*Y<)<6XF26$X`CX)hsADg>>TLP(cONreEycyJF{)c7#F#Jv zQVxpl>tc*g7N6?-DfWj#nREQS$y`~?!=tK2Be_I0SXtdCpboZ4>TDf__HY5EEX?zW z`-Reap0}VBfN>E8P)~ztoTZ_yxUQ~ipwT)F+g6AbIW8z43^W@B^TF4odf) zth{5ovdxz51Bdzy`Xr~o2!@$##3c)sx+ac_<;j{;C`Jua@C$N@D5T?UkZSFPTvIDl zeX<_`rT@s&?8k6mU|Bcg0zOOHYE9E-#6^V!8T157r{%P?LW?0Y zVxKxPo_5(ySIltL7E4-N6GD_`iO0E-BP~;CHmEBteHyu2YnnvUg!A$2enCu^hQY68(4BXHIU*Fsuhm|V^m`TXZSCul> z7z;I;pq~Z2uO&1{%q36?9Mn`LRaymlmo8ykWPOpMbxa7y!VruH`8*AWn!mbGK#%d? zA9xnF?Ks5YujR9*Tbtu+P1BhDB_ z4mPYFWEPg8CdF1!cuK{h1WFXcZ>a~x?xT>+K@)t~n@V!U3!@(n`Aejh3M_bFWrdZ@HAAR4CzbaXQ zS7nzz`plkH2TqKAY472acjikKcI|%j?k#Zlt(#f1QI`}_^b{kL0&rZ(dsxcQjk6El zRrr*2T2(4WjncM)0BAs$zd^7t)?!|`qsneUrtfSL?#w*K?P=LulbMF{a|y9$0FQyb z0vKow0AJ^(@%4F$>brR<{LIzoCLGdZFzcD~SL4#YVb?h!jH&7nEXf^UD@>#!!UPyJ zl+}p59GC9_&kdlIAyKpOE|kJaNOknXd|N*hnwp{Nry?PVp+sU4=m zxE|hi+h!(caIbp0;S>**Dk?pu4&)Uajic(?O|Q5IWF;E`NQ5YxDl`zXzZ{j=S}4F& z!6(q|uNCuLXKn~Nw33deMrl3t3S|c$1wx5TWXc`{GFxC#<9)DL5%}3Fa7~%=G|Q_ELF zy1k3N0InPqH3-^%80hPQzWz=KMT4?RlQ|5){U%wqsXQC^Y|W9S*yj)^70A5QnT4Ed z5%n4s@@TAJfK!t6SL$&wGnau!p4tW9e{v`I12*)uw-GS-GoSwO&wTw-U-wJ8Q}HGa zpFA`DvF|+o!h4_Ed4Lhl{?0ad&rjU}g9DvVELFS)6;&>Obu>sXujCTY*0a7*kyLa) zKbOldDP}aeB-P?}QkuKQC7VR(q1-&7_tat=Bw)oob*Yeq(EI}ZJw>ijdQWH$hVleVg$s~EV3whzc(RZRE|Zo$6u2M~@7HwS zN|-?WHJFi`;Hsn+BHMy=9oM?o&=IzD=K_xylCfxS{FTdSFzoc zkXx>7Drx&=;gG9Z*`cJGc(cty2@Di|LivRwm60W+)ps!j5KQ7pGv}W`K0wP~%jD4Q z&v4<7!D&53Ft0VXj?|5wdcSw_}2%Xhxu#~0zNy_oDBa_EFSpM zr#|@enM-xuFWEl*<|EG;kx=;YXLldFR_)#MYxpGD!v zSl^jXfgsQi<9}-C$sKsu!hqTw3n{ZQ40KhM-1!o9SJ%r~XDbAq7M0bYl0!f(%VdKR z+iOrFZ>+`q0II*B1}j89^P%FegcC3`umWa!`k@eSg=(ycJqjww3Iu%6)lOW2R)~cH zAhD!6VMvb_^X48xotfEqoQt2BQfFSD0oAFA$JWV;58l_d0VNDWA9;2^JiY5MlnP~N zZ;79bC&T~qQy=`fFI}qZd`WiccOKj7!;634?n9%0cKFozavH%s)ZYc~{*fD?wJ9oN z>zZuKY$%sbkLp^GPd%$2+hU8!BlJI4c9r3rvNt)`m6jic-9}4bVa#rpX{t`u>w-tx z+~Ja4jOoeBqpJ^O{VCR`pHw75g*}x2cV&Z3-Lu|QH&fqL$<>o(%vXJYAAT+9(I=l! zJPB!$z5ax#1ca(5;Bi94B~W9#sQ$vBfv804!TU(1$oWJQ%(q}T8gGWw(0Zr^f?V8H zVH(ev_Zg&OuqBg<8JFtRP9fSFVQ zzWUHMI59E>u}IMM`>pT(`N!Y)6PM~bUy@z=g@1a$YL17VdSUPJYtM|&2D)2Y;4NF$ zz#Z2OVHj3pwiQ!es2{v-d9+cF=MCVwZ|YK<-M`}e7gH$!mq#Wu`C@KdZN`8M-@@5DfWcvnA!GdyGqJJ7!7tAhSB%mtnLF*@m@FY-?a~ zQt=Rh<26+hjo;z3eele#Bk=eO2T>VhA!J+o&;xnrKYs2*hc3l+za%^LM_ywuQJ4+KQQUS2=6G&L8 zD!QpELzWXS7F@M@y5Eqz14HJqP0?_yFXt}2i9tk7jrMZujiI4@!uzY^wQ>W^)s;O7 zsoIb-1PlyH>NJzN2pmQuK&9N=tAQG~;Z2(9UkTGIHbMrKfa|x}TO=-601Z`V2aR)2 zg5MXAjbpqaA~MfI^klzkNp5JCr#C*naldNzr!JK;)x;(}q}1?=rnp+wpsMpSRLE<^ z_m%UiTkb0et)M7y4f~aO(FC9Up&h ztx^rGTGk17UcVYv4s`OoG(++5!>DnY`Y6Zj+&^tg2;) zNkm0%ug#I4XM}yIc#=fv#W;}QdO~|0)mxwFqm<7lCLf^ClL8=WFM)rA^b@U@+iZD>HN24=>Owan$aU6(SA{6*wf4q>ZS=-p!m|c)ogWMY z?>WQs7!}rI$&i4oCjV~xR5MeavkXQBEowzp64Jv#VK0TEFtcnm%nq)Bd}}KzfdJU9 z#Wevmldz*T2`$M4x2abpk*c?Z{C?^y7zzcsW3`+%yzrb1)ncUHTtqVnb&ERf{nsr3 zg~(<6{zAS?lZDxnO-6!n_~ZmUzWpE^ADy8gBhcEC{L*#n20rtyAGzi94|CmrsNMD0 zw!N*HY~lZT?xn+@oJr+TAggfKjcehC^@H4q!4)zVr^bym5K{JNYqaI(FG*!imS{lK z6*J45154wz@$<78>ib@TnxT5Y@eH{d_)%j=QENus627#iKF4IYiqm)$+jE>h5AJ*l z+;onKJwWL{A_#FiF3DSW6#Y|92413juf|#l6TEigtQ1rNSeDJ3o?GdfYWlFMdUW`* z8Bm|-^X5->=ANBk+3h$zyT7O(vAaUYxJ!C^ylRMZ6cVVuWPN(otD7CePDQO*|YX zH>oC|h7E7(_@H53NCC)6OVXqY)yddUUI8--Rfi|y2=F`wM;7K(P*LQYIw0TH3x)PB zNcSv9Pe6Kf1YA?ss@7zbTf@s9qCy!q#Vf@_pvIpx`Pg1izFr@~qWc9l_DHzcB1Klh zFd&smL%HZOH^3JNz{E@%Uf6dW4xXOmadUUwyq>r=zy6CqdB@+r((Cy{DS$8j^Mmi% zcVy)MZakTV7Q3jhkblNhWqY?sh)hbmYAN@6Lk%p?J&>Tu=3+9)vC7%eHEncKGxJJhFW+ z?}=E*2LoNLzx>e;{PdS!>GgcY3gEe&hu-<@jzgc=y6f1cXe12%T}ilp?Eo~z!%(8y zdUV5L)BvHQiQN~zAPuPhqBohq5kq)GI0(F>t}5wl0j8#Ap-4p<)ULN?@`VnfwU494 zjHA%U$OfBooHdW|BF15n06)s{XQ9f%Kga*3YChS()8W^nbHk6Bst^d<$D_} zlVp~w^51&tvVK<0{sqY-RI~4dE{HJ}f<#x>q6S2#4Y{<}P@E7`vIwaokx&zq$P1`C zve1XpgA^zhsjkVAw7fOpC4F-6qO&r4owBc*c*7g=P=oP_IhajlKsJ{_rQrHmXq&7mv>9ai@n+FIB4ULjd+`fu z!JVTN9}4}f9L-&un~`*2M_OyytfR3fp91y_ZM_$=zm~pX!9#!>iynls3TkV}&la-J z0cR;Fm0&8=7vZt-<8!z{Iz9I!M zHZlFg*B;t-#}A%A!0}NpTKQ{ME#rCbRqCXRo3|wyhk^bMzJO%o96iTxDPLg9tEG!3 z_;%tI8K0QuQP34NBY@&&KgP#?^xZq`DzuuylQ`~J%RT!FJeil=eW?JbqAv^1+#D)} zBB;Rbbp*7%tOVkla-B~JG%9AJp^ci5)H>OHm^^@nap4palJ%|$X~pSX#b5|h%ho`q ze+Viq9pGYkz;K_!XaYRj&&JTu((_5xF;VkR8`_ye&>~yFK|`USAzRWfWt-J84#{_- zp*S-b|5FvMAHSo9AVq5{q!zZ@KD1A`yaR{p}EqM(U{=Jt%T1AfTGTXX@@b zc;$!>J%Se^o6D=Fk&^aFu7A7Fg)a0wLZ(dl2-jrvpIUWMk3Nx|2RAhfHFW)rOb)7q z_S3{(LWN&9f`4_roON+5!0Y(JSpZWg!VMJ@SysKKfTk(eWH~x_sBnS3*IZi{58rs=o%;@-3eyO$p7v(gu)JGZWJC5?BMmsTypu;X>FS4igUclzl{fiTq)+K`&9u0<9#4v4?WQ0mq-C%%ug-IsK-C?#IGuV zD|anW0uAH8)e{O0tn)C0sRo#uvKcAk0aZDhLAG?@aUxLh1t8l!2&vVZpxD(b+qN}i zt>pkx4b5sH2gyV=DJu|#!NGnA*b+bThCFbM%2H>3NDPCjRVg)id}N9|Zxynd+=gh+ zkx>|#nuS8K0-IJXE4=recl=wqQvJ`%dfE$1y@oGq_dmbygqhFhPJHL_oqc#f22mzg z4|cH^K!RDqi`m*7h2;a?coC9Nz-z(fY9QSg(1qyAQkRDnMnW2q59|DZJ z(lL|*5Dl%*H5NcXVNZ7#dIIg>!+lv(eFj8BYVe}Vi+iP)OAbgnn}_2k$2j{%Jp(A? zI6a?*gQq573_bCljwbQ8Ti1_tv?d3yUq8H5H)H7v;JG~~Vt8qO{Hd)k{q>Pkli_@! z3WMFPux_w}50o@y1>LFDD|%sQuoo|)9Q-WfpM?rQ>uS@B)eKIZcoz{=t$;~Hy*%vM zcN~h;NJPjw0!r`2Yyq7CfF6v=g7opG3>D0>9JtBdc_+r%^{>(>E-LP!!5o#U>@Tms zzbFOhb^3C0#YV6<=6(HuO2{C)|t1j*{hnI=+ayQKdfIj%vmu85qW=$I-q;@%mky(e^Do)gu&ugWaM#> z0yuEw3}@IZMKn8dY8plH=tB{V>d z^QPJXAIAFyICAU^oF1K24MQcJH|Y~F5H_I6HlSJcjt5l(PMv(*Vit_KG&tj9;G|RF zP{9wn{!|G_d0+1FuQtWib!DxKmWwywdmi6ZDF9RV8U$^v4W_6|ffX$D#K0qulQ=B& zpklCMb0MT?xw3KD!_e^!0YZ@L)H@q5*U|T{XTO zbU}+cRGm?v-)qn~jhO`BP+15f)J@H0;K1S2Ff)?^+i&r#!VaNx2J=Fb*`@B2{9@GD0Hp1s|G=m{JEYBCG)qc?KER97Mw))X7~PMOvg1;$DJy^ zz9(eDrnt$IdP26G@Itv=9=TP_tC$}JpF9BK`+1CR8hK|)6 z6aX(*j*R2f9c~aB+*TEG99hj5hHUo`q&Hj()npqu!GJW*s_$eCUsJ3|K7lS2G8(|a z4Qq#Bkg{Rq4~qr`(BQ2|TDZ%aLwI#1j9$v2D5PMhmnMG7$jB7z+I@igAP`xJSg~iO z=HSHW42A|atX|dyTQ&^c8;gV=x?$t+qVdMk6u`vXz_F2ukN(Y99{w+e--Mtq0E3-L zXh}q%Sd&dSk})4#f88c%X^KL*R8%D}QUM4dJh!Q_IHIw1RwxjMLILWLJ~@?!J$p~W z*!VQ~Lq1dj4p4Jv%dlX1#0Q&`5}2WW@9b$%%8w`YWWi0(%5>lK1XuP^O}HSi$8}Wa zF_nM6O0apuuk+Hdbr!%dppjP0DM2(O(gP4I+=M!n6K#-MzXi&z9Z+d%=1_nez;ca; z0flM_3Z+^@Z%~Q)=g}MO>*|C}>z1Qe9D{0Ad%U1tsJJCUdBmpjSQ@frawMKiY*O{% zTq+CC?>GSIOpzPinn43*=5sJImVy$2nbj)1>)xCHa-h5YPp(-zcxtihvm^;Pa(wL9 z_njF1tFL|cIn(wT(AAuTHjHfo=z`FoMjK<2O>6pLa9KA512zxKWXhwd=cR#{Ff}v@ z#+0c(p^AG{sf3(EkD7Vsp5w4@-w94FQkYgVst`ae*oLmpnwSkk_^7JE68wSG&Wpf4 z8rCsC4I)KkpIM#`1A5q}H;eD8;J;bcc_%4@uE4C<52zQ*-hY%1#NW$AHKTu<7>*9E zgkpOK$DiB#;YYXa{`~1NDmt=YSyvMz!Vx*g9tATT4#V4TzYb!tpjS4@IauZL zpA)k&#n#n5bU@};O^$ag%Yo5}6ztf06vifI(c7>&eS>NRLZoPtK3JJh9B&FepvJ5& zF*gtH%q+On(+@oWn(#wYduVWktSuncpu85!ZEk+uWO+~an4l)~n`T4Y}EzNA`e<&G*UJ<;9qse`n$`;|VCwHLDD@e16hLPyPL<~Y#7`ofr z;qIH)|JFUXZ1}=r*Jn`)7#*AawdeO7{m54zczSI(6k;Y)S4$K^{typT2nI|T8tj2B z8&~i!#+u_al<=Nay`{w6lqEI|RsZVsXzFdh0Y7+l4;(r+0utG!q6`cA0tU3B^I%7C24?%rd(FGt=NqPIEnfErSXmTVSS_p}F7+D z0?>Pcs|0~D01g8{Dg){YRm1o{*S{Jv>#m1NS2vhEf570Ov-CYgi=WTrxxc@mCRo>? z%3$}(fnIpaO&b9~jT=+t;2QmRHD5tA4!{LzBn?+N2PRh>7AjSEarYrOeqs_zl`^Lt zi>PR(QU$KU&7nN}?2p{I|CVc3{b_ew^WRd~#cKk(SLeGrd^G42mR zEEr(doKkZw$r!x-j%%Pf8Rb-kSM1~EMH{G%hU7$2X|^5A5+}Gvz9||AYbB9+=i$s7NVks zx zn<`sC*u-om3y(j&4`yalXq2TqDGL{dt!|gY3f`F~Ta8t>P z>?ZA@gKF%VE(!O%1654NKYak-`~G$~c6x%rNb*7|c)U}L131uxhPk_=<@3M$z90MT zi(Q|K%0aPIef*!k`NW+M{$K|rF#ZVy4T#gYEwugwmL#GPxMss3y8pw>o&v*YNb89O zt-jB`@N||A10sUcd31qi=8N#H?>`IKOpb>?(2WS<-v?qrxTZM_-7y2*9a*Buz4{3G z&u6%kuQM?U)!8|2>fzFyKWhBo)GNa{yTU`>Y&AF(>ZFUS>kYLo1pJ-vb?5uLtAbUs zQx#&kI#p_Kg3Zz4w5RA4E_yF)VDG(? zY7|whE?JUgxyDZ7B(`7tp7WgdlDx#uKhJjZPhvZ+vL#!xEK61wRVYz~6iKm&O(enI zz=B>E*uFFG-uun$F2Dv^5C!PR&xQ!>0<*KTbI*6nx#yyroDn_n+7dZ*IeWdaXc?PYH@9l8Dgb-MtY^n=m4=WX^;KuUate&(C)aE(id>eQw>G@9sW) zWedv@`#cEntUXq-p~#3CPzz^F!cAAt6IEWV|8AP4l8Qb_Ktq&!g}MvXDaC3hr_Z$G z=@&PkSK_CXxH()ltq$n;^yoYaKNJzjWZ#E}i$+7D<7e>D&h}O$TgmBh2+w!pp}+DFD@4mQbljqg^Y9X zbPM(#IVr<%dFNO8u@wEihGlanVZodUCM0{b*$gMFE_{UdF_c+ z8)qLo)#}V)WfX^+Y0mp+nBoh51J^8>j>2F-E)LFhA={gj$qBW=QuC z1OV5l(aB()pw68f^6Pv6z{}E!5&&S4n?yc_R!dGJd*)t)4hg4xpzgIs>5`KCHL#S7pWetcrO zl_^crt%o20l>Xy);F&V@qdtxEE(QVy1a6>;A3uh~iQ`Dp&k6ee?EC2QHLoc=%LWy zrmN@UiuqGTMTjZta$n2FZ-*flw<#vhu_nh_vY%&GzoVMs97s11v8T@E`MFcahVz2{ z@>{Q&cUFhsS=O;*-%%|ZOO(B~e)paeXWEJqJXlk+ChZacBnb)2iVASs^$Rg`@+fqN z!s*ClKo?w{xy0fe4;?+xKyl1=>^yKBIrL_jsN;2LD5U2Wmj|${Dkv?Qs#k&)9AhxZ z=u>o)vf7Wq(|q_}cBN7__b^k^u|C<4`abKkhB^RTXvOyKL6p8CTeo~H0O7m|!z=(` zYH!qlVh94lxo0~towPAlRKWj<+u*DntGWxhsfaf)GQ`es0PZ${i;nr>go*#9ZpjXwcun0p#Y=b(s|Qx z<-(~lIfX?!Y#sEApRolKy5dRako&*xon3fk?G|aA;MswkbQziB!l)cCrWX4#sn9JK zU(SJr6`~Ol`A9Z5Q7p!N{yfklEVJNY>AD$KVpTxHW+V-vHJkw;d$Fg$fDn3d)vyJF z3xxsp^O6VPc5|i&li~9uD*jjI4-^%`Icpw*i?4*Iv_uAyO6K0=gr5(qH*Uw~-G{^& z?Xn&ddE72HuD^0FRxF(*ias*?f^{`uGh_#_=y0r7+)A6^Itl^a+OV6Bt28*(SaYXP zk$pZ#=>A)le>KPFern#d@jd+x(RaPEWzVcsDz)m#m$sDm#^Som!4lc2boX4G-r#5M zx*jv8)Jn3=cprliwY2buDGVtiso~j|*W>LiJCPUgOPM817j|x;7c&ZTFe2BBBEK%k zE6Bi2M$CVv8!yq)0Fy->yL)AnmzK(ipcvLPAI;D%1HdI(3;+xSy>GqoQJ=~mwa)1Y zSmZXY3UCt-m+d*Z#sCmas_$eA=*jhMIEf;nkkcl0#g+OiwRPd7=3 zt6s}Ms5gw|3ufZB8|54`R=Mah)14S1Gn34ad6lZc1qIG z?{^uuubA8D)U}&dESb5Z&oTNRl;Vj+dyk&kxP8wN#FKF-m<5}$UqHCfW*{>S_B}=H-7QyJ97 zUP1R0j}JQ_xDaqcJt#$T-m~!f z+uQNx=A8)m1CRifJcn>3f>Bi!SaHQ%Oq)1bl&(!%O(a_r^FqrSG{S>-jvhaO-#q>b zImmIPNWk=BZlN<>1PVH4OdN@cV=5P~STy~eK8NUk%(WZeoB8-m|yF2QXP=ZncLC^&PfE>&& z&4=GbE=(!`H=$258kQ_8kM&A6H4p$aD-jTm{S&Edkrhd(XqI-nIpFNXN3)zIrO5yM z7X=VLPK$w{{l`C&5Mem1^Ckl=MQh&e`5tEw2$NMuMK^%W*@0Mh7b?H-WfWewQabZ= zhg-Z{ZY0X{`LSht9ag>m4#LrdYVeVk;S`hqFnh*C+;Q{O;;O42Ad5FJpiWiAJ@9&6 zXl-f5t8Z<>{sSi{8cxY{~jPEHJ8_@lmhyO?&Dya}a9Kt7bTbtrw^7 zd+C0QB$Dd>Hywx)0OVi20>z)W5jkT)*wWy$7V5*0fsD zBebcFtsQ}T@BD;l29 zp{%UXlKMB(8vX;W1aik9x$^t=AIIx&zJmrrE|=FSU6MXB_^T=c1Ohq8b9>YM(&TR; z74DV+A3V@A)!d3yN0(}hkhZ|2qG>XL*tGf(puf*?ULWmZPoUx_KOeN?o&4nO1NuD% z00th<paW5~_-kyDd% zlO!1{Iew2oK|^s4B%hci)EmT%S@y&C>H5*vVQm&*BE&T)PFk-rJ4t zP>*t%z#9J=?jD(kG5JAh0agP-4QcMtLLq6yNOiWsAkZ**s*h<{5=s~D_!=&&{9pfF zcIClX0|kKoi$NfG<4uE}q+}1E^Vbg!yWD`iR5ieqL|}|ZcF`urq@Yxa6XOY_$mk2N zy$+=-Z-l$JNEoe3?aGi5;(PWSz^@;D+8Q&jOeaO21dHiRoiYhGTz3^*E+t7mP_H$+ zmw_v2JT3dN$Dc++Ln}we!V55UClY!92?{wDOec`a%lY&BZeEh!Y5Kg<>+jUvw`%RS zM>{&Z3@1|{>5n}=FD6c^#f{gmK#t!n-GG@H_X9;O*7_$@N|<=yp~rBhp+Qle znfOarCC>qTVO#~u$cXg%(NtzVI}9dJ1|Tkaa_jETOEURKNb)GRD9}QxJydNAUzf- z$Pls4Gp8Hz((1JoW}gt%A&$|N4xxdHiVA%0{<{#!@v6zv=KHZJWyHGEDWtsDUV8&| z`;VZvC#nE|fGDB)ks?DY%g@KPi>H2im-%l=#SHcfN+x~=>E{^yUcUcr*wIvKr- z0DuKG5@hTJ^vBDVFU0IwQ$(Ve--In_kZxMI*olh!pTg;eW<2`X(+G8U!^Q2CObT)V z6lbsⅇBW= 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⁢dP zY!#-As+8EqMK1T=x`XnSN+qnxKm9M8l?PC*1<0yNsFUPuD4oD$GXAMkCrf@o$t5DtG`U8My2Kar72T)0&z{O{k468L90bsBQK)m6qiCF|N84B?JJ;R8lrihvh);+H(N!y|)j3pC_CDn{`(GOM%&Ku(~xEM91Vw z6KK<&fCPmSI)&GDCz|lyp;M@DY{QHRRfoTJ-!tH>)=ru-Hm%R1={kKnDB}e`4gD-42eWrae3kOUeUx^!+ z%vJ#4cH`8k2JGCm7pG63fy?PqFVv?5XE$eOvi|8M`UaeljG>~k3^QgsTj!=`bTmDj_rov z8g2l*X>Ay)VZ})sTqA`T z9Cn|mKSd#cJAE4GZgbA40i}C@n4m-eP*p|tHe(8^t16J9^W=8pmo~hI-A7IeK`1X6 z*!bOV+`B}M@UzEX+i>JW)8gZ&+Ax_6>$5j6LL#Mf{yW+`uye;=95`?UZcEcoYVg9t zC!=K13(*ifdvcJCRIESFT!2|KXOLSm9uWcr-RAZmK8WtO-$u`&{iunygI~cE55T3$ zg#H^Wq2HY<4xXDG|xnmESn_JS{oAWQL>(D+|i+95t z?3mFbFmv`al$Mo9KEUJf;@2;(L*22Hs4Ol(RY~E-zxnd53ILvZb<2jG`;IN%d+;PC zjv)ZJb*U%=yIHp^62Wdh4BM>e04w1KM_M$C%D0xO6omK>GN3k}!Qa;wfe}(D4Wa=zFaw zAC3vtB3-6A+)Am{`XJBh^19p&_@S)9+Wk59Z>HV0b8^R+dc698<_z_et;IrcY? zHb4u7Brg@C9lJN7n13-I3PJ{!uo7_wPu23}qrIpcCu^o6=?&1mt~N*0DqFkB!@Yjl zOiZe+LO7aKz@R7L^?0!D-96Z`eYYqEGbiApD?J)ajoj17&LWX8rcIlKIdf(p5DdWJ z@+8^Suby6my@yX?N^K3MjvKl0Q#UPE0I+laiG8oG-!XChwgUtJ)wt)Tg(xg2A^>ob z+wa7t&D-$W>NnYKK*r6_BKv4a_c>>s88TslA@s)Sv!`O^4Ob&ZAGXd7N37V>+<>la z+t9sj3(Ak|SE^MkS;brd77=x7(BnoL0NDM~3Uc8ZQ%TPgKzKb;onYpmba3^TM!F0D zL;74Vb?k31TRTR>*`0ryVWx>SVpv@i_t!UwEEqHDxTmito{}$~1=yX6OL!`RWpv+$ z0)=R(s6|up7$nKq9oD_6Wa3OzR~F;iMKdv~rVLTu(B##5EO?IC+4k+b;Nft405`76 zWmw}z1V|jxNqF@&S7834+2oj~M6xW@+l${m^E&DdpTdF}cF$d#DSHndffZykp-T$SRUWMD~R_?!5cg&_Ro)wE*a&+on zB~Cb=-2ZEDxC%az)uK~o1MloY{O}<}w{3!Z?dxhfknWK7NR1_tG{p+k7yvA9LMI?{ zR+qr^x{(U{rElRf0Q6lSCIB$=ujJET`M|sXN6Gsa**iOI%znZWkdfG&?>)Njw6-oJ z&$LSX&%ORp`U+9pAL41iNfy~i3NSbsEHo$Da!YY+

xUN|6$sOVze+x2u!!Vae>t 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 J{}Sr&{{!rG$AJI< literal 0 HcmV?d00001 diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo_Icon.ico b/demo/YxdJson/YxdJson_VS_QJson/jsondemo_Icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b1c12989887292f05b49011e20d31365fbce26bb GIT binary patch literal 91106 zcmV)lK%c(=00966000000000008AeN09F7105C8B000000H`GZ0HPlO03aX$00000 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

Dq9)2^mPy*xZT%E*7J$2`U6V80Z55R^~Og+P3x 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Ųa32DprSbd z+`z=YSaDcH^6qZG6LU=?U1O)X$e!&n-Oib;4 zhKDD>$F~PK?)_YLqCGcQvJodJfZ?7t!2LZ!Ox-X(*oC�{tyj5MZtW<>}E!`dX`J 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 literal 0 HcmV?d00001 diff --git a/demo/YxdJson/YxdJson_VS_QJson/main.dfm b/demo/YxdJson/YxdJson_VS_QJson/main.dfm new file mode 100644 index 0000000..d7286cd --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/main.dfm @@ -0,0 +1,234 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'QJson & YxdJson Cmp Demo' + ClientHeight = 497 + ClientWidth = 899 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object Splitter1: TSplitter + Left = 481 + Top = 105 + Height = 392 + ExplicitLeft = 456 + ExplicitTop = 184 + ExplicitHeight = 100 + end + object Panel1: TPanel + Left = 0 + Top = 0 + Width = 899 + Height = 105 + Align = alTop + BevelOuter = bvNone + TabOrder = 0 + OnClick = Panel1Click + object Button1: TButton + Left = 8 + Top = 8 + Width = 117 + Height = 25 + Caption = #21019#24314' 100000 '#32467#28857 + TabOrder = 0 + OnClick = Button1Click + end + object Button2: TButton + Left = 140 + Top = 8 + Width = 117 + Height = 25 + Caption = #28155#21152#32467#28857 + TabOrder = 1 + OnClick = Button2Click + end + object Button3: TButton + Left = 273 + Top = 8 + Width = 117 + Height = 25 + Caption = #21152#36733#25991#20214 + TabOrder = 2 + OnClick = Button3Click + end + object Button4: TButton + Left = 406 + Top = 8 + Width = 117 + Height = 25 + Caption = #20445#23384#21040#25991#20214 + TabOrder = 3 + OnClick = Button4Click + end + object Button5: TButton + Left = 539 + Top = 8 + Width = 117 + Height = 25 + Caption = #35299#26512#25991#26412 + TabOrder = 4 + OnClick = Button5Click + end + object Button6: TButton + Left = 672 + Top = 8 + Width = 75 + Height = 25 + Caption = 'RTTI'#36816#34892#26102 + TabOrder = 5 + OnClick = Button6Click + end + object Button7: TButton + Left = 8 + Top = 39 + Width = 117 + Height = 25 + Caption = #27969#24335#21152#36733 + TabOrder = 6 + OnClick = Button7Click + end + object Button8: TButton + Left = 140 + Top = 39 + Width = 117 + Height = 25 + Caption = #25968#32452 + TabOrder = 7 + OnClick = Button8Click + end + object Button10: TButton + Left = 273 + Top = 39 + Width = 117 + Height = 25 + Caption = 'For..In' + TabOrder = 8 + OnClick = Button10Click + end + object Button11: TButton + Left = 406 + Top = 39 + Width = 117 + Height = 25 + Caption = #25353#36335#24452#36171#20540 + TabOrder = 9 + OnClick = Button11Click + end + object Button12: TButton + Left = 539 + Top = 39 + Width = 117 + Height = 25 + Caption = #26597#25214 + TabOrder = 10 + OnClick = Button12Click + end + object Button9: TButton + Left = 672 + Top = 39 + Width = 75 + Height = 25 + Caption = 'XXXXIf' + TabOrder = 11 + OnClick = Button9Click + end + object Button13: TButton + Left = 760 + Top = 8 + Width = 75 + Height = 25 + Caption = 'Invoke' + TabOrder = 12 + OnClick = Button13Click + end + object Button14: TButton + Left = 760 + Top = 39 + Width = 75 + Height = 25 + Caption = 'd2007 RTTI' + TabOrder = 13 + OnClick = Button14Click + end + object Button15: TButton + Left = 8 + Top = 70 + Width = 117 + Height = 25 + Caption = #32534#30721#27979#35797 + TabOrder = 14 + OnClick = Button15Click + end + object Button16: TButton + Left = 140 + Top = 70 + Width = 117 + Height = 25 + Caption = 'TryParse' + TabOrder = 15 + OnClick = Button16Click + end + object Button17: TButton + Left = 273 + Top = 70 + Width = 75 + Height = 25 + Caption = 'Button17' + TabOrder = 16 + OnClick = Button17Click + end + end + object mmResult: TMemo + Left = 0 + Top = 105 + Width = 481 + Height = 392 + Hint = 'QJson '#27979#35797#32467#26524 + Align = alLeft + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -12 + Font.Name = 'Courier New' + Font.Style = [] + ImeName = #20013#25991' - QQ'#20116#31508#36755#20837#27861 + ParentFont = False + ParentShowHint = False + ScrollBars = ssBoth + ShowHint = True + TabOrder = 1 + end + object mmResult2: TMemo + Left = 484 + Top = 105 + Width = 415 + Height = 392 + Hint = 'YxdJson '#27979#35797#32467#26524 + Align = alClient + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -12 + Font.Name = 'Courier New' + Font.Style = [] + ImeName = #20013#25991' - QQ'#20116#31508#36755#20837#27861 + ParentFont = False + ParentShowHint = False + ScrollBars = ssBoth + ShowHint = True + TabOrder = 2 + end + object OpenDialog1: TOpenDialog + Left = 104 + Top = 144 + end + object SaveDialog1: TSaveDialog + Left = 40 + Top = 144 + end +end diff --git a/demo/YxdJson/YxdJson_VS_QJson/main.pas b/demo/YxdJson/YxdJson_VS_QJson/main.pas new file mode 100644 index 0000000..7caa943 --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/main.pas @@ -0,0 +1,1217 @@ +unit main; + +interface +{$I 'qdac.inc'} +uses + Windows, Messages, SysUtils, Variants, Classes, Graphics, YxdStr, + Controls, Forms, Dialogs, StdCtrls, ExtCtrls,yxdjson, YxdRtti, qstring, qjson; + +type + TForm1 = class(TForm) + Panel1: TPanel; + mmResult: TMemo; + Button1: TButton; + Button2: TButton; + Button3: TButton; + Button4: TButton; + OpenDialog1: TOpenDialog; + SaveDialog1: TSaveDialog; + Button5: TButton; + Button6: TButton; + Button7: TButton; + Button8: TButton; + Button10: TButton; + Button11: TButton; + Button12: TButton; + Button9: TButton; + Button13: TButton; + Button14: TButton; + Button15: TButton; + Button16: TButton; + Button17: TButton; + mmResult2: TMemo; + Splitter1: TSplitter; + procedure Button1Click(Sender: TObject); + procedure Button2Click(Sender: TObject); + procedure Button3Click(Sender: TObject); + procedure Button4Click(Sender: TObject); + procedure Button5Click(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure Button6Click(Sender: TObject); + procedure Button7Click(Sender: TObject); + procedure Button8Click(Sender: TObject); + procedure Button10Click(Sender: TObject); + procedure Button11Click(Sender: TObject); + procedure Button12Click(Sender: TObject); + procedure Button9Click(Sender: TObject); + procedure Button13Click(Sender: TObject); + procedure Button15Click(Sender: TObject); + procedure Button16Click(Sender: TObject); + procedure Button17Click(Sender: TObject); + procedure Panel1Click(Sender: TObject); + procedure Button14Click(Sender: TObject); + private + { Private declarations } + procedure DoCopyIf(ASender,AItem:TQJson;var Accept:Boolean;ATag:Pointer); + procedure DoDeleteIf(ASender,AChild:TQJson;var Accept:Boolean;ATag:Pointer); + procedure DoFindIf(ASender,AChild:TQJson;var Accept:Boolean;ATag:Pointer); + procedure DoCopyIfY(ASender: JSONBase; AItem:PJSONValue;var Accept:Boolean;ATag:Pointer); + procedure DoDeleteIfY(ASender: JSONBase; AChild:PJSONValue;var Accept:Boolean;ATag:Pointer); + procedure DoFindIfY(ASender: JSONBase; AChild:PJSONValue;var Accept:Boolean;ATag:Pointer); + public + { Public declarations } + function Add(X,Y:Integer):Integer; + end; +type + TRttiTestSubRecord=record + Int64Val: Int64; + UInt64Val: UInt64; + UStr: String; + AStr:AnsiString; + SStr:ShortString; + IntVal: Integer; + MethodVal: TNotifyEvent; + SetVal: TBorderIcons; + WordVal: Word; + ByteVal: Byte; + ObjVal: TObject; + DtValue: TDateTime; + tmValue: TTime; + dValue:TDate; + CardinalVal: Cardinal; + ShortVal: Smallint; + CurrVal: Currency; + EnumVal: TAlign; + CharVal: Char; + VarVal:Variant; + ArrayVal: TBytes; + {$IFDEF UNICODE} + IntArray:TArray; + {$ENDIF} + end; + TRttiUnionRecord=record + case Integer of + 0:(iVal:Integer); +// 1:(bVal:Boolean); + end; + + TRttiTestRecord=record + Name:QStringW; + Id:Integer; + SubRecord:TRttiTestSubRecord; + UnionRecord:TRttiUnionRecord; + end; + //Test for user + TTitleRecord = packed record + Title: TFileName; + Date: TDateTime; + end; + + TTitleRecords = packed record + Len: Integer; + TitleRecord: array[0..100] of TTitleRecord; + end; + TFixedRecordArray=array[0..1] of TRttiUnionRecord; + TRttiObjectRecord=record + Obj:TStringList; + end; + TDeviceType = (dtSM3000,dtSM6000,dtSM6100,dtSM7000,dtSM8000); + //λ + TRCU_Cmd = record + ID:string; //ID Ϊ豸+.+INDEX + DevcType:TDeviceType;//豸ͣ SM-6000 + Rcu_Kind:Integer;// + Name:string; //ƣ̵ + KEY_IDX:Integer;//˫ϼֵ255 + SHOW_IDX:Integer;//ʾ˳ + {$IFDEF UNICODE} + Cmds:TArray>;// ֽ,пǶģʽ + {$ENDIF} + //ֵ + ResultValue:string;//ֵĹʽjsonʽ + RCU_Type_ID:string;// ID + RCU_Type_Name:string; //ƣ SM-6000 +// procedure Clear; + end; + //Ϣһϵ + TSence = record + Name:string;// + {$IFDEF UNICODE} + Cmds:TArray;//TArray; + {$ENDIF} + end; + //ÿͷϢ + TRoom = record + Hotel_ID:string; //ƵID + Hotel_Code:string; //Ƶ + Room_ID:string; //ͷID + ROOM_Name:string; //ʵĿͷ + //ͷţXXXX = X.X.X.X + Room_Code:string;//ͷ Ϊ˱ڿͻ˵ãRoom_Code + RCU_TYPE_ID:string;//豸 + RCU_Type_Name:string; //RCU + RCU_HOST:string; + RCU_Port:string; + {$IFDEF UNICODE} + Cmds:TArray;//ԭʼϢ + {$ENDIF} + //ͷ豸ϢԼ豸 +// Cmd_Name_Ids:TNameValueRow; // IDƶӦ ԭ + //ƵͷijϢһӦ + {$IFDEF UNICODE} + Sences:TArray; + {$ENDIF} +// procedure Clear; + end; +var + Form1: TForm1; + +implementation +uses typinfo{$IFDEF UNICODE},rtti{$ENDIF}; +{$R *.dfm} + +function GetFileSize(AFileName:String):Int64; +var + sr:TSearchRec; + AHandle:Integer; +begin +AHandle:=FindFirst(AFileName,faAnyFile,sr); +if AHandle=0 then + begin + Result:=sr.Size; + FindClose(sr); + end +else + Result:=0; +end; +function TForm1.Add(X, Y: Integer): Integer; +begin +Result:=X+Y; +end; + +procedure TForm1.Button10Click(Sender: TObject); +var + AJson,AItem:TQJson; + YJson: JSONObject; + YItem: PJSONValue; + S:String; +begin +s := ''; +AJson:=TQJson.Create; +try + AJson.Add('Item1',0); + AJson.Add('Item2',true); + AJson.Add('Item3',1.23); + for AItem in AJson do + begin + S:=S+AItem.Name+' => '+AItem.AsString+#13#10; + end; + mmResult.Lines.Add(S); +finally + AJson.Free; +end; +s := ''; +YJson:=JSONObject.Create; +try + YJson.put('Item1',0); + YJson.put('Item2',true); + YJson.put('Item3',1.23); + for YItem in YJson do + begin + S:=S+YItem.FName+' => '+YItem.AsString+#13#10; + end; + mmResult2.Lines.Add(S); +finally + YJson.Free; +end; +end; + +procedure TForm1.Button11Click(Sender: TObject); +var + AJson:TQJson; + YJson:JSONObject; +begin +YJson:=JSONObject.Create; +try + //ǿ·ʣ·ڣᴴ··ָ./\֮һ + YJson.ForcePath('demo1.item[0].name').AsString:='102'; + YJson.ForcePath('demo1.item[0].name').AsString:='103'; + YJson.ForcePath('demo1.item[1].name').AsString:='100'; + try + ShowMessage('YxdJSON ׳һ쳣'); + YJson.ForcePath('demo1[0].item[1]').AsString:='200'; + except + //Ӧ׳쳣demo1Ƕ飬Ǵ + end; + //ʵ6Ԫأǰ5ԪػԶΪnull + YJson.ForcePath('demo2[5]').AsInteger:=103; + //ǿƴһȻAddӳԱ + YJson.ForcePath('demo3[]').AsJsonArray.add(1.23); + //Ĵ뽫"demo4":[{"Name":"demo4"}]Ľ + YJson.ForcePath('demo4[].Name').AsString:='demo4'; + //ֱǿ· + YJson.ForcePath('demo5[0]').AsString:='demo5'; + mmResult2.Text:=YJson.ToString(4); + mmResult2.Lines.add(YJson.ForcePath('demo1.item[1]').GetPath()); +finally + YJson.Free; +end; +AJson:=TQJson.Create; +try + //ǿ·ʣ·ڣᴴ··ָ./\֮һ + AJson.ForcePath('demo1.item[0].name').AsString:='1'; + AJson.ForcePath('demo1.item[1].name').AsString:='100'; + try + ShowMessage('QJson ׳һ쳣'); + AJson.ForcePath('demo1[0].item[1]').AsString:='200'; + except + //Ӧ׳쳣demo1Ƕ飬Ǵ + end; + //ʵ6Ԫأǰ5ԪػԶΪnull + AJson.ForcePath('demo2[5]').AsInteger:=103; + //ǿƴһȻAddӳԱ + AJson.ForcePath('demo3[]').Add('Value',1.23); + //Ĵ뽫"demo4":[{"Name":"demo4"}]Ľ + AJson.ForcePath('demo4[].Name').AsString:='demo4'; + //ֱǿ· + AJson.ForcePath('demo5[0]').AsString:='demo5'; + mmResult.Text:=AJson.AsJson; +finally + AJson.Free; +end; +end; + +procedure TForm1.Button12Click(Sender: TObject); +var + AJson:TQJson; + YJson:JSONObject; + AList:TQJsonItemList; + YList:JSONList; +begin +AJson:=TQJson.Create; +try + AJson.Parse( + '{'+ + '"object":{'+ + ' "name":"object_1",'+ + ' "subobj":{'+ + ' "name":"subobj_1"'+ + ' },'+ + ' "subarray":[1,3,4]'+ + ' },'+ + '"array":[100,200,300,{"name":"object"}]'+ + '}'); + AList:=TQJsonItemList.Create; + AJson.ItemByRegex('sub.+',AList,true); + mmResult.Lines.Add('ItemByRegexҵ'+IntToStr(AList.Count)+''); + AList.Free; + mmResult.Lines.Add('ItemByPath(''object\subobj\name'')='+AJson.ItemByPath('object\subobj\name').AsString); + mmResult.Lines.Add('ItemByPath(''object\subarray[1]'')='+AJson.ItemByPath('object\subarray[1]').AsString); + mmResult.Lines.Add('ItemByPath(''array[1]'')='+AJson.ItemByPath('array[1]').AsString); + mmResult.Lines.Add('ItemByPath(''array[3].name'')='+AJson.ItemByPath('array[3].name').AsString); +finally + AJson.Free; +end; +yJson:=JSONObject.Create; +try + yJson.Parse( + '{'+ + '"object":{'+ + ' "name":"object_1",'+ + ' "subobj":{'+ + ' "name":"subobj_1"'+ + ' },'+ + ' "subarray":[1,3,4]'+ + ' },'+ + '"array":[100,200,300,{"name":"object"}]'+ + '}'); + YList := JSONList.Create; + yJson.ItemByRegex('sub.+',YList,true); + mmResult2.Lines.Add('ItemByRegexҵ'+IntToStr(YList.Count)+''); + YList.Free; + mmResult2.Lines.Add('ItemByPath(''object\subobj\name'')='+yJson.ItemByPath('object\subobj\name', '\').AsString); + mmResult2.Lines.Add('ItemByPath(''object\subarray[1]'')='+yJson.ItemByPath('object\subarray[1]', '\').AsString); + mmResult2.Lines.Add('ItemByPath(''array[1]'')='+yJson.ItemByPath('array[1]').AsString); + mmResult2.Lines.Add('ItemByPath(''array[3].name'')='+yJson.ItemByPath('array[3].name').AsString); +finally + yJson.Free; +end; +end; + +procedure TForm1.Button13Click(Sender: TObject); +{$IFNDEF UNICODE} +begin + ShowMessage('ֵ֧Ĺ'); +{$ELSE} +var + AJson:TQJson; + AValue:TValue; + YJSON: JSONObject; +begin +AJson:=TQJson.Create; +try + with AJson.Add('Add') do + begin + Add('X').AsInteger:=100; + Add('Y').AsInteger:=200; + end; + AValue:=AJson.ItemByName('Add').Invoke(Self); + mmResult.Lines.Add(AJson.AsJson); + mmResult.Lines.Add('.Invoke='+IntToStr(AValue.AsInteger)); +finally + AJson.Free; +end; +YJSON:=JSONObject.Create; +try + with YJSON.AddChildObject('Add') do + begin + Add('X').AsInteger:=100; + Add('Y').AsInteger:=200; + end; + AValue:=YJSON.getItem('Add').AsJsonObject.Invoke(Self); + mmResult2.Lines.Add(YJSON.ToString(4)); + mmResult2.Lines.Add('.Invoke='+IntToStr(AValue.AsInteger)); +finally + YJSON.Free; +end; +{$ENDIF} +end; + +procedure TForm1.Button14Click(Sender: TObject); +var + yjson: JSONObject; +begin + yjson := JSONObject.Create; + try + yjson.PutObject('test', Self); + yjson.GetJsonObject('test').GetItem('Caption').AsString := 'YxdJson RTTI Test'; + yjson.GetJsonObject('test').ToObject(Self); + mmResult2.Text := yjson.ToString(4); + finally + yjson.Free; + end; +end; + +procedure TForm1.Button15Click(Sender: TObject); +var + AJson:TQJson; + YJson:JSONObject; + S:String; +begin +AJson:=TQJson.Create; +try + AJson.Add('Text').AsString:='Hello,й'; + ShowMessage(AJson.Encode(True,True)); + AJson.Parse(AJson.Encode(True,True)); + ShowMessage(AJson.AsJson); +finally + AJson.Free; +end; +YJson:=JSONObject.Create; +try + YJson.put('Text', 'Hello,й'); + ShowMessage(YJson.tostring(4, True)); + YJson.Parse(YJson.tostring(4, True)); + ShowMessage(YJson.ToString(4)); +finally + YJson.Free; +end; +end; + +procedure TForm1.Button16Click(Sender: TObject); +var + AJson:TQJson; + Yjson: JSONObject; + procedure DoTry(S:QStringW); + begin + if AJson.TryParse(S) then + ShowMessage(AJson.AsString) + else + ShowMessage('QJson ʧ'#13#10+S); + end; + procedure DoTry2(S:JSONString); + begin + if Yjson.TryParse(S) then + ShowMessage(Yjson.ToString) + else + ShowMessage('YJson ʧ'#13#10+S); + end; +begin +AJson:=TQJson.Create; +try + DoTry('{aaa}'); + DoTry('{"aaa":100}'); +finally + AJson.Free; +end; +Yjson:=JSONObject.Create; +try + DoTry2('{aaa}'); + DoTry2('{"aaa":100}'); +finally + Yjson.Free; +end; +end; + +procedure TForm1.Button17Click(Sender: TObject); +var + YJson:JSONObject; +begin +YJson:=JSONObject.Create; +try + //ǿ·ʣ·ڣᴴ··ָ./\֮һ + YJson.ForcePath('demo1.item[0].name').AsString:='1'; + YJson.ForcePath('demo1.item[0].name').AsString:='122'; + YJson.ForcePath('demo1.item[1].name').AsString:='100'; + //Ĵ뽫"demo4":[{"Name":"demo4"}]Ľ + YJson.ForcePath('demo4[].Name').AsString:='demo4'; + mmResult2.Text:=YJson.ToString(4); +finally + YJson.Free; +end; +end; + +procedure TForm1.Button1Click(Sender: TObject); +var + AJson:TQJson; + YJson:JSONObject; + I:Integer; + T:Cardinal; +begin +AJson:=TQJson.Create; +try + T:=GetTickCount; + for I := 0 to 1000000 do + AJson.Add('_'+IntToStr(I),Now); + T:=GetTickCount-T; + mmResult.Clear; + mmResult.Lines.Add('qjson 1000,000ʱ:'+IntToStr(T)+'ms'); +finally + AJson.Free; +end; +yJson:=JSONObject.Create; +try + T:=GetTickCount; + for I := 0 to 1000000 do + yJson.put('_'+IntToStr(I),Now); + T:=GetTickCount-T; + mmResult2.Clear; + mmResult2.Lines.Add('yjson 1000,000ʱ:'+IntToStr(T)+'ms'); +finally + yJson.Free; +end; +end; + +procedure TForm1.Button2Click(Sender: TObject); +var + AJson:TQJson; + YJSON:JSONObject; + TestRecord:TRttiTestRecord; +begin +AJson:=TQJson.Create; +YJson:=JSONObject.Create; +try + TestRecord.Id:=10001; + TestRecord.Name:='Complex Record'; + TestRecord.UnionRecord.iVal:=100; + TestRecord.SubRecord.Int64Val:=1; + TestRecord.SubRecord.UInt64Val:=2; + TestRecord.SubRecord.UStr:='Test String'; + TestRecord.SubRecord.IntVal:=3; + TestRecord.SubRecord.MethodVal:=Button2Click; + TestRecord.SubRecord.SetVal:=[{$IFDEF UNICODE}TBorderIcon.{$ENDIF}biSystemMenu]; + TestRecord.SubRecord.WordVal:=4; + TestRecord.SubRecord.ByteVal:=5; + TestRecord.SubRecord.ObjVal:=Button2; + TestRecord.SubRecord.DtValue:=Now; + TestRecord.SubRecord.tmValue:=Time; + TestRecord.SubRecord.dValue:=Now; + TestRecord.SubRecord.CardinalVal:=6; + TestRecord.SubRecord.ShortVal:=7; + TestRecord.SubRecord.CurrVal:=8.9; + TestRecord.SubRecord.EnumVal:={$IFDEF UNICODE}TAlign.{$ENDIF}alTop; + TestRecord.SubRecord.CharVal:='A'; + TestRecord.SubRecord.VarVal:=VarArrayOf(['VariantArray',1,2.5,true,false]); + SetLength(TestRecord.SubRecord.ArrayVal,3); + TestRecord.SubRecord.ArrayVal[0]:=100; + TestRecord.SubRecord.ArrayVal[1]:=101; + TestRecord.SubRecord.ArrayVal[2]:=102; + AJson.Add('IP','192.168.1.1'); + with AJson.Add('FixedTypes') do + begin + AddDateTime('DateTime',Now); + Add('Integer',1000); + Add('Boolean',True); + Add('Float',1.23); + Add('Array',[1,'goods',true,3.4]); + {$IFDEF UNICODE} + Add('RTTIObject').FromRtti(Button2); + Add('RTTIRecord').FromRecord(TestRecord); + {$ENDIF} + end; + with AJson.Add('AutoTypes') do + begin + Add('Integer','-100'); + Add('Float','-12.3'); + Add('Array','[2,''goods'',true,4.5]'); + Add('Object','{"Name":"Object_Name","Value":"Object_Value"}'); + Add('ForceArrayAsString','[2,''goods'',true,4.5]',jdtString); + Add('ForceObjectAsString','{"Name":"Object_Name","Value":"Object_Value"}',jdtString); + end; + with AJson.Add('AsTypes') do + begin + Add('Integer').AsInteger:=123; + Add('Float').AsFloat:=5.6; + Add('Boolean').AsBoolean:=False; + Add('VarArray').AsVariant:=VarArrayOf([9,10,11,2]); + Add('Array').AsArray:='[10,3,22,99]'; + Add('Object').AsObject:='{"Name":"Object_2","Value":"Value_2"}'; + end; + mmResult.Clear; + mmResult.Lines.Add('QJSON ӲԽ:'); + mmResult.Lines.Add(AJson.Encode(True)); + YJson.put('IP','192.168.1.1'); + with YJson.addChildObject('FixedTypes') do + begin + putDateTime('DateTime',Now); + put('Integer',1000); + put('Boolean',True); + put('Float',1.23); + addChildArray('Array',[1,'goods',true,3.4]); + {$IFDEF UNICODE} + putObject('RTTIObject', Button2); + putRecord('RTTIRecord', TestRecord); + {$ENDIF} + end; + with YJson.addChildObject('AutoTypes') do + begin + put('Integer','-100'); + putJSON('Float','-12.3'); + putJSON('Array','[2,''goods'',true,4.5]'); + putJSON('Object','{"Name":"Object_Name","Value":"Object_Value"}'); + put('ForceArrayAsString','[2,''goods'',true,4.5]'); + put('ForceObjectAsString','{"Name":"Object_Name","Value":"Object_Value"}'); + end; + with YJson.addChildObject('AsTypes') do + begin + put('Integer', 123); + put('Float', 5.6); + put('Boolean', False); + put('VarArray', VarArrayOf([9,10,11,2])); + putJSON('Array', '[10,3,22,99]'); + putJSON('Object', '{"Name":"Object_2","Value":"Value_2"}'); + end; + mmResult2.Clear; + mmResult2.Lines.Add('YxdJSON ӲԽ:'); + mmResult2.Lines.Add(YJson.ToString(4)); +finally + AJson.Free; + YJson.Free; +end; +end; + +procedure TForm1.Button3Click(Sender: TObject); +var + AJson:TQJson; + YJson:JSONObject; + T:Cardinal; + i: Integer; + Speed:Cardinal; + procedure PreCache; + var + AStream:TMemoryStream; + begin + AStream:=TMemoryStream.Create; + try + AStream.LoadFromFile(OpenDialog1.FileName); + finally + AStream.Free; + end; + end; +begin +if OpenDialog1.Execute then + begin +// uJsonTest; + try + YJson:=JSONObject.Create; + try + T:=GetTickCount; + for i := 0 to 2 do + YJson.LoadFromFile(OpenDialog1.FileName); + T:=GetTickCount-T; + if T>0 then + Speed:=(GetFileSize(OpenDialog1.FileName)*1000 div T) + else + Speed:=0; + mmResult2.Clear; + mmResult2.Lines.Add('صJSONļݣ'); + mmResult2.Lines.Add(YJson.ToString(4)); + mmResult2.Lines.Add('YxdJsonʱ:'+IntToStr(T)+'msٶ:'+RollupSize(Speed)); + //mmResult2.Lines.Add(YJson.ToString(4)); + finally + YJson.Free; + end; + except end; + end; + + try + AJson:=TQJson.Create; + try + T:=GetTickCount; + for i := 0 to 2 do + AJson.LoadFromFile(OpenDialog1.FileName); + T:=GetTickCount-T; + if T>0 then + Speed:=(GetFileSize(OpenDialog1.FileName)*1000 div T) + else + Speed:=0; + mmResult.Clear; + mmResult.Lines.Add('صJSONļݣ'); + mmResult.Lines.Add(AJson.Encode(True)); + mmResult.Lines.Add('QJsonʱ:'+IntToStr(T)+'msٶ:'+RollupSize(Speed)); + finally + AJson.Free; + end; + except end; +end; + +procedure TForm1.Button4Click(Sender: TObject); +var + AJson:TQJson; + YJson:JSONObject; + II:Integer; + T1,T2:Cardinal; + Speed:Cardinal; +begin +if SaveDialog1.Execute then + begin + AJson:=TQJson.Create; + try + mmResult.Clear; + T1:=GetTickCount; + with AJson.Add('Integers',jdtObject) do + begin + for II := 0 to 2000000 do + Add('Node'+IntToStr(II)).AsInteger :=II; + end; + T1:=GetTickCount-T1; + T2:=GetTickCount; + AJson.SaveToFile(SaveDialog1.FileName,teAnsi,false); + T2:=GetTickCount-T2; + if T2>0 then + Speed:=(GetFileSize(SaveDialog1.FileName)*1000 div T2) + else + Speed:=0; + mmResult.Lines.Add('QJSON 200ʱ'+IntToStr(T1)+'ms,ʱ:'+IntToStr(T2)+'msٶȣ'+RollupSize(Speed)); + finally + AJson.Free; + end; + YJson:=JSONObject.Create; + try + mmResult2.Clear; + T1:=GetTickCount; + with YJson.AddChildObject('Integers') do + begin + for II := 0 to 2000000 do + add('Node'+IntToStr(II)).AsInteger := II; + end; + T1:=GetTickCount-T1; + T2:=GetTickCount; + YJson.SaveToFile(SaveDialog1.FileName, 4, YxdStr{$IFDEF UNICODE}.TTextEncoding{$ENDIF}.teAnsi,false); + T2:=GetTickCount-T2; + if T2>0 then + Speed:=(GetFileSize(SaveDialog1.FileName)*1000 div T2) + else + Speed:=0; + mmResult2.Lines.Add('YxdJSON 200ʱ'+IntToStr(T1)+'ms,ʱ:'+IntToStr(T2)+'msٶȣ'+RollupSize(Speed)); + finally + YJson.Free; + end; + end; +end; + +procedure TForm1.Button5Click(Sender: TObject); +var + AJson:TQJson; + yjson: JSONObject; +begin +AJson:=TQJson.Create; +try + AJson.Parse('{"results":[],"status":102,"msg":"IP\/SN\/SCODE\/REFERER Illegal:"}'); + mmResult.Text := (AJson.Encode(True)); +finally + AJson.Free; +end; +yjson:=JSONObject.Create; +try + yjson.Parse('{"results":[],"status":102,"msg":"IP\/SN\/SCODE\/REFERER Illegal:"}'); + mmResult2.Text := (yjson.ToString(4)); +finally + yjson.Free; +end; +end; + +procedure TForm1.Button6Click(Sender: TObject); +var + ARec, BRec: TRttiTestSubRecord; + AJson,ACopy:TQJson; + YJson,YCopy:JSONObject; + t: Cardinal; + I: Integer; +begin +{$IFNDEF UNICODE} + ShowMessage('ڵǰIDEв֧.'); +{$ELSE} + ARec.Int64Val:=1; + ARec.UInt64Val:=2; + ARec.UStr:='Test String'; + ARec.AStr:='AnsiString'; + ARec.SStr:='ShortString'; + ARec.IntVal:=3; + ARec.MethodVal:=Button2Click; + ARec.SetVal:=[{$IFDEF UNICODE}TBorderIcon.{$ENDIF}biSystemMenu]; + ARec.WordVal:=4; + ARec.ByteVal:=5; + ARec.ObjVal:=Button2; + ARec.DtValue:=Now; + ARec.tmValue:=Time; + ARec.dValue:=Now; + ARec.CardinalVal:=6; + ARec.ShortVal:=7; + ARec.CurrVal:=8.9; + ARec.EnumVal:={$IFDEF UNICODE}TAlign.{$ENDIF}alTop; + ARec.CharVal:='A'; + ARec.VarVal:=VarArrayOf(['VariantArray',1,2.5,true,false]); + SetLength(ARec.ArrayVal,3); + ARec.ArrayVal[0]:=100; + ARec.ArrayVal[1]:=101; + ARec.ArrayVal[2]:=102; + SetLength(ARec.IntArray,2); + ARec.IntArray[0]:=300; + ARec.IntArray[1]:=200; + BRec := ARec; + t := GetTickCount; + for i := 0 to 1000 do begin + AJson:=TQJson.Create; + try + {$IFDEF UNICODE} + AJson.Add('Record').FromRecord(ARec); + ACopy:=AJson.ItemByName('Record').Copy; + ACopy.ItemByName('Int64Val').AsInt64:=100; + ACopy.ItemByPath('UStr').AsString:='UnicodeString-ByJson'; + ACopy.ItemByPath('AStr').AsString:='AnsiString-ByJson'; + ACopy.ItemByPath('SStr').AsString:='ShortString-ByJson'; + ACopy.ItemByPath('EnumVal').AsString:='alBottom'; + ACopy.ItemByPath('SetVal').AsString:='[biHelp]'; + ACopy.ItemByPath('ArrayVal').AsJson:='[10,30,15]'; + ACopy.ItemByPath('VarVal').AsVariant:=VarArrayOf(['By Json',3,4,false,true]); + ACopy.ToRecord(ARec); + ACopy.Free; + AJson.Add('NewRecord').FromRecord(ARec); + {$ENDIF} + + mmResult.text := (AJson.AsJson); + finally + AJson.Free; + end; + end; + t := GetTickCount - t; + mmResult.Lines.add(Format('QJson %dms.', [t])); + ARec := BRec; + t := GetTickCount; + for i := 0 to 1000 do begin + YJson:=JSONObject.Create; + try + {$IFDEF UNICODE} + YJson.PutRecord('Record', ARec); + YCopy:=YJson.getItem('Record').AsJsonObject.Clone; + YCopy.getItem('Int64Val').AsInt64:=100; + YCopy.ItemByPath('UStr').AsString:='UnicodeString-ByJson'; + YCopy.ItemByPath('AStr').AsString:='AnsiString-ByJson'; + YCopy.ItemByPath('SStr').AsString:='ShortString-ByJson'; + YCopy.ItemByPath('EnumVal').AsString:='alBottom'; + YCopy.ItemByPath('SetVal').AsString:='[biHelp]'; + YCopy.ItemByPath('ArrayVal').AsJsonArray.Parse('[10,30,15]'); + YCopy.ItemByPath('VarVal').AsVariant:=VarArrayOf(['By Json',3,4,false,true]); + YCopy.ToRecord(ARec); + YCopy.Free; + YJson.PutRecord('NewRecord', ARec); + {$ENDIF} + + mmResult2.text := (YJson.ToString(4)); + finally + YJson.Free; + end; + end; + t := GetTickCount - t; + mmResult2.Lines.add(Format('YxdJson %dms.', [t])); + {$ENDIF} +end; + +procedure TForm1.Button7Click(Sender: TObject); +var + AStream:TMemoryStream; + AJson:TQJson; + S:QStringW; + AEncode:TTextEncoding; +begin +AStream:=TMemoryStream.Create; +AJson:=TQJson.Create; +try + AJson.DataType:=jdtObject; + S:='{"record1":{"id":100,"name":"name1"}}'#13#10+ + '{"record2":{"id":200,"name":"name2"}}'#13#10+ + '{"record3":{"id":300,"name":"name3"}}'#13#10; + //UCS2 + mmResult.Lines.Add('Unicode 16 LE:'); + AEncode:=teUnicode16LE; + AStream.Size:=0; + SaveTextW(AStream,S,False); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add('һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); + //UTF-8 + mmResult.Lines.Add('UTF8:'); + AEncode:=teUtf8; + AStream.Size:=0; + SaveTextU(AStream,qstring.Utf8Encode(S),False); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); + //ANSI + mmResult.Lines.Add(#13#10'ANSI:'); + AEncode:=teAnsi; + AStream.Size:=0; + SaveTextA(AStream,qstring.AnsiEncode(S)); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add('һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); + //UCS2BE + mmResult.Lines.Add(#13#10'Unicode16BE:'); + AEncode:=teUnicode16BE; + AStream.Size:=0; + SaveTextWBE(AStream,S,False); + AStream.Position:=0; + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add('һν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ڶν:'#13#10); + mmResult.Lines.Add(AJson.AsJson); + AJson.Clear; + AJson.ParseBlock(AStream,AEncode); + mmResult.Lines.Add(#13#10'ν:'); + mmResult.Lines.Add(AJson.AsJson); +finally + AStream.Free; + AJson.Free; +end; +end; + +procedure TForm1.Button8Click(Sender: TObject); +var + AJson,AItem:TQJson; + YJson, A, B: JSONObject; + YItem: JSONArray; + YItemValue: PJSONValue; + II:Integer; + DynArray:array of Integer; + RecordArray:array of TRttiUnionRecord; +begin +AJson:=TQJson.Create; +try + //ԪصNַʽʾ + // 1. ֱӵAddԪıķʽ + AJson.Add('AddArrayText','["Item1",100,null,true,false,123.4]',jdtArray);//jdtArrayʡԻԶԣȷ֪ͲҪжӿ + // 2. ֱ + AJson.Add('AddArray',['Item1',100,Null,True,False,123.4]); + // 3. ֱVarArrayOfֵ + AJson.Add('AsVariant').AsVariant:=VarArrayOf(['Item1',100,Null,True,False,123.4]); + //ڶ̬飬 + SetLength(DynArray,5); + DynArray[0]:=100; + DynArray[1]:=200; + DynArray[2]:=300; + DynArray[3]:=400; + DynArray[4]:=500; + AJson.Add('DynArray').AsVariant:=DynArray; + {$IFDEF UNICODE} + SetLength(RecordArray,2); + RecordArray[0].iVal:=1; + RecordArray[1].iVal:=2; + with AJson.Add('RecordArray',jdtArray) do + begin + for II := 0 to High(RecordArray) do + Add.FromRecord(RecordArray[II]); + end; + {$ENDIF} +// AJson.Add('RecordArray').AsVariant:=RecordArray; +// 4. ֱAsArrayļ + AJson.Add('AsArray').AsArray:='["Item1",100,null,true,false,123.4]'; + // 5. ֶԪ + with AJson.Add('Manul') do + begin + DataType:=jdtArray; + Add.AsString:='Item1'; + Add.AsInteger:=100; + Add; + Add.AsBoolean:=True; + Add.AsBoolean:=False; + Add.AsFloat:=123.4; + end; + // Ӷֻͣӽ㻻ǶͿ + AJson.Add('Object',[TQJson.Create.Add('Item1',100).Parent,TQJson.Create.Add('Item2',true).Parent]); + mmResult.Lines.Add(AJson.AsJson); + //еԪ + mmResult.Lines.Add('ʹfor inöManulԪֵ'); + II:=0; + for AItem in AJson.ItemByName('Manul') do + begin + mmResult.Lines.Add('Manul['+IntToStr(II)+']='+AItem.AsString); + Inc(II); + end; + mmResult.Lines.Add('ʹͨforѭöManulԪֵ'); + AItem:=AJson.ItemByName('Manul'); + for II := 0 to AItem.Count-1 do + mmResult.Lines.Add('Manul['+IntToStr(II)+']='+AItem[II].AsString); +finally + FreeObject(AJson); +end; +YJson:=JSONObject.Create; +try + //ԪصNַʽʾ + // 1. ֱӵAddԪıķʽ + YJson.putJSON('AddArrayText','["Item1",100,null,true,false,123.4]');//jdtArrayʡԻԶԣȷ֪ͲҪжӿ + // 2. ֱ + YJson.put('AddArray',['Item1',100,Null,True,False,123.4]); + // 3. ֱVarArrayOfֵ + YJson.put('AsVariant', VarArrayOf(['Item1',100,Null,True,False,123.4])); + //ڶ̬飬 + SetLength(DynArray,5); + DynArray[0]:=100; + DynArray[1]:=200; + DynArray[2]:=300; + DynArray[3]:=400; + DynArray[4]:=500; + YJson.put('DynArray', DynArray); + {$IFDEF UNICODE} + SetLength(RecordArray,2); + RecordArray[0].iVal:=1; + RecordArray[1].iVal:=2; + with YJson.AddChildArray('RecordArray') do + begin + for II := 0 to High(RecordArray) do + putRecord(RecordArray[II]); + end; + {$ENDIF} +// AJson.Add('RecordArray').AsVariant:=RecordArray; +// 4. ֱAsArrayļ + YJson.putJSON('AsArray', '["Item1",100,null,true,false,123.4]'); + // 5. ֶԪ + with YJson.AddChildArray('Manul') do + begin + Add('Item1'); + Add(100); + Add(NULL); + Add(True); + Add(False); + Add(123.4); + end; + // Ӷֻͣӽ㻻ǶͿ + a := JSONObject.Create; + a.Put('Item1', 100); + b := JSONObject.Create; + b.Put('Item2', True); + YJson.AddChildArray('Object',[a, b]); + mmResult2.Lines.Add(YJson.ToString(4)); + //еԪ + mmResult2.Lines.Add('ʹfor inöManulԪֵ'); + II:=0; + for YItemValue in YJson.GetJsonArray('Manul') do + begin + mmResult2.Lines.Add('Manul['+IntToStr(II)+']='+YItemValue.AsString); + Inc(II); + end; + mmResult2.Lines.Add('ʹͨforѭöManulԪֵ'); + YItem:=YJson.GetJsonArray('Manul'); + for II := 0 to YItem.Count-1 do + mmResult2.Lines.Add('Manul['+IntToStr(II)+']='+YItem[II].AsString); +finally + FreeObject(YJson); +end; +end; + +procedure TForm1.Button9Click(Sender: TObject); +const + TMPSTR = '{'+ + '"object":{'+ + ' "name":"object_1",'+ + ' "subobj":{'+ + ' "name":"subobj_1"'+ + ' },'+ + ' "subarray":[1,3,4]'+ + ' },'+ + '"array":[100,200,300,{"name":"object"}]'+ + '}'; +var + AJson,AItem:TQJson; + YJSON:JSONObject; + YItem:JSONObject; +begin +AJson:=TQJson.Create; +try + AJson.Parse(TMPSTR); + {$IFDEF UNICODE} + AItem:=AJson.CopyIf(nil,procedure(ASender,AChild:TQJson;var Accept:Boolean;ATag:Pointer) + begin + Accept:=(AChild.DataType<>jdtArray); + end); + {$ELSE} + AItem:=AJson.CopyIf(nil,DoCopyIf); + {$ENDIF} + mmResult.Lines.Add('CopyIfƳн'); + mmResult.Lines.Add(AItem.AsJson); + mmResult.Lines.Add('FindIfָĽ'); + {$IFDEF UNICODE} + mmResult.Lines.Add(AItem.FindIf(nil,true,procedure(ASender,AChild:TQJson;var Accept:Boolean;ATag:Pointer) + begin + Accept:=(AChild.Name='subobj'); + end).AsJson); + {$ELSE} + mmResult.Lines.Add(AItem.FindIf(nil,true,DoFindIf).AsJson); + {$ENDIF} + mmResult.Lines.Add('ɾеsubobj'); + {$IFDEF UNICODE} + AItem.DeleteIf(nil,true,procedure(ASender,AChild:TQJson;var Accept:Boolean;ATag:Pointer) + begin + Accept:=(AChild.Name='subobj'); + end); + {$ELSE} + AItem.DeleteIf(nil,true,DoDeleteIf); + {$ENDIF} + mmResult.Lines.Add(AItem.AsJson); +finally + FreeObject(AItem); + FreeObject(AJson); +end; + +YJson:=JSONObject.Create; +try + YJson.Parse(TMPSTR); + {$IFDEF UNICODE} + YItem:=YJson.CopyIf(nil,procedure(ASender: JSONBase; AChild: PJSONValue;var Accept:Boolean;ATag:Pointer) + begin + Accept:=not Assigned(AChild.AsJsonArray); + end) as JSONObject; + {$ELSE} + YItem:=YJson.CopyIf(nil,DoCopyIfY) as JSONObject; + {$ENDIF} + mmResult2.Lines.Add('CopyIfƳн'); + mmResult2.Lines.Add(YItem.ToString(4)); + mmResult2.Lines.Add('FindIfָĽ'); + {$IFDEF UNICODE} + mmResult2.Lines.Add(YItem.FindIf(nil,true,procedure(ASender: JSONBase; AChild: PJSONValue;var Accept:Boolean;ATag:Pointer) + begin + Accept:=(AChild.FName='subobj'); + end).ToString(4)); + {$ELSE} + mmResult2.Lines.Add(YItem.FindIf(nil,true,DoFindIfY).ToString(4)); + {$ENDIF} + mmResult2.Lines.Add('ɾеsubobj'); + {$IFDEF UNICODE} + YItem.DeleteIf(nil,true,procedure(ASender: JSONBase; AChild: PJSONValue;var Accept:Boolean;ATag:Pointer) + begin + Accept:=(AChild.FName='subobj'); + end); + {$ELSE} + YItem.DeleteIf(nil,true,DoDeleteIfY); + {$ENDIF} + mmResult2.Lines.Add(YItem.ToString(4)); +finally + FreeObject(YItem); + FreeObject(YJson); +end; + +end; + +procedure TForm1.DoCopyIf(ASender, AItem: TQJson; var Accept: Boolean; + ATag: Pointer); +begin +Accept:=(AItem.DataType<>jdtArray); +end; +procedure TForm1.DoCopyIfY(ASender: JSONBase; AItem: PJSONValue; + var Accept: Boolean; ATag: Pointer); +begin +Accept:=not Assigned(AItem.AsJsonArray); +end; + +procedure TForm1.DoDeleteIf(ASender,AChild:TQJson;var Accept:Boolean;ATag:Pointer); +begin +Accept:=(AChild.Name='subobj'); +end; + +procedure TForm1.DoDeleteIfY(ASender: JSONBase; AChild: PJSONValue; + var Accept: Boolean; ATag: Pointer); +begin +Accept:=(AChild.FName='subobj'); +end; + +procedure TForm1.DoFindIf(ASender, AChild: TQJson; var Accept: Boolean; + ATag: Pointer); +begin +Accept:=(AChild.Name='subobj'); +end; + +procedure TForm1.DoFindIfY(ASender: JSONBase; AChild: PJSONValue; + var Accept: Boolean; ATag: Pointer); +begin +Accept:=(AChild.FName='subobj'); +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin +ReportMemoryLeaksOnShutdown:=True; +OpenDialog1.InitialDir := ExtractFilePath(ParamStr(0)); +end; + +procedure TForm1.Panel1Click(Sender: TObject); +var + S:QStringA; +begin +S:='Hello,world'; +ShowMessage(S); +end; + +end. diff --git a/demo/YxdJson/YxdJson_VS_QJson/qdac.inc b/demo/YxdJson/YxdJson_VS_QJson/qdac.inc new file mode 100644 index 0000000..2818a7f --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/qdac.inc @@ -0,0 +1,10 @@ +{$DEFINE QDAC} + +{$IF RTLVersion<18} +{$MESSAGE Error '!!!QDAC Only test in 2007 and XE6,No support in other version!!!'} +{$IFEND =24} +{$LEGACYIFEND ON} +{$IFEND} + + diff --git a/demo/clean.bat b/demo/clean.bat new file mode 100644 index 0000000..eb7d086 --- /dev/null +++ b/demo/clean.bat @@ -0,0 +1,21 @@ +cd Source +del *.dcu;*.zip;*.7z;*.hpp;*.identcache;*.local /q /s +del __history\*.* /q /s +rd __history + +cd .. +cd Qdac +del *.dcu;*.zip;*.7z;*.hpp;*.identcache;*.local /q /s +del __history\*.* /q /s +rd __history + +cd .. +cd dcu +del *.dcu /q /s +del __history\*.* /q /s + +cd ..\Demo +del *.exe /q/s + +cmdex cleanup(".\") +pause \ No newline at end of file diff --git a/qdac/QString.pas b/qdac/QString.pas new file mode 100644 index 0000000..3922be7 --- /dev/null +++ b/qdac/QString.pas @@ -0,0 +1,8482 @@ +unit qstring; +{$I 'qdac.inc'} + +interface + +{ + ԴQDACĿȨswish(QQ:109867294)С + (1)ʹɼ + ɸơַ޸ıԴ룬޸Ӧ÷ߣڱҪʱ + ϲĿԹʹãϲԴͬѭQDACȨơ + IJƷĹУӦµİ汾: + ƷʹõJSONQDACĿеQJSONȨС + (2)֧ + м⣬ԼQDACٷQQȺ250530692ͬ̽֡ + (3) + ʹñԴҪ֧κηáñԴа + Ŀǿƣʹ߲ΪȣиľΪָõƷ + ʽ + ֧ guansonghuan@sina.com + У + + ˺ţ4367 4209 4324 0179 731 + Угŷ索 +} + +{ ޶־ + 2015.7.15 + ========= + * qsl ĹϣԽ HashOf 㷨Ϊ BKDR Windowsƽ̨ʹû + Чʣлqsl + 2015.6.6 + ========= + * Utf8Decode ʱ0x10000+Χַʱλ󣨸лqsl + 2015.5.29 + ========= + * TQPagedStream ڵͰ汾IDE޷ + + 2015.5.25 + ========= + + TQPagedStream 룬дڴʱTMemoryStream + + 2015.5.22 + ========= + * ParseNumeric ParseInt ʱʾΧֵʽ + 2015.5.21 + ========= + * StringReplaceWithW AWithTag Ϊ True ʱȷ⣨ٷʱ棩 + * StringReplaceWithW 滻ĽַΪʱЧַ⣨ٷʱ棩 + 2015.5.20 + ========= + + StringReplaceW һأ滻ַеijһΪضַһڲݵ + * ƳһHint + 2015.5.8 + ========= + * ޸ TQPtr ʵ֣ͷ¼ͨ¼ʹͬһ + + DecodeText ڴֱӼ벢Unicodeַ + + 2015.4.17 + ========= + * ŻUTFEncodeռڴ + 2015.4.8 + ========= + * ParseNumeric ڽ -0.xx ַų⣨лYZ棩 + 2015.4.1 + ========= + * TQStringCatHelperҪĻʱжϴ⣨лetarelecca棩 + 2015.3.9 + ========= + * ޸ NaturalCompareW ǷԵհַѡںʱA 10 A10 ΪһµĽ + 2015.3.5 + ========= + + PosWȼϵͳPos ͰȻ NaturalCompareW + 2015.2.9 + ========= + + FilterCharW FilterNoNumberW + + 2015.1.27 + ========= + * TQPtr.Bind ڴûб + + 2015.1.26 + ========== + + TQPtr Bind + + ȫֱ IsFMXApp ⵱ǰǷ FMX Ӧó + + 2014.11.10 + ========= + * XE3ʱTSystemTimesЧ + + 2014.11.5 + ========= + * QStringAFrom޸ķֵͲһ + + QStringACat + + CharCodeA/CharCodeU/CharCodeWָλõֵַ + + 2014.9.26 + ========= + * TThreadIdͶQWorker뱾Ԫ + 2014.9.11 + ========= + * LoadTextA/LoadTextWشBOMͷĿյUtf8ʱ + 2014.8.20 + ========= + + StringReplaceWithW滻һǩеݣң + 2014.8.15 + ========= + * ֤TQBytesCatHelper2007޷ͨ(沢֤) + + PQStringAͶ + + 2014.8.14 + ========= + * TQBytesCatHelper.NeedSizeDelphi2007޷(Сױ沢ṩ޸) + 2014.8.5 + ======== + * BinToHexALowerCase֧ʹСдʮƱʾʽ + 2014.8.1 + ========= + + SameCharsA/U/WַͬEndWithA/U/WжǷַָβ + 2014.7.17 + ========= + + BinaryCmpڵȼCеmemcmp + 2014.7.16 + ========= + + MemScanָڴвָֽ + 2014.7.12 + ========= + * DecodeLineUеݹԼĴ(Сױ) + * CharCountUַʱ˫ֽUtf8ļ + 2014.7.10 + ========= + + ºStringReplicateW,NameOfW,ValueOfW,IndexOfNameW,IndexOfValueW + + 2014.6.26 + ========= + * HPPEMITĬӱԪ(ٷ ) + 2014.6.21 + ========== + * C++ Builderб + 2014.6.19 + ========== + * QuotedStrڳΪ0ַ +} +uses classes, sysutils, types{$IF RTLVersion>=21}, + Rtti{$IFEND >=XE10}{$IFNDEF MSWINDOWS}, + syncobjs{$ENDIF} +{$IFDEF POSIX} + , Posix.String_ +{$ENDIF} + ; +{$HPPEMIT '#pragma link "qstring"'} + +type +{$IFDEF UNICODE} + QStringW = UnicodeString; +{$ELSE} + QStringW = WideString; +{$ENDIF UNICODE} +{$IF RTLVersion>=21} + TValueArray = array of TValue; +{$IFEND >=2010} +{$IF RTLVersion<25} + IntPtr = Integer; + IntUPtr = Cardinal; +{$IFEND IntPtr} +{$IF RTLVersion<18.5} + DWORD_PTR = DWORD; + ULONGLONG = Int64; +{$IFEND} +{$IF RTLVersion<22} + TThreadId = Longword; +{$IFEND} + PIntPtr = ^IntPtr; + QCharA = Byte; + QCharW = WideChar; + PQCharA = ^QCharA; + PPQCharA = ^PQCharA; + PQStringA = ^QStringA; + PQCharW = PWideChar; + PPQCharW = ^PQCharW; + PQStringW = ^QStringW; + TTextEncoding = (teUnknown, { δ֪ı } + teAuto, { Զ } + teAnsi, { Ansi } + teUnicode16LE, { Unicode LE } + teUnicode16BE, { Unicode BE } + teUTF8 { UTF8 } + ); +{$HPPEMIT '#define DELPHI_ANON(AType,Code,AVar) \'} +{$HPPEMIT ' class AType##AVar:public TCppInterfacedObject\'} +(*$HPPEMIT ' {\'*) +{$HPPEMIT ' public:\'} +{$HPPEMIT ' void __fastcall Invoke##Code\'} +(*$HPPEMIT ' } *AVar=new AType##AVar'*) + + // AβΪAnsiֵ֧ĺUβUtf8ֵ֧ĺWβΪUCS2 + QStringA = record + private + FValue: TBytes; + function GetChars(AIndex: Integer): QCharA; + procedure SetChars(AIndex: Integer; const Value: QCharA); + function GetLength: Integer; + procedure SetLength(const Value: Integer); + function GetIsUtf8: Boolean; + function GetData: PByte; + public + class operator Implicit(const S: QStringW): QStringA; + class operator Implicit(const S: QStringA): Pointer; + class operator Implicit(const S: QStringA): TBytes; + class operator Implicit(const ABytes: TBytes): QStringA; + class operator Implicit(const S: QStringA): QStringW; + class operator Implicit(const S: PQCharA): QStringA; +{$IFNDEF NEXTGEN} + class operator Implicit(const S: AnsiString): QStringA; + class operator Implicit(const S: QStringA): AnsiString; +{$ENDIF} + // ַȽ + function From(p: PQCharA; AOffset, ALen: Integer): PQStringA; overload; + function From(const S: QStringA; AOffset: Integer = 0): PQStringA; overload; + function Cat(p: PQCharA; ALen: Integer): PQStringA; overload; + function Cat(const S: QStringA): PQStringA; overload; + property Chars[AIndex: Integer]: QCharA read GetChars + write SetChars; default; + property Length: Integer read GetLength write SetLength; + property IsUtf8: Boolean read GetIsUtf8; + property Data: PByte read GetData; + end; + + QException = class(Exception) + + end; + + // ַƴ + TQStringCatHelperW = class + private + FValue: array of QCharW; + FStart, FDest: PQCharW; + FBlockSize: Integer; +{$IFDEF DEBUG} + FAllocTimes: Integer; +{$ENDIF} + FSize: Integer; + function GetValue: QStringW; + function GetPosition: Integer; inline; + procedure SetPosition(const Value: Integer); + procedure NeedSize(ASize: Integer); + function GetChars(AIndex: Integer): QCharW; + public + constructor Create; overload; + constructor Create(ASize: Integer); overload; + procedure LoadFromFile(const AFileName: QStringW); + procedure LoadFromStream(const AStream: TStream); + function Cat(p: PQCharW; len: Integer): TQStringCatHelperW; overload; + function Cat(const S: QStringW): TQStringCatHelperW; overload; + function Cat(c: QCharW): TQStringCatHelperW; overload; + function Cat(const V: Int64): TQStringCatHelperW; overload; + function Cat(const V: Double): TQStringCatHelperW; overload; + function Cat(const V: Boolean): TQStringCatHelperW; overload; + function Cat(const V: Currency): TQStringCatHelperW; overload; + function Cat(const V: TGuid): TQStringCatHelperW; overload; + function Cat(const V: Variant): TQStringCatHelperW; overload; + function Replicate(const S: QStringW; count: Integer): TQStringCatHelperW; + function Back(ALen: Integer): TQStringCatHelperW; + function BackIf(const S: PQCharW): TQStringCatHelperW; + procedure TrimRight; + procedure Reset; + property Value: QStringW read GetValue; + property Chars[AIndex: Integer]: QCharW read GetChars; + property Start: PQCharW read FStart; + property Current: PQCharW read FDest; + property Position: Integer read GetPosition write SetPosition; + end; + + TQBytesCatHelper = class + private + FValue: TBytes; + FStart, FDest: PByte; + FBlockSize: Integer; + FSize: Integer; + function GetBytes(AIndex: Integer): Byte; + function GetPosition: Integer; + procedure SetPosition(const Value: Integer); + procedure NeedSize(ASize: Integer); + procedure SetCapacity(const Value: Integer); + public + constructor Create; overload; + constructor Create(ASize: Integer); overload; + function Cat(const V: Byte): TQBytesCatHelper; overload; + function Cat(const V: Shortint): TQBytesCatHelper; overload; + function Cat(const V: Word): TQBytesCatHelper; overload; + function Cat(const V: Smallint): TQBytesCatHelper; overload; + function Cat(const V: Cardinal): TQBytesCatHelper; overload; + function Cat(const V: Integer): TQBytesCatHelper; overload; + function Cat(const V: Int64): TQBytesCatHelper; overload; +{$IFNDEF NEXTGEN} + function Cat(const V: AnsiChar): TQBytesCatHelper; overload; + function Cat(const V: AnsiString): TQBytesCatHelper; overload; +{$ENDIF} + function Cat(const V: QStringA; ACStyle: Boolean = False) + : TQBytesCatHelper; overload; + function Cat(const c: QCharW): TQBytesCatHelper; overload; + function Cat(const S: QStringW): TQBytesCatHelper; overload; + function Cat(const ABytes: TBytes): TQBytesCatHelper; overload; + function Cat(const AData: Pointer; const ALen: Integer) + : TQBytesCatHelper; overload; + function Cat(const V: Single): TQBytesCatHelper; overload; + function Cat(const V: Double): TQBytesCatHelper; overload; + function Cat(const V: Boolean): TQBytesCatHelper; overload; + function Cat(const V: Currency): TQBytesCatHelper; overload; + function Cat(const V: TGuid): TQBytesCatHelper; overload; + function Cat(const V: Variant): TQBytesCatHelper; overload; + function Replicate(const ABytes: TBytes; ACount: Integer): TQBytesCatHelper; + function Back(ALen: Integer): TQBytesCatHelper; + procedure Reset; + property Value: TBytes read FValue; + property Bytes[AIndex: Integer]: Byte read GetBytes; + property Start: PByte read FStart; + property Current: PByte read FDest; + property Position: Integer read GetPosition write SetPosition; + property Capacity: Integer read FSize write SetCapacity; + end; + + IQPtr = interface(IInterface) + function Get: Pointer; + end; + + TQPtrFreeEvent = procedure(AData: Pointer) of object; + PQPtrFreeEvent = ^TQPtrFreeEvent; + TQPtrFreeEventG = procedure(AData: Pointer); +{$IFDEF UNICODE} + TQPtrFreeEventA = reference to procedure(AData: Pointer); +{$ENDIF} + + TQPtrFreeEvents = record + case Integer of + 0: + (Method: TMethod); + 1: + (OnFree: {$IFNDEF NEXTGEN}TQPtrFreeEvent{$ELSE}Pointer{$ENDIF}); + 2: + (OnFreeG: TQPtrFreeEventG); + 3: + (OnFreeA: Pointer); + end; + + TQPtr = class(TInterfacedObject, IQPtr) + private + FObject: Pointer; + FOnFree: TQPtrFreeEvents; + public + constructor Create(AObject: Pointer); overload; + destructor Destroy; override; + class function Bind(AObject: TObject): IQPtr; overload; + class function Bind(AData: Pointer; AOnFree: TQPtrFreeEvent) + : IQPtr; overload; + class function Bind(AData: Pointer; AOnFree: TQPtrFreeEventG) + : IQPtr; overload; +{$IFDEF UNICODE} + class function Bind(AData: Pointer; AOnFree: TQPtrFreeEventA) + : IQPtr; overload; +{$ENDIF} + function Get: Pointer; + end; +{$IF RTLVersion<=23} + + TDirection = (FromBeginning, FromEnd); + TPointerList = array of Pointer; +{$ELSE} + // TDirection = System.Types.TDirection; +{$IFEND} + + // TQPagedList - ҳʽбڼ¼б + TQListPage = class + protected + FStartIndex: Integer; // ʼ + FUsedCount: Integer; // ʹõҳ + FItems: array of Pointer; + public + constructor Create(APageSize: Integer); + end; + + TQPagedListSortCompare = procedure(p1, p2: Pointer; var AResult: Integer) + of object; + TQPagedListSortCompareG = procedure(p1, p2: Pointer; var AResult: Integer); +{$IFDEF UNICODE} + TQPagedListSortCompareA = reference to procedure(p1, p2: Pointer; + var AResult: Integer); +{$ENDIF} + TQPagedList = class; + + TQPagedListEnumerator = class + private + FIndex: Integer; + FList: TQPagedList; + public + constructor Create(AList: TQPagedList); + function GetCurrent: Pointer; inline; + function MoveNext: Boolean; + property Current: Pointer read GetCurrent; + end; + + TQPagedList = class + private + procedure InternalInsert(AIndex: Integer; const p: Pointer); + protected + FPages: array of TQListPage; // ҳб + FPageSize: Integer; // ÿҳС + FCount: Integer; // + FLastUsedPage: Integer; + FFirstDirtyPage: Integer; // ׸ҳ + FOnCompare: TQPagedListSortCompare; // ݱȽϺ + function DoCompare(p1, p2: Pointer): Integer; inline; + function GetItems(AIndex: Integer): Pointer; + procedure SetItems(AIndex: Integer; const Value: Pointer); + procedure DoDelete(const p: Pointer); + function FindPage(AIndex: Integer): Integer; + procedure Dirty(APage: Integer); inline; + procedure Notify(Ptr: Pointer; Action: TListNotification); virtual; + function GetCapacity: Integer; + procedure SetCapacity(const Value: Integer); + function GetList: TPointerList; + procedure SetOnCompare(const Value: TQPagedListSortCompare); + procedure CheckLastPage; + public + ///

+ /// ĬϹ캯δָҳСʹĬϴС4096 + /// + constructor Create; overload; + /// + /// ÿҳСڵ0ʹĬֵ + constructor Create(APageSize: Integer); overload; + /// + destructor Destroy; override; +{$IF RTLVersion<26} + /// οTList.Assign + procedure Assign(ListA: TList; AOperator: TListAssignOp = laCopy; + ListB: TList = nil); overload; +{$IFEND} + /// οTList.Assign + procedure Assign(ListA: TQPagedList; AOperator: TListAssignOp = laCopy; + ListB: TQPagedList = nil); overload; + /// һԪ + /// ҪӵԪ + /// Ԫصֵ + function Add(const p: Pointer): Integer; + /// ָλòһԪ + /// ҪλãСڵ0ʼλãڵCountĩβ + /// ҪԪ + /// ָAIndex + procedure Insert(AIndex: Integer; const p: Pointer); + /// Ԫصλ + /// һԪصλ + /// ڶԪصλ + procedure Exchange(AIndex1, AIndex2: Integer); + /// ָλõԪƶλ + /// ʼλ + /// Ŀλ + procedure MoveTo(AFrom, ATo: Integer); + /// ʵֱӵMoveTo + procedure Move(AFrom, ATo: Integer); inline; + /// ɾָԪ + procedure Delete(AIndex: Integer); + /// ƳָԪ + procedure Remove(AIndex: Integer); overload; + /// ƳָԪ + function Remove(Item: Pointer): Integer; overload; inline; + /// ָķʼҲƳԪ + function RemoveItem(Item: Pointer; Direction: TDirection): Integer; + /// ָԪص + /// ҪҵԪ + /// ҵԪ + /// ҵTrue,򷵻FalseAIdxΪĿӦֵλ + function Find(const p: Pointer; var AIdx: Integer): Boolean; + /// Ԫ + procedure Clear; + /// бΪٵҳ + procedure Pack; + /// OnCompare涨Ĺ + /// һָOnCompareԪʱԶInsertʱָλýᱻ + procedure Sort; overload; +{$IFDEF UNICODE} + /// AOnCompare涨Ĺ + /// һָOnCompareԪʱԶInsertʱָλýᱻ + procedure Sort(AOnCompare: TQPagedListSortCompareA); overload; +{$ENDIF} + /// AOnCompare涨Ĺ + /// һָOnCompareԪʱԶInsertʱָλýᱻ + procedure Sort(AOnCompare: TQPagedListSortCompareG); overload; + /// for .. in ֧ + function GetEnumerator: TQPagedListEnumerator; + /// Ϊ TList ӣ TQPagedList + function Expand: TQPagedList; + /// ƳָĿ + function Extract(Item: Pointer): Pointer; inline; + /// ƳָĿ + function ExtractItem(Item: Pointer; Direction: TDirection): Pointer; + /// ׸Ԫ + function First: Pointer; inline; + /// һԪ + function Last: Pointer; inline; + /// ȡָԪص״γλ + function IndexOf(Item: Pointer): Integer; + /// ָķԪ״γֵλ + function IndexOfItem(Item: Pointer; Direction: TDirection): Integer; + /// Ԫظ + property count: Integer read FCount; + /// Ԫб + property Items[AIndex: Integer]: Pointer read GetItems + write SetItems; default; + /// ԪرȽϹָԪԶ + property OnCompare: TQPagedListSortCompare read FOnCompare + write SetOnCompare; + /// TListã TQPagedList + property Capacity: Integer read GetCapacity write SetCapacity; + /// ȡеԪֵ + property List: TPointerList read GetList; + end; + + TQPagedStream = class(TStream) + private + procedure SetCapacity(Value: Int64); + function GetBytes(AIndex: Int64): Byte; + procedure SetBytes(AIndex: Int64; const Value: Byte); + procedure SetAsBytes(const Value: TBytes); + function GetAsBytes: TBytes; + protected + FPages: array of PByte; + FPageSize: Integer; + FSize: Int64; + FPosition: Int64; + FCapacity: Int64; + function ActivePage: Integer; inline; + function ActiveOffset: Integer; inline; + procedure PageNeeded(APageIndex: Integer); + function GetSize: Int64; override; + public + constructor Create; overload; + constructor Create(APageSize: Integer); overload; + destructor Destroy; override; + procedure Clear; + function Read(var Buffer; count: Longint): Longint; overload; override; + function Read(Buffer: TBytes; Offset, count: Longint): Longint; +{$IF RTLVersion>23} override{$ELSE}reintroduce; + overload{$IFEND}; + function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override; + procedure SaveToStream(Stream: TStream); virtual; + procedure SaveToFile(const FileName: string); + procedure LoadFromStream(Stream: TStream); + procedure LoadFromFile(const FileName: string); + procedure SetSize(const NewSize: Int64); override; + procedure SetSize(NewSize: Longint); override; + function Write(const Buffer; count: Longint): Longint; overload; override; + function Write(const Buffer: TBytes; Offset, count: Longint): Longint; +{$IF RTLVersion>23} override{$ELSE}reintroduce; + overload{$IFEND}; + property Capacity: Int64 read FCapacity write SetCapacity; + property Bytes[AIndex: Int64]: Byte read GetBytes write SetBytes; + property AsBytes: TBytes read GetAsBytes write SetAsBytes; + end; + + TQBits = record + private + FBits: TBytes; + function GetSize: Integer; + procedure SetSize(const Value: Integer); + function GetIsSet(AIndex: Integer): Boolean; + procedure SetIsSet(AIndex: Integer; const Value: Boolean); + public + property Size: Integer read GetSize write SetSize; + property IsSet[AIndex: Integer]: Boolean read GetIsSet + write SetIsSet; default; + property Bytes: TBytes read FBits write FBits; + end; + + TQFilterCharEvent = procedure(const AChar, AIndex: Cardinal; + var Accept: Boolean; ATag: Pointer) of object; +{$IFDEF UNICODE} + TQFilterCharEventA = reference to procedure(const c, AIndex: Cardinal; + var Accept: Boolean; ATag: Pointer); +{$ENDIF} + TQNumberType = (nftFloat, nftHexPrec, nftDelphiHex, nftCHex, nftBasicHex, + nftNegative, nftPositive); + TQNumberTypes = set of TQNumberType; + + TPasswordStrongLevel = (pslLowest, pslLower, pslNormal, pslHigher, + pslHighest); + TPasswordRule = (prIncNumber, prIncLowerCase, prIncUpperCase, prIncChart, + prIncUnicode, prRepeat, prSimpleOrder); + TPasswordRules = set of TPasswordRule; + + // UTF8UnicodeתʹԼʵ +function Utf8Decode(p: PQCharA; l: Integer): QStringW; overload; +function Utf8Decode(const p: QStringA): QStringW; overload; +function Utf8Encode(p: PQCharW; l: Integer): QStringA; overload; +function Utf8Encode(const p: QStringW): QStringA; overload; +// AnsiUnicodeתʹϵͳTEncodingʵ +function AnsiEncode(p: PQCharW; l: Integer): QStringA; overload; +function AnsiEncode(const p: QStringW): QStringA; overload; +function AnsiDecode(p: PQCharA; l: Integer): QStringW; overload; +function AnsiDecode(const p: QStringA): QStringW; overload; + +function CNSpellChars(S: QStringA; AIgnoreEnChars: Boolean): QStringW; overload; +function CNSpellChars(S: QStringW; AIgnoreEnChars: Boolean): QStringW; overload; + +// 㵱ǰַij +function CharSizeA(c: PQCharA): Integer; +function CharSizeU(c: PQCharA): Integer; +function CharSizeW(c: PQCharW): Integer; +// ַCharCountWд԰UCS2չַ +function CharCountA(const source: QStringA): Integer; +function CharCountW(const S: QStringW): Integer; +function CharCountU(const source: QStringA): Integer; +function CharCodeA(c: PQCharA): Cardinal; +function CharCodeU(c: PQCharA): Cardinal; +function CharCodeW(c: PQCharW): Cardinal; +// ַǷָб +function CharInA(const c: PQCharA; const List: array of QCharA; + ACharLen: PInteger = nil): Boolean; +function CharInW(const c: PQCharW; const List: array of QCharW; + ACharLen: PInteger = nil): Boolean; overload; +function CharInW(const c, List: PQCharW; ACharLen: PInteger = nil) + : Boolean; overload; +function CharInU(const c: PQCharA; const List: array of QCharA; + ACharLen: PInteger = nil): Boolean; + +// Ƿǿհַ +function IsSpaceA(const c: PQCharA; ASpaceSize: PInteger = nil): Boolean; +function IsSpaceW(const c: PQCharW; ASpaceSize: PInteger = nil): Boolean; +function IsSpaceU(const c: PQCharA; ASpaceSize: PInteger = nil): Boolean; + +// ȫǰת +function CNFullToHalf(const S: QStringW): QStringW; +function CNHalfToFull(const S: QStringW): QStringW; + +// Ŵ +function QuotedStrA(const S: QStringA; const AQuoter: QCharA = $27): QStringA; +function QuotedStrW(const S: QStringW; const AQuoter: QCharW = #$27): QStringW; +function SQLQuoted(const S: QStringW): QStringW; +function DequotedStrA(const S: QStringA; const AQuoter: QCharA = $27): QStringA; +function DequotedStrW(const S: QStringW; const AQuoter: QCharW = #$27) + : QStringW; + +// беַ +function SkipCharA(var p: PQCharA; const List: array of QCharA): Integer; +function SkipCharU(var p: PQCharA; const List: array of QCharA): Integer; +function SkipCharW(var p: PQCharW; const List: array of QCharA) + : Integer; overload; +function SkipCharW(var p: PQCharW; const List: PQCharW): Integer; overload; + +// հַ Ansi룬#9#10#13#161#161UCS룬#9#10#13#$3000 +function SkipSpaceA(var p: PQCharA): Integer; +function SkipSpaceU(var p: PQCharA): Integer; +function SkipSpaceW(var p: PQCharW): Integer; +// һ,#10Ϊнβ +function SkipLineA(var p: PQCharA): Integer; +function SkipLineU(var p: PQCharA): Integer; +function SkipLineW(var p: PQCharW): Integer; +// ֱַָ +function SkipUntilA(var p: PQCharA; AExpects: array of QCharA; + AQuoter: QCharA = 0): Integer; +function SkipUntilU(var p: PQCharA; AExpects: array of QCharA; + AQuoter: QCharA = 0): Integer; +function SkipUntilW(var p: PQCharW; AExpects: array of QCharW; + AQuoter: QCharW = #0): Integer; overload; +function SkipUntilW(var p: PQCharW; AExpects: PQCharW; AQuoter: QCharW = #0) + : Integer; overload; +// ַкţеʼַ +function StrPosA(Start, Current: PQCharA; var ACol, ARow: Integer): PQCharA; +function StrPosU(Start, Current: PQCharA; var ACol, ARow: Integer): PQCharA; +function StrPosW(Start, Current: PQCharW; var ACol, ARow: Integer): PQCharW; + +// ַֽ +function DecodeTokenA(var p: PQCharA; ADelimiters: array of QCharA; + AQuoter: QCharA; AIgnoreSpace: Boolean): QStringA; +function DecodeTokenU(var p: PQCharA; ADelimiters: array of QCharA; + AQuoter: QCharA; AIgnoreSpace: Boolean): QStringA; +function DecodeTokenW(var p: PQCharW; ADelimiters: array of QCharW; + AQuoter: QCharW; AIgnoreSpace: Boolean; ASkipDelimiters: Boolean = True) + : QStringW; overload; +function DecodeTokenW(var p: PQCharW; ADelimiters: PQCharW; AQuoter: QCharW; + AIgnoreSpace: Boolean; ASkipDelimiters: Boolean = True): QStringW; overload; +function DecodeTokenW(var S: QStringW; ADelimiters: PQCharW; AQuoter: QCharW; + AIgnoreCase, ARemove: Boolean; ASkipDelimiters: Boolean = True) + : QStringW; overload; +function SplitTokenW(AList: TStrings; p: PQCharW; ADelimiters: PQCharW; + AQuoter: QCharW; AIgnoreSpace: Boolean): Integer; overload; +function SplitTokenW(AList: TStrings; const S: QStringW; ADelimiters: PQCharW; + AQuoter: QCharW; AIgnoreSpace: Boolean): Integer; overload; + +function LeftStrW(const S: QStringW; AMaxCount: Integer; ACheckExt: Boolean) + : QStringW; +function RightStrW(const S: QStringW; AMaxCount: Integer; ACheckExt: Boolean) + : QStringW; +function StrBetween(var S: PQCharW; AStartTag, AEndTag: QStringW; + AIgnoreCase: Boolean): QStringW; +function TokenWithIndex(var S: PQCharW; AIndex: Integer; ADelimiters: PQCharW; + AQuoter: QCharW; AIgnoreSapce: Boolean): QStringW; +function UpperFirstW(const S: QStringW): QStringW; +// ȡһ +function DecodeLineA(var p: PQCharA; ASkipEmpty: Boolean = True): QStringA; +function DecodeLineU(var p: PQCharA; ASkipEmpty: Boolean = True): QStringA; +function DecodeLineW(var p: PQCharW; ASkipEmpty: Boolean = True): QStringW; + +// жǷַָʼ +function CharUpperA(c: QCharA): QCharA; +function CharUpperW(c: QCharW): QCharW; +function CharLowerA(c: QCharA): QCharA; +function CharLowerW(c: QCharW): QCharW; +function StartWithA(S, startby: PQCharA; AIgnoreCase: Boolean): Boolean; +function StartWithU(S, startby: PQCharA; AIgnoreCase: Boolean): Boolean; +function StartWithW(S, startby: PQCharW; AIgnoreCase: Boolean): Boolean; +function EndWithA(const S, endby: QStringA; AIgnoreCase: Boolean): Boolean; +function EndWithU(const S, endby: QStringA; AIgnoreCase: Boolean): Boolean; +function EndWithW(const S, endby: QStringW; AIgnoreCase: Boolean): Boolean; +function SameCharsA(s1, s2: PQCharA; AIgnoreCase: Boolean): Integer; +function SameCharsU(s1, s2: PQCharA; AIgnoreCase: Boolean): Integer; +function SameCharsW(s1, s2: PQCharW; AIgnoreCase: Boolean): Integer; +// ı +function LoadTextA(AFileName: String; AEncoding: TTextEncoding = teUnknown) + : QStringA; overload; +function LoadTextA(AStream: TStream; AEncoding: TTextEncoding = teUnknown) + : QStringA; overload; +function LoadTextU(AFileName: String; AEncoding: TTextEncoding = teUnknown) + : QStringA; overload; +function LoadTextU(AStream: TStream; AEncoding: TTextEncoding = teUnknown) + : QStringA; overload; +function LoadTextW(AFileName: String; AEncoding: TTextEncoding = teUnknown) + : QStringW; overload; +function LoadTextW(AStream: TStream; AEncoding: TTextEncoding = teUnknown) + : QStringW; overload; +// ı벢ıݣעûBOMıļⲻ100%ûBOM +// ʱUnicodeAnsiַ +function DecodeText(p: Pointer; ASize: Integer; + AEncoding: TTextEncoding = teUnknown): QStringW; +// ı +procedure SaveTextA(AFileName: String; const S: QStringA); overload; +procedure SaveTextA(AStream: TStream; const S: QStringA); overload; +procedure SaveTextU(AFileName: String; const S: QStringA; + AWriteBom: Boolean = True); overload; +procedure SaveTextU(AFileName: String; const S: QStringW; + AWriteBom: Boolean = True); overload; +procedure SaveTextU(AStream: TStream; const S: QStringA; + AWriteBom: Boolean = True); overload; +procedure SaveTextU(AStream: TStream; const S: QStringW; + AWriteBom: Boolean = True); overload; +procedure SaveTextW(AFileName: String; const S: QStringW; + AWriteBom: Boolean = True); overload; +procedure SaveTextW(AStream: TStream; const S: QStringW; + AWriteBom: Boolean = True); overload; +procedure SaveTextWBE(AStream: TStream; const S: QStringW; + AWriteBom: Boolean = True); overload; + +function StrStrA(s1, s2: PQCharA): PQCharA; +function StrIStrA(s1, s2: PQCharA): PQCharA; +function StrStrU(s1, s2: PQCharA): PQCharA; +function StrIStrU(s1, s2: PQCharA): PQCharA; +function StrStrW(s1, s2: PQCharW): PQCharW; +function StrIStrW(s1, s2: PQCharW): PQCharW; +function StrLikeW(S, pat: PQCharW; AIgnoreCase: Boolean): Boolean; overload; +/// Posǿ汾ʵ +/// Ҫҵַ +/// ҵԭַ +/// ǷԴСд +/// ʼλãһַλΪ1 +/// ҵӴʼλãʧܣ0 +function PosW(sub, S: PQCharW; AIgnoreCase: Boolean; AStartPos: Integer = 1) + : Integer; overload; +/// Ҫҵַ +/// ҵԭַ +/// ǷԴСд +/// ʼλãһַλΪ1 +/// ҵӴʼλãʧܣ0 +function PosW(sub, S: QStringW; AIgnoreCase: Boolean; AStartPos: Integer = 1) + : Integer; overload; +function StrDupX(const S: PQCharW; ACount: Integer): QStringW; +function StrDupW(const S: PQCharW; AOffset: Integer; + const ACount: Integer = MaxInt): QStringW; +procedure StrCpyW(d: PQCharW; S: PQCharW; ACount: Integer = -1); +function StrCmpA(const s1, s2: PQCharA; AIgnoreCase: Boolean): Integer; +function StrCmpW(const s1, s2: PQCharW; AIgnoreCase: Boolean): Integer; +function StrNCmpW(const s1, s2: PQCharW; AIgnoreCase: Boolean; + ALength: Integer): Integer; +function NaturalCompareW(s1, s2: PQCharW; AIgnoreCase: Boolean; + AIgnoreSpace: Boolean = True): Integer; +function IsHexChar(c: QCharW): Boolean; inline; +function HexValue(c: QCharW): Integer; +function HexChar(V: Byte): QCharW; +function TryStrToGuid(const S: QStringW; var AGuid: TGuid): Boolean; +function TryStrToIPV4(const S: QStringW; var AIPV4: +{$IFDEF MSWINDOWS}Integer{$ELSE}Cardinal{$ENDIF}): Boolean; +function StringReplaceW(const S, Old, New: QStringW; AFlags: TReplaceFlags) + : QStringW; overload; +/// 滻ָΧڵַΪַָ +/// ռλַ +/// ʼλã0ʼ +/// 滻 +/// 滻ַ +function StringReplaceW(const S: QStringW; const AChar: QCharW; + AFrom, ACount: Integer): QStringW; overload; +/// ʹָ滻AStartTagEndTag֮ +/// +/// Ҫ滻ַ +/// ʼıǩ +/// ıǩ +/// 滻Ľ +/// ǷͬAStartTagAEndTagǩһ滻 +/// ȽϱǩʱǷԴС +/// 滻ĬΪ1 +/// +/// +function StringReplaceWithW(const S, AStartTag, AEndTag, AReplaced: QStringW; + AWithTag, AIgnoreCase: Boolean; AMaxTimes: Cardinal = 1): QStringW; + +function StringReplicateW(const S: QStringW; ACount: Integer): QStringW; + +/// ˵ַвҪַ +/// Ҫ˵ַ +/// ַб +/// ع˺Ľ +function FilterCharW(const S: QStringW; AcceptChars: QStringW) + : QStringW; overload; +/// ˵ַвҪַ +/// Ҫ˵ַ +/// ڹ˵Ļص +/// ûԶĸӲᴫݸAOnValidate¼ +/// ع˺Ľ +function FilterCharW(const S: QStringW; AOnValidate: TQFilterCharEvent; + ATag: Pointer = nil): QStringW; overload; +{$IFDEF UNICODE} +/// ˵ַвҪַ +/// Ҫ˵ַ +/// ڹ˵Ļص +/// ûԶĸӲᴫݸAOnValidate¼ +/// ع˺Ľ +function FilterCharW(const S: QStringW; AOnValidate: TQFilterCharEventA; + ATag: Pointer = nil): QStringW; overload; +{$ENDIF} +/// ˵зֵ͵ַӶʽһԱ׼ĸ +/// Ҫ˵ַ +/// +/// ع˺Ľ +/// +/// FilterNoNumberW ˺Ľʹ ParseNumeric Ϊ飬󲿷Ҳ +/// ԱStrToFloatStrToFloat֧Щʽ + +function FilterNoNumberW(const S: QStringW; Accepts: TQNumberTypes): QStringW; + +function MemScan(S: Pointer; len_s: Integer; sub: Pointer; + len_sub: Integer): Pointer; +function BinaryCmp(const p1, p2: Pointer; len: Integer): Integer; +// ĺֻUnicode汾ûAnsiUTF-8汾Ҫټ +function NameOfW(const S: QStringW; ASpliter: QCharW): QStringW; +function ValueOfW(const S: QStringW; ASpliter: QCharW): QStringW; +function IndexOfNameW(AList: TStrings; const AName: QStringW; + ASpliter: QCharW): Integer; +function IndexOfValueW(AList: TStrings; const AValue: QStringW; + ASpliter: QCharW): Integer; +function DeleteCharW(const ASource, ADeletes: QStringW): QStringW; +function DeleteRightW(const S, ADelete: QStringW; AIgnoreCase: Boolean = False; + ACount: Integer = MaxInt): QStringW; +function DeleteLeftW(const S, ADelete: QStringW; AIgnoreCase: Boolean = False; + ACount: Integer = MaxInt): QStringW; +function ContainsCharW(const S, ACharList: QStringW): Boolean; +function HtmlEscape(const S: QStringW): QStringW; +function HtmlUnescape(const S: QStringW): QStringW; +function HtmlTrimText(const S: QStringW): QStringW; +function LeftStrCount(const S: QStringW; const sub: QStringW; + AIgnoreCase: Boolean): Integer; +function RightStrCount(const S: QStringW; const sub: QStringW; + AIgnoreCase: Boolean): Integer; +// һЩ +function ParseInt(var S: PQCharW; var ANum: Int64): Integer; +function ParseHex(var p: PQCharW; var Value: Int64): Integer; +function ParseNumeric(var S: PQCharW; var ANum: Extended): Boolean; +function ParseDateTime(S: PWideChar; var AResult: TDateTime): Boolean; +function ParseWebTime(p: PWideChar; var AResult: TDateTime): Boolean; +function RollupSize(ASize: Int64): QStringW; +function RollupTime(ASeconds: Int64; AHideZero: Boolean = True): QStringW; +function DetectTextEncoding(const p: Pointer; l: Integer; var b: Boolean) + : TTextEncoding; +procedure ExchangeByteOrder(p: PQCharA; l: Integer); overload; +function ExchangeByteOrder(V: Smallint): Smallint; overload; inline; +function ExchangeByteOrder(V: Word): Word; overload; inline; +function ExchangeByteOrder(V: Integer): Integer; overload; inline; +function ExchangeByteOrder(V: Cardinal): Cardinal; overload; inline; +function ExchangeByteOrder(V: Int64): Int64; overload; inline; +function ExchangeByteOrder(V: Single): Single; overload; inline; +function ExchangeByteOrder(V: Double): Double; overload; inline; + +procedure FreeObject(AObject: TObject); inline; +// ԭӲ +function AtomicAnd(var Dest: Integer; const AMask: Integer): Integer; +function AtomicOr(var Dest: Integer; const AMask: Integer): Integer; +{$IF RTLVersion<24} +// ΪXE6ݣInterlockedCompareExchangeȼ +function AtomicCmpExchange(var Target: Integer; Value: Integer; + Comparand: Integer): Integer; inline; overload; +function AtomicCmpExchange(var Target: Pointer; Value: Pointer; + Comparand: Pointer): Pointer; inline; overload; +// ȼInterlockedExchanged +function AtomicExchange(var Target: Integer; Value: Integer): Integer; + inline; overload; +function AtomicExchange(var Target: Pointer; Value: Pointer): Pointer; + inline; overload; + +function AtomicIncrement(var Target: Integer; const Value: Integer = 1) + : Integer; inline; +function AtomicDecrement(var Target: Integer): Integer; inline; +{$IFEND ָݵǿ +/// +/// һ>=0ǿֵ +function PasswordScale(const S: QStringW): Integer; +/// ָǿϵתΪǿȵȼ +/// ͨPasswordScaleõǿȵȼ +/// תǿȵȼ +function CheckPassword(const AScale: Integer): TPasswordStrongLevel; overload; +/// ָݵǿȵȼ +/// +/// ؼõǿȵȼ +function CheckPassword(const S: QStringW): TPasswordStrongLevel; overload; + +var + JavaFormatUtf8: Boolean; + IsFMXApp: Boolean; + +const + SLineBreak: PQCharW = {$IFDEF MSWINDOWS}#13#10{$ELSE}#10{$ENDIF}; + DefaultNumberSet = [nftFloat, nftDelphiHex, nftCHex, nftBasicHex, nftHexPrec, + nftNegative, nftPositive]; + +implementation + +uses dateutils, math, variants +{$IFDEF MSWINDOWS} + , windows +{$ENDIF} +{$IF (RTLVersion>=25) and (not Defined(NEXTGEN))} + , AnsiStrings +{$IFEND >=XE4} + ; + +resourcestring + SBadUnicodeChar = 'ЧUnicodeַ:%d'; + SBadUtf8Char = 'ЧUTF8ַ:%d'; + SOutOfIndex = 'Խ磬ֵ %d [%d..%d]ķΧڡ'; + SDayName = ''; + SHourName = 'Сʱ'; + SMinuteName = ''; + SSecondName = ''; + SCharNeeded = 'ǰλӦ "%s" "%s"'; + SRangeEndNeeded = 'ַΧַ߽δָ'; + +type + TGBKCharSpell = record + SpellChar: QCharW; + StartChar, EndChar: Word; + end; + + TStrStrFunction = function(s1, s2: PQCharW): PQCharW; +{$IF RTLVersion>=21} + TIntArray = TArray; +{$ELSE} + TIntArray = array of Integer; +{$IFEND >=2010} +{$IFDEF MSWINDOWS} + TMSVCStrStr = function(s1, s2: PQCharA): PQCharA; cdecl; + TMSVCStrStrW = function(s1, s2: PQCharW): PQCharW; cdecl; + TMSVCMemCmp = function(s1, s2: Pointer; len: Integer): Integer; cdecl; +{$ENDIF} + +var + // GBKƴĸ + GBKSpells: array [0 .. 22] of TGBKCharSpell = ( + ( + SpellChar: 'A'; StartChar: $B0A1; EndChar: $B0C4; + ), (SpellChar: 'B'; StartChar: $B0C5; EndChar: $B2C0; + ), (SpellChar: 'C'; StartChar: $B2C1; EndChar: $B4ED; + ), (SpellChar: 'D'; StartChar: $B4EE; EndChar: $B6E9; + ), (SpellChar: 'E'; StartChar: $B6EA; EndChar: $B7A1; + ), (SpellChar: 'F'; StartChar: $B7A2; EndChar: $B8C0; + ), (SpellChar: 'G'; StartChar: $B8C1; EndChar: $B9FD; + ), (SpellChar: 'H'; StartChar: $B9FE; EndChar: $BBF6; + ), (SpellChar: 'J'; StartChar: $BBF7; EndChar: $BFA5; + ), (SpellChar: 'K'; StartChar: $BFA6; EndChar: $C0AB; + ), (SpellChar: 'L'; StartChar: $C0AC; EndChar: $C2E7; + ), (SpellChar: 'M'; StartChar: $C2E8; EndChar: $C4C2; + ), (SpellChar: 'N'; StartChar: $C4C3; EndChar: $C5B5; + ), (SpellChar: 'O'; StartChar: $C5B6; EndChar: $C5BD; + ), (SpellChar: 'P'; StartChar: $C5BE; EndChar: $C6D9; + ), (SpellChar: 'Q'; StartChar: $C6DA; EndChar: $C8BA; + ), (SpellChar: 'R'; StartChar: $C8BB; EndChar: $C8F5; + ), (SpellChar: 'S'; StartChar: $C8F6; EndChar: $CBF0; + ), (SpellChar: 'T'; StartChar: $CBFA; EndChar: $CDD9; + ), (SpellChar: 'W'; StartChar: $CDDA; EndChar: $CEF3; + ), (SpellChar: 'X'; StartChar: $CEF4; EndChar: $D188; + ), (SpellChar: 'Y'; StartChar: $D1B9; EndChar: $D4D0; + ), (SpellChar: 'Z'; StartChar: $D4D1; EndChar: $D7F9;)); +{$IFDEF MSWINDOWS} + hMsvcrtl: HMODULE; + VCStrStr: TMSVCStrStr; + VCStrStrW: TMSVCStrStrW; + VCMemCmp: TMSVCMemCmp; +{$ENDIF} + +const + HtmlEscapeChars: array [0 .. 185] of QStringW = (QCharW(32), ' ', + QCharW(34), '"', QCharW(38), '&', QCharW(39), ''', QCharW(60), + '<', QCharW(62), '>', QCharW(161), '¡', QCharW(162), '¢', + QCharW(163), '£', QCharW(164), '¤', QCharW(165), '¥', + QCharW(166), '¦', QCharW(167), '§', QCharW(168), '¨', + QCharW(169), '©', QCharW(170), 'ª', QCharW(171), '«', + QCharW(172), '¬', QCharW(173), '­', QCharW(174), '®', + QCharW(175), '¯', QCharW(176), '°', QCharW(177), '±', + QCharW(180), '´', QCharW(181), 'µ', QCharW(182), '¶', + QCharW(183), '·', QCharW(184), '¸', QCharW(186), 'º', + QCharW(187), '»', QCharW(191), '¿', QCharW(192), 'À', + QCharW(193), 'Á', QCharW(194), 'ˆ', QCharW(195), 'Ã', + QCharW(197), '˚', QCharW(198), 'Æ', QCharW(199), 'Ç', + QCharW(200), 'È', QCharW(201), 'É', QCharW(202), 'Ê', + QCharW(203), 'Ë', QCharW(204), 'Ì', QCharW(205), 'Í', + QCharW(206), 'Î', QCharW(207), 'Ï', QCharW(208), 'Ð', + QCharW(209), 'Ñ', QCharW(210), 'Ò', QCharW(211), 'Ó', + QCharW(212), 'Ô', QCharW(213), 'Õ', QCharW(214), 'Ö', + QCharW(215), '×', QCharW(216), 'Ø', QCharW(217), 'Ù', + QCharW(218), 'Ú', QCharW(219), 'Û', QCharW(220), 'Ü', + QCharW(221), 'Ý', QCharW(222), 'Þ', QCharW(223), 'ß', + QCharW(224), 'à', QCharW(225), 'á', QCharW(227), 'ã', + QCharW(228), 'ä', QCharW(229), 'å', QCharW(230), 'æ', + QCharW(231), 'ç', QCharW(232), 'è', QCharW(233), 'é', + QCharW(234), 'ê', QCharW(235), 'ë', QCharW(236), 'ì', + QCharW(237), 'í', QCharW(238), 'î', QCharW(239), 'ï', + QCharW(240), '&ieth;', QCharW(241), 'ñ', QCharW(242), 'ò', + QCharW(243), 'ó', QCharW(244), 'ô', QCharW(245), 'õ', + QCharW(246), 'ö', QCharW(247), '÷', QCharW(248), 'ø', + QCharW(249), 'ù', QCharW(250), 'ú', QCharW(251), 'û', + QCharW(252), 'ü', QCharW(253), 'ý', QCharW(254), 'þ', + QCharW(255), 'ÿ'); + // QString + +function Utf8Decode(const p: QStringA): QStringW; +begin + if p.IsUtf8 then + Result := Utf8Decode(PQCharA(p), p.Length) + else if p.Length > 0 then + Result := AnsiDecode(p) + else + SetLength(Result, 0); +end; + +function Utf8Encode(const p: QStringW): QStringA; +var + l: NativeInt; +begin + l := Length(p); + if l > 0 then + Result := Utf8Encode(PQCharW(p), l) + else + begin + Result.Length := 0; + Result.FValue[0] := 1; + end; +end; + +function Utf8Decode(p: PQCharA; l: Integer; var AResult: QStringW; + var ABadAt: PQCharA): Boolean; overload; +var + ps, pe: PByte; + pd, pds: PWord; + c: Cardinal; +begin + if l <= 0 then + begin + ps := PByte(p); + while ps^ <> 0 do + Inc(ps); + l := IntPtr(ps) - IntPtr(p); + end; + ps := PByte(p); + pe := ps; + Inc(pe, l); + System.SetLength(AResult, l); + pd := PWord(PQCharW(AResult)); + pds := pd; + Result := True; + while IntPtr(ps) < IntPtr(pe) do + begin + if (ps^ and $80) <> 0 then + begin + if (ps^ and $FC) = $FC then // 4000000+ + begin + c := (ps^ and $03) shl 30; + Inc(ps); + c := c or ((ps^ and $3F) shl 24); + Inc(ps); + c := c or ((ps^ and $3F) shl 18); + Inc(ps); + c := c or ((ps^ and $3F) shl 12); + Inc(ps); + c := c or ((ps^ and $3F) shl 6); + Inc(ps); + c := c or (ps^ and $3F); + Inc(ps); + c := c - $10000; + pd^ := $D800 + ((c shr 10) and $3FF); + Inc(pd); + pd^ := $DC00 + (c and $3FF); + Inc(pd); + end + else if (ps^ and $F8) = $F8 then // 200000-3FFFFFF + begin + c := (ps^ and $07) shl 24; + Inc(ps); + c := c or ((ps^ and $3F) shl 18); + Inc(ps); + c := c or ((ps^ and $3F) shl 12); + Inc(ps); + c := c or ((ps^ and $3F) shl 6); + Inc(ps); + c := c or (ps^ and $3F); + Inc(ps); + c := c - $10000; + pd^ := $D800 + ((c shr 10) and $3FF); + Inc(pd); + pd^ := $DC00 + (c and $3FF); + Inc(pd); + end + else if (ps^ and $F0) = $F0 then // 10000-1FFFFF + begin + c := (ps^ and $0F) shl 18; + Inc(ps); + c := c or ((ps^ and $3F) shl 12); + Inc(ps); + c := c or ((ps^ and $3F) shl 6); + Inc(ps); + c := c or (ps^ and $3F); + Inc(ps); + c := c - $10000; + pd^ := $D800 + ((c shr 10) and $3FF); + Inc(pd); + pd^ := $DC00 + (c and $3FF); + Inc(pd); + end + else if (ps^ and $E0) = $E0 then // 800-FFFF + begin + c := (ps^ and $1F) shl 12; + Inc(ps); + c := c or ((ps^ and $3F) shl 6); + Inc(ps); + c := c or (ps^ and $3F); + Inc(ps); + pd^ := c; + Inc(pd); + end + else if (ps^ and $C0) = $C0 then // 80-7FF + begin + pd^ := (ps^ and $3F) shl 6; + Inc(ps); + pd^ := pd^ or (ps^ and $3F); + Inc(pd); + Inc(ps); + end + else + begin + ABadAt := PQCharA(ps); + Result := False; + Exit; + end; + end + else + begin + pd^ := ps^; + Inc(ps); + Inc(pd); + end; + end; + System.SetLength(AResult, (IntPtr(pd) - IntPtr(pds)) shr 1); + +end; + +function Utf8Decode(p: PQCharA; l: Integer): QStringW; +var + ABadChar: PQCharA; +begin + if not Utf8Decode(p, l, Result, ABadChar) then + raise Exception.Create(Format(SBadUtf8Char, [ABadChar^])); +end; + +function WideCharUtf8Size(c: Integer): Integer; +begin + if c < $7F then + Result := 1 + else if c < $7FF then + Result := 2 + else if c < $FFFF then + Result := 3 + else if c < $1FFFFF then + Result := 4 + else if c < $3FFFFFF then + Result := 5 + else + Result := 6; +end; + +function Utf8BufferSize(p: PQCharW; var l: Integer): Integer; +var + c: Cardinal; + t: Integer; +begin + Result := 0; + if l < 0 then + begin + l := 0; + while p^ <> #0 do + begin + if (p^ >= #$D800) and (p^ <= #$DFFF) then // Unicode չַ + begin + c := (Word(p^) - $D800); + Inc(p); + if (p^ >= #$DC00) and (p^ <= #$DFFF) then + begin + c := $10000 + (c shl 10) + Word(p^) - $DC00; + Inc(p); + end; + Inc(Result, WideCharUtf8Size(c)); + end + else + Inc(Result, WideCharUtf8Size(Word(p^))); + Inc(p); + Inc(l); + end; + end + else + begin + t := l; + while t > 0 do + begin + if (p^ >= #$D800) and (p^ <= #$DFFF) then // Unicode չַ + begin + c := (Word(p^) - $D800); + Inc(p); + if (p^ >= #$DC00) and (p^ <= #$DFFF) then + begin + c := $10000 + (c shl 10) + Word(p^) - $DC00; + Inc(p); + end; + Inc(Result, WideCharUtf8Size(c)); + end + else + Inc(Result, WideCharUtf8Size(Word(p^))); + Inc(p); + Dec(t); + end; + end; +end; + +function Utf8Encode(p: PQCharW; l: Integer): QStringA; +var + ps: PQCharW; + pd, pds: PQCharA; + c: Cardinal; +begin + if p = nil then + begin + Result.Length := 0; + Result.FValue[0] := 1; + end + else + begin + Result.Length := Utf8BufferSize(p, l); + if l > 0 then + begin + Result.FValue[0] := 1; + ps := p; + pd := PQCharA(Result); + pds := pd; + while l > 0 do + begin + c := Cardinal(ps^); + Inc(ps); + if (c >= $D800) and (c <= $DFFF) then // Unicode չַ + begin + c := (c - $D800); + if (ps^ >= #$DC00) and (ps^ <= #$DFFF) then + begin + c := $10000 + ((c shl 10) + (Cardinal(ps^) - $DC00)); + Inc(ps); + Dec(l); + end + else + raise Exception.Create(Format(SBadUnicodeChar, [IntPtr(ps^)])); + end; + Dec(l); + if c = $0 then + begin + if JavaFormatUtf8 then // Javaʽ룬#$0ַΪ#$C080 + begin + pd^ := $C0; + Inc(pd); + pd^ := $80; + Inc(pd); + end + else + begin + pd^ := c; + Inc(pd); + end; + end + else if c <= $7F then // 1B + begin + pd^ := c; + Inc(pd); + end + else if c <= $7FF then // $80-$7FF,2B + begin + pd^ := $C0 or (c shr 6); + Inc(pd); + pd^ := $80 or (c and $3F); + Inc(pd); + end + else if c <= $FFFF then // $8000 - $FFFF,3B + begin + pd^ := $E0 or (c shr 12); + Inc(pd); + pd^ := $80 or ((c shr 6) and $3F); + Inc(pd); + pd^ := $80 or (c and $3F); + Inc(pd); + end + else if c <= $1FFFFF then // $01 0000-$1F FFFF,4B + begin + pd^ := $F0 or (c shr 18); // 1111 0xxx + Inc(pd); + pd^ := $80 or ((c shr 12) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or ((c shr 6) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or (c and $3F); // 10 xxxxxx + Inc(pd); + end + else if c <= $3FFFFFF then // $20 0000 - $3FF FFFF,5B + begin + pd^ := $F8 or (c shr 24); // 1111 10xx + Inc(pd); + pd^ := $F0 or ((c shr 18) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or ((c shr 12) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or ((c shr 6) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or (c and $3F); // 10 xxxxxx + Inc(pd); + end + else if c <= $7FFFFFFF then // $0400 0000-$7FFF FFFF,6B + begin + pd^ := $FC or (c shr 30); // 1111 11xx + Inc(pd); + pd^ := $F8 or ((c shr 24) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $F0 or ((c shr 18) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or ((c shr 12) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or ((c shr 6) and $3F); // 10 xxxxxx + Inc(pd); + pd^ := $80 or (c and $3F); // 10 xxxxxx + Inc(pd); + end; + end; + pd^ := 0; + Result.Length := IntPtr(pd) - IntPtr(pds); + end; + end; +end; + +function AnsiEncode(p: PQCharW; l: Integer): QStringA; +var + ps: PQCharW; +begin + if l <= 0 then + begin + ps := p; + while ps^ <> #0 do + Inc(ps); + l := ps - p; + end; + if l > 0 then + begin +{$IFDEF MSWINDOWS} + Result.Length := WideCharToMultiByte(CP_ACP, 0, p, l, nil, 0, nil, nil); + WideCharToMultiByte(CP_ACP, 0, p, l, PAnsiChar(PQCharA(Result)), + Result.Length, nil, nil); +{$ELSE} + Result.Length := l shl 1; + Result.FValue[0] := 0; + Move(p^, PQCharA(Result)^, l shl 1); + Result := TEncoding.Convert(TEncoding.Unicode, TEncoding.ANSI, + Result.FValue, 1, l shl 1); +{$ENDIF} + end + else + Result.Length := 0; +end; + +function AnsiEncode(const p: QStringW): QStringA; +var + l: NativeInt; +begin + l := Length(p); + if l > 0 then + Result := AnsiEncode(PQCharW(p), l) + else + Result.Length := 0; +end; + +function AnsiDecode(p: PQCharA; l: Integer): QStringW; +var + ps: PQCharA; +{$IFNDEF MSWINDOWS} + ABytes: TBytes; +{$ENDIF} +begin + if l <= 0 then + begin + ps := p; + while ps^ <> 0 do + Inc(ps); + l := IntPtr(ps) - IntPtr(p); + end; + if l > 0 then + begin +{$IFDEF MSWINDOWS} + System.SetLength(Result, MultiByteToWideChar(CP_ACP, 0, PAnsiChar(p), + l, nil, 0)); + MultiByteToWideChar(CP_ACP, 0, PAnsiChar(p), l, PWideChar(Result), + Length(Result)); +{$ELSE} + System.SetLength(ABytes, l); + Move(p^, PByte(@ABytes[0])^, l); + Result := TEncoding.ANSI.GetString(ABytes); +{$ENDIF} + end + else + System.SetLength(Result, 0); +end; + +function AnsiDecode(const p: QStringA): QStringW; +begin + if p.IsUtf8 then + Result := Utf8Decode(p) + else if p.Length > 0 then + begin +{$IFDEF MSWINDOWS} + Result := AnsiDecode(PQCharA(p), p.Length); +{$ELSE} + Result := TEncoding.ANSI.GetString(p.FValue, 1, p.Length); +{$ENDIF} + end + else + SetLength(Result, 0); +end; + +function CNSpellChars(S: QStringA; AIgnoreEnChars: Boolean): QStringW; +var + p: PQCharA; + pd, pds: PQCharW; + function SpellOfChar: QCharW; + var + I: Integer; + w: Word; + begin + w := p^ shl 8; + Inc(p); + w := w or p^; + Inc(p); + Result := #0; + for I := 0 to 22 do + begin + if (w >= GBKSpells[I].StartChar) and (w <= GBKSpells[I].EndChar) then + begin + Result := GBKSpells[I].SpellChar; + Break; + end; + end; + end; + +begin + if S.Length > 0 then + begin + p := PQCharA(S); + System.SetLength(Result, S.Length); + pd := PQCharW(Result); + pds := pd; + while p^ <> 0 do + begin + if p^ in [1 .. 127] then + begin + if not AIgnoreEnChars then + begin + pd^ := QCharW(p^); + Inc(pd); + end; + Inc(p); + end + else + begin + pd^ := SpellOfChar; + if pd^ <> #0 then + Inc(pd); + end; + end; + System.SetLength(Result, pd - pds); + end + else + System.SetLength(Result, 0); +end; + +function CNSpellChars(S: QStringW; AIgnoreEnChars: Boolean): QStringW; +begin + Result := CNSpellChars(AnsiEncode(S), AIgnoreEnChars); +end; + +function CharSizeA(c: PQCharA): Integer; +begin + { GB18030,GBKGB2312 + ֽڣֵ00x7F + ˫ֽڣһֽڵֵ0x810xFEڶֽڵֵ0x400xFE0x7F + ֽڣһֽڵֵ0x810xFEڶֽڵֵ0x300x39ֽڴ0x810xFEĸֽڴ0x300x39 + } +{$IFDEF MSWINDOWS} + if GetACP = 936 then +{$ELSE} + if TEncoding.ANSI.CodePage = 936 then +{$ENDIF} + begin + Result := 1; + if (c^ >= $81) and (c^ <= $FE) then + begin + Inc(c); + if (c^ >= $40) and (c^ <= $FE) and (c^ <> $7F) then + Result := 2 + else if (c^ >= $30) and (c^ <= $39) then + begin + Inc(c); + if (c^ >= $81) and (c^ <= $FE) then + begin + Inc(c); + if (c^ >= $30) and (c^ <= $39) then + Result := 4; + end; + end; + end; + end + else +{$IFDEF QDAC_ANSISTRINGS} + Result := AnsiStrings.StrCharLength(PAnsiChar(c)); +{$ELSE} +{$IFDEF NEXTGEN} + if TEncoding.ANSI.CodePage = CP_UTF8 then + Result := CharSizeU(c) + else if (c^ < 128) or (TEncoding.ANSI.CodePage = 437) then + Result := 1 + else + Result := 2; +{$ELSE} +{$IF RTLVersion>=25} + Result := AnsiStrings.StrCharLength(PAnsiChar(c)); +{$ELSE} + Result := sysutils.StrCharLength(PAnsiChar(c)); +{$IFEND} +{$ENDIF} +{$ENDIF !QDAC_ANSISTRINGS} +end; + +function CharSizeU(c: PQCharA): Integer; +begin + if (c^ and $80) = 0 then + Result := 1 + else + begin + if (c^ and $FC) = $FC then // 4000000+ + Result := 6 + else if (c^ and $F8) = $F8 then // 200000-3FFFFFF + Result := 5 + else if (c^ and $F0) = $F0 then // 10000-1FFFFF + Result := 4 + else if (c^ and $E0) = $E0 then // 800-FFFF + Result := 3 + else if (c^ and $C0) = $C0 then // 80-7FF + Result := 2 + else + Result := 1; + end +end; + +function CharSizeW(c: PQCharW): Integer; +begin + if (c[0] >= #$D800) and (c[0] <= #$DBFF) and (c[1] >= #$DC00) and + (c[1] <= #$DFFF) then + Result := 2 + else + Result := 1; +end; + +function CharCodeA(c: PQCharA): Cardinal; +var + t: QStringA; +begin + t := AnsiDecode(c, CharSizeA(c)); + Result := CharCodeW(PQCharW(t)); +end; + +function CharCodeU(c: PQCharA): Cardinal; +begin + if (c^ and $80) <> 0 then + begin + if (c^ and $FC) = $FC then // 4000000+ + begin + Result := (c^ and $03) shl 30; + Inc(c); + Result := Result or ((c^ and $3F) shl 24); + Inc(c); + Result := Result or ((c^ and $3F) shl 18); + Inc(c); + Result := Result or ((c^ and $3F) shl 12); + Inc(c); + Result := Result or ((c^ and $3F) shl 6); + Inc(c); + Result := Result or (c^ and $3F); + end + else if (c^ and $F8) = $F8 then // 200000-3FFFFFF + begin + Result := (c^ and $07) shl 24; + Inc(c); + Result := Result or ((c^ and $3F) shl 18); + Inc(c); + Result := Result or ((c^ and $3F) shl 12); + Inc(c); + Result := Result or ((c^ and $3F) shl 6); + Inc(c); + Result := Result or (c^ and $3F); + end + else if (c^ and $F0) = $F0 then // 10000-1FFFFF + begin + Result := (c^ and $0F) shr 18; + Inc(c); + Result := Result or ((c^ and $3F) shl 12); + Inc(c); + Result := Result or ((c^ and $3F) shl 6); + Inc(c); + Result := Result or (c^ and $3F); + end + else if (c^ and $E0) = $E0 then // 800-FFFF + begin + Result := (c^ and $1F) shl 12; + Inc(c); + Result := Result or ((c^ and $3F) shl 6); + Inc(c); + Result := Result or (c^ and $3F); + end + else if (c^ and $C0) = $C0 then // 80-7FF + begin + Result := (c^ and $3F) shl 6; + Inc(c); + Result := Result or (c^ and $3F); + end + else + raise Exception.Create(Format(SBadUtf8Char, [IntPtr(c^)])); + end + else + Result := c^; +end; + +function CharCodeW(c: PQCharW): Cardinal; +begin + if (c^ >= #$D800) and (c^ <= #$DFFF) then // Unicode չַ + begin + Result := (Ord(c^) - $D800); + Inc(c); + if (c^ >= #$DC00) and (c^ <= #$DFFF) then + begin + Result := $10000 + ((Result shl 10) + (Cardinal(Ord(c^)) - $DC00)); + end + else + Result := 0 + end + else + Result := Ord(c^); +end; + +function CharCountA(const source: QStringA): Integer; +var + p: PQCharA; + l, ASize: Integer; +begin + p := PQCharA(source); + l := source.Length; + Result := 0; + while l > 0 do + begin + ASize := CharSizeA(p); + Dec(l, ASize); + Inc(p, ASize); + Inc(Result); + end; + // Result:=TEncoding.ANSI.GetCharCount(source); +end; + +function CharCountW(const S: QStringW): Integer; +var + p, pe: PWord; + ALen: Integer; + procedure CountChar; + begin + if (p^ > $D800) and (p^ < $DFFF) then + begin + Inc(p); + if (p^ >= $DC00) and (p^ < $DFFF) then + begin + Inc(p); + Inc(Result); + end + else + Result := -1; + end + else + begin + Inc(Result); + Inc(p); + end; + end; + +begin + Result := 0; + p := PWord(S); + ALen := Length(S); + pe := PWord(IntPtr(p) + (ALen shl 1)); + while IntPtr(p) < IntPtr(pe) do + CountChar; +end; + +function CharCountU(const source: QStringA): Integer; +var + p, pe: PQCharA; + procedure CountChar; + begin + if (p^ and $80) = 0 then + begin + Inc(Result); + Inc(p); + end + else if (p^ and $FC) = $FC then + begin + Inc(Result); + Inc(p, 6); + end + else if (p^ and $F8) = $F8 then + begin + Inc(Result); + Inc(p, 5); + end + else if (p^ and $F0) = $F0 then + begin + Inc(Result); + Inc(p, 4); + end + else if (p^ and $E0) = $E0 then + begin + Inc(Result); + Inc(p, 3); + end + else if (p^ and $C0) = $C0 then + begin + Inc(Result); + Inc(p, 2); + end + else + Result := -1; + end; + +begin + Result := 0; + p := PQCharA(source); + pe := PQCharA(IntPtr(p) + source.Length); + while (IntPtr(p) < IntPtr(pe)) and (Result >= 0) do + CountChar; +end; + +procedure CalcCharLengthA(var Lens: TIntArray; const List: array of QCharA); +var + I, l: Integer; +begin + I := Low(List); + System.SetLength(Lens, Length(List)); + while I <= High(List) do + begin + l := CharSizeA(@List[I]); + Lens[I] := l; + Inc(I, l); + end; +end; + +function CharInA(const c: PQCharA; const List: array of QCharA; + ACharLen: PInteger): Boolean; +var + I, count: Integer; + Lens: TIntArray; +begin + count := High(List) + 1; + Result := False; + CalcCharLengthA(Lens, List); + I := Low(List); + while I < count do + begin + if CompareMem(c, @List[I], Lens[I]) then + begin + if ACharLen <> nil then + ACharLen^ := Lens[I]; + Result := True; + Break; + end + else + Inc(I, Lens[I]); + end; +end; + +procedure CalcCharLengthW(var Lens: TIntArray; const List: array of QCharW); +var + I, l: Integer; +begin + I := Low(List); + System.SetLength(Lens, Length(List)); + while I <= High(List) do + begin + l := CharSizeW(@List[I]); + Lens[I] := l; + Inc(I, l); + end; +end; + +function CharInW(const c: PQCharW; const List: array of QCharW; + ACharLen: PInteger): Boolean; +var + I, count: Integer; + Lens: TIntArray; +begin + count := High(List) + 1; + Result := False; + CalcCharLengthW(Lens, List); + I := Low(List); + while I < count do + begin + if c^ = List[I] then + begin + if Lens[I] = 2 then + begin + Result := c[1] = List[I + 1]; + if Assigned(ACharLen) and Result then + ACharLen^ := 2; + if Result then + Break; + end + else + begin + Result := True; + if Assigned(ACharLen) then + ACharLen^ := 1; + Break; + end; + end; + Inc(I, Lens[I]); + end; +end; + +function CharInW(const c, List: PQCharW; ACharLen: PInteger): Boolean; +var + p: PQCharW; +begin + Result := False; + p := List; + while p^ <> #0 do + begin + if p^ = c^ then + begin + if (p[0] >= #$D800) and (p[0] <= #$DBFF) then + begin + // (p[1] >= #$DC00) and (p[1] <= #$DFFF) + if p[1] = c[1] then + begin + Result := True; + if ACharLen <> nil then + ACharLen^ := 2; + Break; + end; + end + else + begin + Result := True; + if ACharLen <> nil then + ACharLen^ := 1; + Break; + end; + end; + Inc(p); + end; +end; + +procedure CalcCharLengthU(var Lens: TIntArray; const List: array of QCharA); +var + I, l: Integer; +begin + I := Low(List); + System.SetLength(Lens, Length(List)); + while I <= High(List) do + begin + l := CharSizeU(@List[I]); + Lens[I] := l; + Inc(I, l); + end; +end; + +function CharInU(const c: PQCharA; const List: array of QCharA; + ACharLen: PInteger): Boolean; +var + I, count: Integer; + Lens: TIntArray; +begin + count := High(List) + 1; + Result := False; + CalcCharLengthU(Lens, List); + I := Low(List); + while I < count do + begin + if CompareMem(c, @List[I], Lens[I]) then + begin + if ACharLen <> nil then + ACharLen^ := Lens[I]; + Result := True; + Break; + end + else + Inc(I, Lens[I]); + end; +end; + +function IsSpaceA(const c: PQCharA; ASpaceSize: PInteger): Boolean; +begin + if c^ in [9, 10, 13, 32] then + begin + Result := True; + if Assigned(ASpaceSize) then + ASpaceSize^ := 1; + end + else if (c^ = 161) and (PQCharA(IntPtr(c) + 1)^ = 161) then + begin + Result := True; + if Assigned(ASpaceSize) then + ASpaceSize^ := 2; + end + else + Result := False; +end; + +function IsSpaceW(const c: PQCharW; ASpaceSize: PInteger): Boolean; +begin + Result := (c^ = #9) or (c^ = #10) or (c^ = #13) or (c^ = #32) or + (c^ = #$3000); + if Result and Assigned(ASpaceSize) then + ASpaceSize^ := 1; +end; + +function IsSpaceU(const c: PQCharA; ASpaceSize: PInteger): Boolean; +begin + // ȫǿո$3000UTF-8227,128,128 + if c^ in [9, 10, 13, 32] then + begin + Result := True; + if Assigned(ASpaceSize) then + ASpaceSize^ := 1; + end + else if (c^ = 227) and (PQCharA(IntPtr(c) + 1)^ = 128) and + (PQCharA(IntPtr(c) + 2)^ = 128) then + begin + Result := True; + if Assigned(ASpaceSize) then + ASpaceSize^ := 3; + end + else + Result := False; +end; + +function CNFullToHalf(const S: QStringW): QStringW; +var + p, pd: PWord; + l: Integer; +begin + l := Length(S); + if l > 0 then + begin + System.SetLength(Result, l); + p := PWord(PQCharW(S)); + pd := PWord(PQCharW(Result)); + while l > 0 do + begin + if (p^ = $3000) then // ȫǿո'' + pd^ := $20 + else if (p^ >= $FF01) and (p^ <= $FF5E) then + pd^ := $21 + (p^ - $FF01) + else + pd^ := p^; + Dec(l); + Inc(p); + Inc(pd); + end; + end + else + System.SetLength(Result, 0); +end; + +function CNHalfToFull(const S: QStringW): QStringW; +var + p, pd: PWord; + l: Integer; +begin + l := Length(S); + if l > 0 then + begin + System.SetLength(Result, l); + p := PWord(PQCharW(S)); + pd := PWord(PQCharW(Result)); + while l > 0 do + begin + if p^ = $20 then // ȫǿո'' + pd^ := $3000 + else if (p^ >= $21) and (p^ <= $7E) then + pd^ := $FF01 + (p^ - $21) + else + pd^ := p^; + Dec(l); + Inc(p); + Inc(pd); + end; + end + else + System.SetLength(Result, 0); +end; + +function QuotedStrA(const S: QStringA; const AQuoter: QCharA): QStringA; +var + p, pe, pd, pds: PQCharA; +begin + p := PQCharA(S); + Result.Length := S.Length shl 1; + pe := p; + Inc(pe, S.Length); + pd := PQCharA(Result); + pds := pd; + pd^ := AQuoter; + Inc(pd); + while IntPtr(p) < IntPtr(pe) do + begin + if p^ = AQuoter then + begin + pd^ := AQuoter; + Inc(pd); + pd^ := AQuoter; + end + else + pd^ := p^; + Inc(pd); + Inc(p); + end; + pd^ := AQuoter; + Result.Length := IntPtr(pd) - IntPtr(pds) + 1; +end; + +function QuotedStrW(const S: QStringW; const AQuoter: QCharW): QStringW; +var + p, pe, pd, pds: PQCharW; + l: Integer; +begin + if AQuoter <> #0 then + begin + l := System.Length(S); + p := PQCharW(S); + SetLength(Result, (l + 1) shl 1); + pe := p; + Inc(pe, l); + pd := PQCharW(Result); + pds := pd; + pd^ := AQuoter; + Inc(pd); + while IntPtr(p) < IntPtr(pe) do + begin + if p^ = AQuoter then + begin + pd^ := AQuoter; + Inc(pd); + pd^ := AQuoter; + end + else + pd^ := p^; + Inc(pd); + Inc(p); + end; + pd^ := AQuoter; + SetLength(Result, ((IntPtr(pd) - IntPtr(pds)) shr 1) + 1); + end + else + Result := S; +end; + +function SQLQuoted(const S: QStringW): QStringW; +begin + Result := QuotedStrW(S); +end; + +function DequotedStrA(const S: QStringA; const AQuoter: QCharA): QStringA; +var + p, pe, pd, pds: PQCharA; +begin + if (S.Length > 0) and (S[0] = AQuoter) and (S[S.Length - 1] = AQuoter) then + begin + p := PQCharA(S); + pe := p; + Inc(pe, S.Length); + Inc(p); + Result.Length := S.Length; + pd := PQCharA(Result); + pds := pd; + while IntPtr(p) < IntPtr(pe) do + begin + if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + begin + pd^ := AQuoter; + end + else if IntPtr(p) < IntPtr(pe) then // 治ǵ,ֱַӿ + begin + pd^ := AQuoter; + Inc(pd); + pd^ := p^; + end + else + Break; + end + else + pd^ := p^; + Inc(p); + Inc(pd); + end; + Result.Length := IntPtr(pd) - IntPtr(pds); + end + else + Result := S; +end; + +function DequotedStrW(const S: QStringW; const AQuoter: QCharW): QStringW; +var + p, pe, pd, pds: PQCharW; +begin + if (Length(S) > 0) and (PQCharW(S)[0] = AQuoter) and + (PQCharW(S)[Length(S) - 1] = AQuoter) then + begin + p := PQCharW(S); + pe := p; + Inc(pe, Length(S)); + Inc(p); + SetLength(Result, Length(S)); + pd := PQCharW(Result); + pds := pd; + while IntPtr(p) < IntPtr(pe) do + begin + if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + begin + pd^ := AQuoter; + end + else if IntPtr(p) < IntPtr(pe) then // 治ǵ,ֱַӿ + begin + pd^ := AQuoter; + Inc(pd); + pd^ := p^; + end + else + Break; + end + else + pd^ := p^; + Inc(p); + Inc(pd); + end; + SetLength(Result, (IntPtr(pd) - IntPtr(pds)) shr 1); + end + else + Result := S; +end; + +function SkipCharA(var p: PQCharA; const List: array of QCharA): Integer; +var + I, count: Integer; + Lens: TIntArray; + AFound: Boolean; + ps: PQCharA; +begin + count := High(List) + 1; + Result := 0; + if count > 0 then + begin + CalcCharLengthA(Lens, List); + ps := p; + while p^ <> 0 do + begin + I := Low(List); + AFound := False; + while I < count do + begin + if CompareMem(p, @List[I], Lens[I]) then + begin + AFound := True; + Inc(p, Lens[I]); + Break; + end + else + Inc(I, Lens[I]); + end; + if not AFound then + begin + Result := IntPtr(p) - IntPtr(ps); + Break; + end; + end; + end; +end; + +function SkipCharU(var p: PQCharA; const List: array of QCharA): Integer; +var + I, count: Integer; + Lens: TIntArray; + AFound: Boolean; + ps: PQCharA; +begin + count := High(List) + 1; + Result := 0; + if count > 0 then + begin + CalcCharLengthU(Lens, List); + ps := p; + while p^ <> 0 do + begin + I := Low(List); + AFound := False; + while I < count do + begin + if CompareMem(p, @List[I], Lens[I]) then + begin + AFound := True; + Inc(p, Lens[I]); + Break; + end + else + Inc(I, Lens[I]); + end; + if not AFound then + begin + Result := IntPtr(p) - IntPtr(ps); + Break; + end; + end; + end; +end; + +function SkipCharW(var p: PQCharW; const List: array of QCharA): Integer; +var + I, count: Integer; + Lens: TIntArray; + AFound: Boolean; + ps: PQCharW; +begin + count := High(List) + 1; + Result := 0; + if count > 0 then + begin + CalcCharLengthA(Lens, List); + ps := p; + while p^ <> #0 do + begin + I := Low(List); + AFound := False; + while I < count do + begin + if CompareMem(p, @List[I], Lens[I] shl 1) then + begin + AFound := True; + Break; + end + else + Inc(I, Lens[I]); + end; + if AFound then + Inc(p) + else + begin + Result := IntPtr(p) - IntPtr(ps); + Break; + end; + end; + end; +end; + +function SkipCharW(var p: PQCharW; const List: PQCharW): Integer; +var + l: Integer; + ps: PQCharW; +begin + Result := 0; + if (List <> nil) and (List^ <> #0) then + begin + ps := p; + while p^ <> #0 do + begin + if CharInW(p, List, @l) then + Inc(p, l) + else + begin + Result := IntPtr(p) - IntPtr(ps); + Break; + end; + end; + end; +end; + +function SkipSpaceA(var p: PQCharA): Integer; +var + ps: PQCharA; + l: Integer; +begin + ps := p; + while p^ <> 0 do + begin + if IsSpaceA(p, @l) then + Inc(p, l) + else + Break; + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipSpaceU(var p: PQCharA): Integer; +var + ps: PQCharA; + l: Integer; +begin + ps := p; + while p^ <> 0 do + begin + if IsSpaceU(p, @l) then + Inc(p, l) + else + Break; + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipSpaceW(var p: PQCharW): Integer; +var + ps: PQCharW; + l: Integer; +begin + ps := p; + while p^ <> #0 do + begin + if IsSpaceW(p, @l) then + Inc(p, l) + else + Break; + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +// һ,#10Ϊнβ +function SkipLineA(var p: PQCharA): Integer; +var + ps: PQCharA; +begin + ps := p; + while p^ <> 0 do + begin + if p^ = 10 then + begin + Inc(p); + Break; + end + else + Inc(p); + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipLineU(var p: PQCharA): Integer; +begin + Result := SkipLineA(p); +end; + +function SkipLineW(var p: PQCharW): Integer; +var + ps: PQCharW; +begin + ps := p; + while p^ <> #0 do + begin + if p^ = #10 then + begin + Inc(p); + Break; + end + else + Inc(p); + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function StrPosA(Start, Current: PQCharA; var ACol, ARow: Integer): PQCharA; +begin + ACol := 1; + ARow := 1; + Result := Start; + while IntPtr(Start) < IntPtr(Current) do + begin + if Start^ = 10 then + begin + Inc(ARow); + ACol := 1; + Inc(Start); + Result := Start; + end + else + begin + Inc(Start, CharSizeA(Start)); + Inc(ACol); + end; + end; +end; + +function StrPosU(Start, Current: PQCharA; var ACol, ARow: Integer): PQCharA; +begin + ACol := 1; + ARow := 1; + Result := Start; + while IntPtr(Start) < IntPtr(Current) do + begin + if Start^ = 10 then + begin + Inc(ARow); + ACol := 1; + Inc(Start); + Result := Start; + end + else + begin + Inc(Start, CharSizeU(Start)); + Inc(ACol); + end; + end; +end; + +function StrPosW(Start, Current: PQCharW; var ACol, ARow: Integer): PQCharW; +begin + ACol := 1; + ARow := 1; + Result := Start; + while Start < Current do + begin + if Start^ = #10 then + begin + Inc(ARow); + ACol := 1; + Inc(Start); + Result := Start; + end + else + begin + Inc(Start, CharSizeW(Start)); + Inc(ACol); + end; + end; +end; + +function DecodeTokenA(var p: PQCharA; ADelimiters: array of QCharA; + AQuoter: QCharA; AIgnoreSpace: Boolean): QStringA; +var + S: PQCharA; + l: Integer; +begin + if AIgnoreSpace then + SkipSpaceA(p); + S := p; + while p^ <> 0 do + begin + if p^ = AQuoter then // õݲ + begin + Inc(p); + while p^ <> 0 do + begin + if p^ = $5C then + begin + Inc(p); + if p^ <> 0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInA(p, ADelimiters, @l) then + Break + else // \",\',"",''ֱת + Inc(p); + end; + l := IntPtr(p) - IntPtr(S); + Result.Length := l; + Move(S^, PQCharA(Result)^, l); + while CharInA(p, ADelimiters, @l) do + Inc(p, l); +end; + +function DecodeTokenU(var p: PQCharA; ADelimiters: array of QCharA; + AQuoter: QCharA; AIgnoreSpace: Boolean): QStringA; +var + S: PQCharA; + l: Integer; +begin + if AIgnoreSpace then + SkipSpaceU(p); + S := p; + while p^ <> 0 do + begin + if p^ = AQuoter then // õݲ + begin + Inc(p); + while p^ <> 0 do + begin + if p^ = $5C then + begin + Inc(p); + if p^ <> 0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInU(p, ADelimiters, @l) then + Break + else // \",\',"",''ֱת + Inc(p); + end; + l := IntPtr(p) - IntPtr(S); + Result.Length := l; + Move(S^, PQCharA(Result)^, l); + while CharInU(p, ADelimiters, @l) do + Inc(p, l); +end; + +function DecodeTokenW(var p: PQCharW; ADelimiters: array of QCharW; + AQuoter: QCharW; AIgnoreSpace: Boolean; ASkipDelimiters: Boolean): QStringW; +var + S: PQCharW; + l: Integer; +begin + if AIgnoreSpace then + SkipSpaceW(p); + S := p; + while p^ <> #0 do + begin + if p^ = AQuoter then // õݲ + begin + Inc(p); + while p^ <> #0 do + begin + if p^ = #$5C then + begin + Inc(p); + if p^ <> #0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInW(p, ADelimiters, @l) then + Break + else // \",\',"",''ֱת + Inc(p); + end; + l := p - S; + SetLength(Result, l); + Move(S^, PQCharW(Result)^, l shl 1); + if ASkipDelimiters then + begin + while CharInW(p, ADelimiters, @l) do + Inc(p, l); + end; + if AIgnoreSpace then + SkipSpaceW(p); +end; + +function DecodeTokenW(var p: PQCharW; ADelimiters: PQCharW; AQuoter: QCharW; + AIgnoreSpace: Boolean; ASkipDelimiters: Boolean): QStringW; +var + S: PQCharW; + l: Integer; +begin + if AIgnoreSpace then + SkipSpaceW(p); + S := p; + while p^ <> #0 do + begin + if p^ = AQuoter then // õݲ + begin + Inc(p); + while p^ <> #0 do + begin + if p^ = #$5C then + begin + Inc(p); + if p^ <> #0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInW(p, ADelimiters, @l) then + Break + else // \",\',"",''ֱת + Inc(p); + end; + l := p - S; + SetLength(Result, l); + Move(S^, PQCharW(Result)^, l shl 1); + if ASkipDelimiters then + begin + while CharInW(p, ADelimiters, @l) do + Inc(p, l); + end; + if AIgnoreSpace then + SkipSpaceW(p); +end; + +function DecodeTokenW(var S: QStringW; ADelimiters: PQCharW; AQuoter: QCharW; + AIgnoreCase, ARemove, ASkipDelimiters: Boolean): QStringW; +var + p: PQCharW; +begin + p := PQCharW(S); + Result := DecodeTokenW(p, ADelimiters, AQuoter, AIgnoreCase); + if ARemove then + S := StrDupX(p, Length(S) - (p - PQCharW(S))); +end; + +function SplitTokenW(AList: TStrings; p: PQCharW; ADelimiters: PQCharW; + AQuoter: QCharW; AIgnoreSpace: Boolean): Integer; +begin + Result := 0; + AList.BeginUpdate; + try + while p^ <> #0 do + begin + AList.Add(DecodeTokenW(p, ADelimiters, AQuoter, AIgnoreSpace, True)); + Inc(Result); + end; + finally + AList.EndUpdate; + end; +end; + +function SplitTokenW(AList: TStrings; const S: QStringW; ADelimiters: PQCharW; + AQuoter: QCharW; AIgnoreSpace: Boolean): Integer; +begin + Result := SplitTokenW(AList, PQCharW(S), ADelimiters, AQuoter, AIgnoreSpace); +end; + +function UpperFirstW(const S: QStringW): QStringW; +var + p, pd: PQCharW; +begin + if Length(S) > 0 then + begin + p := PQCharW(S); + SetLength(Result, Length(S)); + pd := PQCharW(Result); + pd^ := CharUpperW(p^); + Inc(p); + Inc(pd); + while p^ <> #0 do + begin + pd^ := CharLowerW(p^); + Inc(p); + Inc(pd); + end; + end + else + Result := S; +end; + +function DecodeLineA(var p: PQCharA; ASkipEmpty: Boolean): QStringA; +var + ps: PQCharA; +begin + ps := p; + while p^ <> 0 do + begin + if ((p^ = 13) and (PQCharA(IntPtr(p) + 1)^ = 10)) or (p^ = 10) then + begin + if ps = p then + begin + if ASkipEmpty then + begin + if p^ = 13 then + Inc(p, 2) + else + Inc(p); + ps := p; + end + else + begin + Result.Length := 0; + Exit; + end; + end + else + begin + Result.Length := IntPtr(p) - IntPtr(ps); + Move(ps^, PQCharA(Result)^, IntPtr(p) - IntPtr(ps)); + if p^ = 13 then + Inc(p, 2) + else + Inc(p); + Exit; + end; + end + else + Inc(p); + end; + if ps = p then + Result.Length := 0 + else + begin + Result.Length := IntPtr(p) - IntPtr(ps); + Move(ps^, PQCharA(Result)^, IntPtr(p) - IntPtr(ps)); + end; +end; + +function DecodeLineU(var p: PQCharA; ASkipEmpty: Boolean): QStringA; +begin + Result := DecodeLineA(p, ASkipEmpty); + if Result.Length > 0 then + Result.FValue[0] := 1; +end; + +function DecodeLineW(var p: PQCharW; ASkipEmpty: Boolean): QStringW; +var + ps: PQCharW; +begin + ps := p; + while p^ <> #0 do + begin + if ((p[0] = #13) and (p[1] = #10)) or (p[0] = #10) then + begin + if ps = p then + begin + if ASkipEmpty then + begin + if p^ = #13 then + Inc(p, 2) + else + Inc(p); + ps := p; + end + else + begin + SetLength(Result, 0); + Exit; + end; + end + else + begin + SetLength(Result, p - ps); + Move(ps^, PQCharW(Result)^, IntPtr(p) - IntPtr(ps)); + if p^ = #13 then + Inc(p, 2) + else + Inc(p); + Exit; + end; + end + else + Inc(p); + end; + if ps = p then + SetLength(Result, 0) + else + begin + SetLength(Result, p - ps); + Move(ps^, PQCharW(Result)^, IntPtr(p) - IntPtr(ps)); + end; +end; + +function LeftStrW(const S: QStringW; AMaxCount: Integer; ACheckExt: Boolean) + : QStringW; +var + ps, p: PQCharW; + l: Integer; +begin + l := Length(S); + if AMaxCount > l then + Result := S + else if AMaxCount > 0 then + begin + ps := PQCharW(S); + if ACheckExt then + begin + p := ps; + while (p^ <> #0) and (AMaxCount > 0) do + begin + if (p^ >= #$D800) and (p^ <= #$DBFF) then + begin + Inc(p); + if (p^ >= #$DC00) and (p^ <= #$DFFF) then + Inc(p); + // else ЧչַȻѭ + end + else + Inc(p); + Dec(AMaxCount); + end; + l := p - ps; + SetLength(Result, l); + Move(ps^, PQCharW(Result)^, l shl 1); + end + else + begin + SetLength(Result, AMaxCount); + Move(ps^, PQCharW(Result)^, AMaxCount shl 1); + end; + end + else + SetLength(Result, 0); +end; + +function RightStrW(const S: QStringW; AMaxCount: Integer; ACheckExt: Boolean) + : QStringW; +var + ps, p: PQCharW; + l: Integer; +begin + l := Length(S); + if AMaxCount > l then + Result := S + else if AMaxCount > 0 then + begin + ps := PQCharW(S); + if ACheckExt then + begin + p := ps + l - 1; + while (p > ps) and (AMaxCount > 0) do + begin + if (p^ >= #$DC00) and (p^ <= #$DFFF) then + begin + Dec(p); + if (p^ >= #$D800) and (p^ <= #$DBFF) then + Dec(p) + // else ЧչַȻѭ + end + else + Dec(p); + Dec(AMaxCount); + end; + Inc(p); + l := l - (p - ps); + SetLength(Result, l); + Move(p^, PQCharW(Result)^, l shl 1); + end + else + begin + Inc(ps, l - AMaxCount); + SetLength(Result, AMaxCount); + Move(ps^, PQCharW(Result)^, AMaxCount shl 1); + end; + end + else + SetLength(Result, 0); +end; + +function StrBetween(var S: PQCharW; AStartTag, AEndTag: QStringW; + AIgnoreCase: Boolean): QStringW; +var + ps, pe: PQCharW; + l: Integer; +begin + if AIgnoreCase then + begin + ps := StrIStrW(S, PQCharW(AStartTag)); + if ps <> nil then + begin + Inc(ps, Length(AStartTag)); + pe := StrIStrW(ps, PQCharW(AEndTag)); + if pe <> nil then + begin + l := pe - ps; + SetLength(Result, l); + Move(ps^, PQCharW(Result)^, l shl 1); + Inc(pe, Length(AEndTag)); + S := pe; + end + else + SetLength(Result, 0); + end + else + SetLength(Result, 0); + end + else + begin + ps := StrStrW(S, PQCharW(AStartTag)); + if ps <> nil then + begin + Inc(ps, Length(AStartTag)); + pe := StrStrW(ps, PQCharW(AEndTag)); + if pe <> nil then + begin + l := pe - ps; + SetLength(Result, l); + Move(ps, PQCharW(Result)^, l shl 1); + Inc(pe, Length(AEndTag)); + S := pe; + end + else + SetLength(Result, 0); + end + else + SetLength(Result, 0); + end; +end; + +function TokenWithIndex(var S: PQCharW; AIndex: Integer; ADelimiters: PQCharW; + AQuoter: QCharW; AIgnoreSapce: Boolean): QStringW; +begin + while (AIndex >= 0) and (S^ <> #0) do + begin + if AIndex <> 0 then + DecodeTokenW(S, ADelimiters, AQuoter, AIgnoreSapce) + else + begin + Result := DecodeTokenW(S, ADelimiters, AQuoter, AIgnoreSapce); + Break; + end; + Dec(AIndex); + end; +end; + +function SkipUntilA(var p: PQCharA; AExpects: array of QCharA; + AQuoter: QCharA): Integer; +var + ps: PQCharA; +begin + ps := p; + while p^ <> 0 do + begin + if (p^ = AQuoter) then + begin + Inc(p); + while p^ <> 0 do + begin + if p^ = $5C then + begin + Inc(p); + if p^ <> 0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInA(p, AExpects) then + Break + else + Inc(p, CharSizeA(p)); + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipUntilU(var p: PQCharA; AExpects: array of QCharA; + AQuoter: QCharA): Integer; +var + ps: PQCharA; +begin + ps := p; + while p^ <> 0 do + begin + if (p^ = AQuoter) then + begin + Inc(p); + while p^ <> 0 do + begin + if p^ = $5C then + begin + Inc(p); + if p^ <> 0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInU(p, AExpects) then + Break + else + Inc(p, CharSizeU(p)); + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipUntilW(var p: PQCharW; AExpects: array of QCharW; + AQuoter: QCharW): Integer; +var + ps: PQCharW; +begin + ps := p; + while p^ <> #0 do + begin + if (p^ = AQuoter) then + begin + Inc(p); + while p^ <> #0 do + begin + if p^ = #$5C then + begin + Inc(p); + if p^ <> #0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInW(p, AExpects) then + Break + else + Inc(p, CharSizeW(p)); + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipUntilW(var p: PQCharW; AExpects: PQCharW; AQuoter: QCharW) + : Integer; +var + ps: PQCharW; +begin + ps := p; + while p^ <> #0 do + begin + if (p^ = AQuoter) then + begin + Inc(p); + while p^ <> #0 do + begin + if p^ = #$5C then + begin + Inc(p); + if p^ <> #0 then + Inc(p); + end + else if p^ = AQuoter then + begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end + else + Inc(p); + end; + end + else if CharInW(p, AExpects) then + Break + else + Inc(p, CharSizeW(p)); + end; + Result := (IntPtr(p) - IntPtr(ps)) shr 1; +end; + +function CharUpperA(c: QCharA): QCharA; +begin + if (c >= $61) and (c <= $7A) then + Result := c xor $20 + else + Result := c; +end; + +function CharUpperW(c: QCharW): QCharW; +begin + if (c >= #$61) and (c <= #$7A) then + Result := QCharW(PWord(@c)^ xor $20) + else + Result := c; +end; + +function CharLowerA(c: QCharA): QCharA; +begin + if (c >= Ord('A')) and (c <= Ord('Z')) then + Result := Ord('a') + Ord(c) - Ord('A') + else + Result := c; +end; + +function CharLowerW(c: QCharW): QCharW; +begin + if (c >= 'A') and (c <= 'Z') then + Result := QCharW(Ord('a') + Ord(c) - Ord('A')) + else + Result := c; +end; + +function StartWithA(S, startby: PQCharA; AIgnoreCase: Boolean): Boolean; +begin + while (S^ <> 0) and (startby^ <> 0) do + begin + if AIgnoreCase then + begin + if CharUpperA(S^) <> CharUpperA(startby^) then + Break; + end + else if S^ <> startby^ then + Break; + Inc(S); + Inc(startby); + end; + Result := (startby^ = 0); +end; + +function StartWithU(S, startby: PQCharA; AIgnoreCase: Boolean): Boolean; +begin + Result := StartWithA(S, startby, AIgnoreCase); +end; + +function StartWithW(S, startby: PQCharW; AIgnoreCase: Boolean): Boolean; +begin + if AIgnoreCase then + begin + while (S^ <> #0) and (startby^ <> #0) do + begin + if CharUpperW(S^) <> CharUpperW(startby^) then + Break; + Inc(S); + Inc(startby); + end; + end + else + begin + while (S^ <> #0) and (S^ = startby^) do + begin + Inc(S); + Inc(startby); + end; + end; + Result := (startby^ = #0); +end; + +function EndWithA(const S, endby: QStringA; AIgnoreCase: Boolean): Boolean; +var + p: PQCharA; +begin + if S.Length < endby.Length then + Result := False + else + begin + p := PQCharA(S); + Inc(p, S.Length - endby.Length); + if AIgnoreCase then + Result := (StrIStrA(p, PQCharA(endby)) = p) + else + Result := (StrStrA(p, PQCharA(endby)) = p); + end; +end; + +function EndWithU(const S, endby: QStringA; AIgnoreCase: Boolean): Boolean; +begin + Result := EndWithA(S, endby, AIgnoreCase); +end; + +function EndWithW(const S, endby: QStringW; AIgnoreCase: Boolean): Boolean; +var + p: PQCharW; +begin + if System.Length(S) < System.Length(endby) then + Result := False + else + begin + p := PQCharW(S); + Inc(p, System.Length(S) - System.Length(endby)); + if AIgnoreCase then + Result := (StrIStrW(p, PQCharW(endby)) = p) + else + Result := (StrStrW(p, PQCharW(endby)) = p); + end; +end; + +function SameCharsA(s1, s2: PQCharA; AIgnoreCase: Boolean): Integer; +begin + Result := 0; + if (s1 <> nil) and (s2 <> nil) then + begin + if AIgnoreCase then + begin + while (s1^ <> 0) and (s2^ <> 0) and + ((s1^ = s2^) or (CharUpperA(s1^) = CharUpperA(s2^))) do + Inc(Result); + end + else + begin + while (s1^ <> 0) and (s2^ <> 0) and (s1^ = s2^) do + Inc(Result); + end; + end; +end; + +function SameCharsU(s1, s2: PQCharA; AIgnoreCase: Boolean): Integer; + function CompareSubSeq: Boolean; + var + ACharSize1, ACharSize2: Integer; + begin + ACharSize1 := CharSizeU(s1) - 1; + ACharSize2 := CharSizeU(s2) - 1; + Result := ACharSize1 = ACharSize2; + if Result then + begin + Inc(s1); + Inc(s2); + while (ACharSize1 > 0) and (s1^ = s2^) do + begin + Inc(s1); + Inc(s2); + end; + Result := (ACharSize1 = 0); + end; + end; + +begin + Result := 0; + if (s1 <> nil) and (s2 <> nil) then + begin + if AIgnoreCase then + begin + while (s1^ <> 0) and (s2^ <> 0) and + ((s1^ = s2^) or (CharUpperA(s1^) = CharUpperA(s2^))) do + begin + if CompareSubSeq then + Inc(Result) + else + Break; + end; + end + else + begin + while (s1^ <> 0) and (s2^ <> 0) and (s1^ = s2^) do + begin + if CompareSubSeq then + Inc(Result) + else + Break; + end; + end; + end; +end; + +function SameCharsW(s1, s2: PQCharW; AIgnoreCase: Boolean): Integer; +begin + Result := 0; + if (s1 <> nil) and (s2 <> nil) then + begin + if AIgnoreCase then + begin + while (s1^ <> #0) and (s2^ <> #0) and + ((s1^ = s2^) or (CharUpperW(s1^) = CharUpperW(s2^))) do + Inc(Result); + end + else + begin + while (s1^ <> #0) and (s2^ <> #0) and (s1^ = s2^) do + Inc(Result); + end; + end; +end; + +function DetectTextEncoding(const p: Pointer; l: Integer; var b: Boolean) + : TTextEncoding; +var + pAnsi: PByte; + pWide: PWideChar; + I, AUtf8CharSize: Integer; +const + NoUtf8Char: array [0 .. 3] of Byte = ($C1, $AA, $CD, $A8); // ANSIͨ + function IsUtf8Order(var ACharSize: Integer): Boolean; + var + I: Integer; + ps: PByte; + const + Utf8Masks: array [0 .. 4] of Byte = ($C0, $E0, $F0, $F8, $FC); + begin + ps := pAnsi; + ACharSize := CharSizeU(PQCharA(ps)); + Result := False; + if ACharSize > 1 then + begin + I := ACharSize - 2; + if ((Utf8Masks[I] and ps^) = Utf8Masks[I]) then + begin + Inc(ps); + Result := True; + for I := 1 to ACharSize - 1 do + begin + if (ps^ and $80) <> $80 then + begin + Result := False; + Break; + end; + Inc(ps); + end; + end; + end; + end; + +begin + Result := teAnsi; + b := False; + if l >= 2 then + begin + pAnsi := PByte(p); + pWide := PWideChar(p); + b := True; + if pWide^ = #$FEFF then + Result := teUnicode16LE + else if pWide^ = #$FFFE then + Result := teUnicode16BE + else if l >= 3 then + begin + if (pAnsi^ = $EF) and (PByte(IntPtr(pAnsi) + 1)^ = $BB) and + (PByte(IntPtr(pAnsi) + 2)^ = $BF) then // UTF-8 + Result := teUTF8 + else // ַǷзUFT-8ַ11... + begin + b := False; + Result := teUnknown; // ΪUTF8룬ȻǷвUTF-8 + I := 0; + Dec(l, 2); + while I <= l do + begin + if (pAnsi^ and $80) <> 0 then // λΪ1 + begin + if (l - I >= 4) then + begin + if CompareMem(pAnsi, @NoUtf8Char[0], 4) then + // ͨԵUTF-8ж + begin + Inc(pAnsi, 4); + Inc(I, 4); + Result := teAnsi; + continue; + end; + end; + if IsUtf8Order(AUtf8CharSize) then + begin + Inc(pAnsi, AUtf8CharSize); + Result := teUTF8; + Break; + end + else + begin + Result := teAnsi; + Break; + end; + end + else + begin + if pAnsi^ = 0 then // 00 xx (xx<128) λǰBE + begin + if PByte(IntPtr(pAnsi) + 1)^ < 128 then + begin + Result := teUnicode16BE; + Break; + end; + end + else if PByte(IntPtr(pAnsi) + 1)^ = 0 then // xx 00 λǰLE + begin + Result := teUnicode16LE; + Break; + end; + Inc(pAnsi); + Inc(I); + end; + if Result = teUnknown then + Result := teAnsi; + end; + end; + end; + end; +end; + +function LoadTextA(AFileName: String; AEncoding: TTextEncoding): QStringA; +var + AStream: TStream; +begin + AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); + try + Result := LoadTextA(AStream, AEncoding); + finally + AStream.Free; + end; +end; + +procedure ExchangeByteOrder(p: PQCharA; l: Integer); +var + pe: PQCharA; + c: QCharA; +begin + pe := p; + Inc(pe, l); + while IntPtr(p) < IntPtr(pe) do + begin + c := p^; + p^ := PQCharA(IntPtr(p) + 1)^; + PQCharA(IntPtr(p) + 1)^ := c; + Inc(p, 2); + end; +end; + +function ExchangeByteOrder(V: Smallint): Smallint; +var + pv: array [0 .. 1] of Byte absolute V; + pd: array [0 .. 1] of Byte absolute Result; +begin + pd[0] := pv[1]; + pd[1] := pv[0]; +end; + +function ExchangeByteOrder(V: Word): Word; +var + pv: array [0 .. 1] of Byte absolute V; + pd: array [0 .. 1] of Byte absolute Result; +begin + pd[0] := pv[1]; + pd[1] := pv[0]; +end; + +function ExchangeByteOrder(V: Integer): Integer; +var + pv: array [0 .. 3] of Byte absolute V; + pd: array [0 .. 3] of Byte absolute Result; +begin + pd[0] := pv[3]; + pd[1] := pv[2]; + pd[2] := pv[1]; + pd[3] := pv[0]; +end; + +function ExchangeByteOrder(V: Cardinal): Cardinal; +var + pv: array [0 .. 3] of Byte absolute V; + pd: array [0 .. 3] of Byte absolute Result; +begin + pd[0] := pv[3]; + pd[1] := pv[2]; + pd[2] := pv[1]; + pd[3] := pv[0]; +end; + +function ExchangeByteOrder(V: Int64): Int64; +var + pv: array [0 .. 7] of Byte absolute V; + pd: array [0 .. 7] of Byte absolute Result; +begin + pd[0] := pv[7]; + pd[1] := pv[6]; + pd[2] := pv[5]; + pd[3] := pv[4]; + pd[4] := pv[3]; + pd[5] := pv[2]; + pd[6] := pv[1]; + pd[7] := pv[0]; +end; + +function ExchangeByteOrder(V: Single): Single; +var + pv: array [0 .. 3] of Byte absolute V; + pd: array [0 .. 3] of Byte absolute Result; +begin + pd[0] := pv[3]; + pd[1] := pv[2]; + pd[2] := pv[1]; + pd[3] := pv[0]; +end; + +function ExchangeByteOrder(V: Double): Double; +var + pv: array [0 .. 7] of Byte absolute V; + pd: array [0 .. 7] of Byte absolute Result; +begin + pd[0] := pv[7]; + pd[1] := pv[6]; + pd[2] := pv[5]; + pd[3] := pv[4]; + pd[4] := pv[3]; + pd[5] := pv[2]; + pd[6] := pv[1]; + pd[7] := pv[0]; +end; + +function LoadTextA(AStream: TStream; AEncoding: TTextEncoding): QStringA; +var + ASize: Integer; + ABuffer: TBytes; + ABomExists: Boolean; +begin + ASize := AStream.Size - AStream.Position; + if ASize > 0 then + begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + if AEncoding in [teUnknown, teAuto] then + AEncoding := DetectTextEncoding(@ABuffer[0], ASize, ABomExists) + else if ASize >= 2 then + begin + case AEncoding of + teUnicode16LE: + ABomExists := (ABuffer[0] = $FF) and (ABuffer[1] = $FE); + teUnicode16BE: + ABomExists := (ABuffer[1] = $FE) and (ABuffer[1] = $FF); + teUTF8: + begin + if ASize >= 3 then + ABomExists := (ABuffer[0] = $EF) and (ABuffer[1] = $BB) and + (ABuffer[2] = $BF) + else + ABomExists := False; + end; + end; + end + else + ABomExists := False; + if AEncoding = teAnsi then + Result := ABuffer + else if AEncoding = teUTF8 then + begin + if ABomExists then + begin + if ASize > 3 then + Result := AnsiEncode(Utf8Decode(@ABuffer[3], ASize - 3)) + else + Result.Length := 0; + end + else + Result := AnsiEncode(Utf8Decode(@ABuffer[0], ASize)); + end + else + begin + if AEncoding = teUnicode16BE then + ExchangeByteOrder(@ABuffer[0], ASize); + if ABomExists then + Result := AnsiEncode(PQCharW(@ABuffer[2]), (ASize - 2) shr 1) + else + Result := AnsiEncode(PQCharW(@ABuffer[0]), ASize shr 1); + end; + end + else + Result.Length := 0; +end; + +function LoadTextU(AFileName: String; AEncoding: TTextEncoding): QStringA; +var + AStream: TStream; +begin + AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); + try + Result := LoadTextU(AStream, AEncoding); + finally + AStream.Free; + end; +end; + +function LoadTextU(AStream: TStream; AEncoding: TTextEncoding): QStringA; +var + ASize: Integer; + ABuffer: TBytes; + ABomExists: Boolean; +begin + ASize := AStream.Size - AStream.Position; + if ASize > 0 then + begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + if AEncoding in [teUnknown, teAuto] then + AEncoding := DetectTextEncoding(@ABuffer[0], ASize, ABomExists) + else if ASize >= 2 then + begin + case AEncoding of + teUnicode16LE: + ABomExists := (ABuffer[0] = $FF) and (ABuffer[1] = $FE); + teUnicode16BE: + ABomExists := (ABuffer[1] = $FE) and (ABuffer[1] = $FF); + teUTF8: + begin + if ASize > 3 then + ABomExists := (ABuffer[0] = $EF) and (ABuffer[1] = $BB) and + (ABuffer[2] = $BF) + else + ABomExists := False; + end; + end; + end + else + ABomExists := False; + if AEncoding = teAnsi then + Result := qstring.Utf8Encode(AnsiDecode(@ABuffer[0], ASize)) + else if AEncoding = teUTF8 then + begin + if ABomExists then + begin + Dec(ASize, 3); + Result.From(@ABuffer[0], 3, ASize); + end + else + Result := ABuffer; + if ASize > 0 then + Result.FValue[0] := 1; // UTF-8 + end + else + begin + if AEncoding = teUnicode16BE then + ExchangeByteOrder(@ABuffer[0], ASize); + if ABomExists then + Result := qstring.Utf8Encode(PQCharW(@ABuffer[2]), (ASize - 2) shr 1) + else + Result := qstring.Utf8Encode(PQCharW(@ABuffer[0]), ASize shr 1); + end; + end + else + begin + Result.Length := 0; + Result.FValue[0] := 1; + end; +end; + +function LoadTextW(AFileName: String; AEncoding: TTextEncoding): QStringW; +var + AStream: TStream; +begin + AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); + try + Result := LoadTextW(AStream, AEncoding); + finally + AStream.Free; + end; +end; + +function DecodeText(p: Pointer; ASize: Integer; AEncoding: TTextEncoding) + : QStringW; +var + ABomExists: Boolean; + pb: PByte; + pe: PQCharA; + function ByteOf(AOffset: Integer): Byte; + begin + Result := PByte(IntPtr(pb) + AOffset)^; + end; + +begin + pb := p; + if ASize >= 2 then + begin + // Ƿָ룬ǿƼBOMͷڱָ + if (ByteOf(0) = $FF) and (ByteOf(1) = $FE) then + begin + AEncoding := teUnicode16LE; + Inc(pb, 2); + Dec(ASize, 2); + end + else if (ByteOf(0) = $FE) and (ByteOf(1) = $FF) then + begin + AEncoding := teUnicode16BE; + Inc(pb, 2); + Dec(ASize, 2); + end + else if (ASize > 2) and (ByteOf(0) = $EF) and (ByteOf(1) = $BB) and + (ByteOf(2) = $BF) then + begin + AEncoding := teUTF8; + Inc(pb, 3); + Dec(ASize, 3); + end + else if AEncoding in [teUnknown, teAuto] then // No BOM + AEncoding := DetectTextEncoding(pb, ASize, ABomExists); + if AEncoding = teAnsi then + Result := AnsiDecode(PQCharA(pb), ASize) + else if AEncoding = teUTF8 then + begin + if not Utf8Decode(PQCharA(pb), ASize, Result, pe) then + Result := AnsiDecode(PQCharA(pb), ASize); + end + else + begin + if AEncoding = teUnicode16BE then + ExchangeByteOrder(PQCharA(pb), ASize); + SetLength(Result, ASize shr 1); + Move(pb^, PQCharW(Result)^, ASize); + end; + end + else if ASize > 0 then + Result := WideChar(pb^) + else + SetLength(Result, 0); +end; + +function LoadTextW(AStream: TStream; AEncoding: TTextEncoding) + : QStringW; overload; +var + ASize: Integer; + ABuffer: TBytes; +begin + ASize := AStream.Size - AStream.Position; + if ASize > 0 then + begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + Result := DecodeText(@ABuffer[0], ASize, AEncoding); + end + else + SetLength(Result, 0); +end; + +procedure SaveTextA(AFileName: String; const S: QStringA); +var + AStream: TFileStream; +begin + AStream := TFileStream.Create(AFileName, fmCreate); + try + SaveTextA(AStream, S); + finally + AStream.Free; + end; +end; + +procedure SaveTextA(AStream: TStream; const S: QStringA); + procedure Utf8Save; + var + t: QStringA; + begin + t := AnsiEncode(Utf8Decode(S)); + AStream.WriteBuffer(PQCharA(t)^, t.Length); + end; + +begin + if not S.IsUtf8 then + AStream.WriteBuffer(PQCharA(S)^, S.Length) + else + Utf8Save; +end; + +procedure SaveTextU(AFileName: String; const S: QStringA; AWriteBom: Boolean); +var + AStream: TFileStream; +begin + AStream := TFileStream.Create(AFileName, fmCreate); + try + SaveTextU(AStream, S, AWriteBom); + finally + AStream.Free; + end; +end; + +procedure SaveTextU(AFileName: String; const S: QStringW; + AWriteBom: Boolean); overload; +begin + SaveTextU(AFileName, qstring.Utf8Encode(S), AWriteBom); +end; + +procedure SaveTextU(AStream: TStream; const S: QStringA; AWriteBom: Boolean); + procedure WriteBom; + var + ABom: TBytes; + begin + SetLength(ABom, 3); + ABom[0] := $EF; + ABom[1] := $BB; + ABom[2] := $BF; + AStream.WriteBuffer(ABom[0], 3); + end; + procedure SaveAnsi; + var + t: QStringA; + begin + t := qstring.Utf8Encode(AnsiDecode(S)); + AStream.WriteBuffer(PQCharA(t)^, t.Length); + end; + +begin + if AWriteBom then + WriteBom; + if S.IsUtf8 then + AStream.WriteBuffer(PQCharA(S)^, S.Length) + else + SaveAnsi; +end; + +procedure SaveTextU(AStream: TStream; const S: QStringW; + AWriteBom: Boolean); overload; +begin + SaveTextU(AStream, qstring.Utf8Encode(S), AWriteBom); +end; + +procedure SaveTextW(AFileName: String; const S: QStringW; AWriteBom: Boolean); +var + AStream: TFileStream; +begin + AStream := TFileStream.Create(AFileName, fmCreate); + try + SaveTextW(AStream, S, AWriteBom); + finally + AStream.Free; + end; +end; + +procedure SaveTextW(AStream: TStream; const S: QStringW; AWriteBom: Boolean); + procedure WriteBom; + var + bom: Word; + begin + bom := $FEFF; + AStream.WriteBuffer(bom, 2); + end; + +begin + if AWriteBom then + WriteBom; + AStream.WriteBuffer(PQCharW(S)^, System.Length(S) shl 1); +end; + +procedure SaveTextWBE(AStream: TStream; const S: QStringW; AWriteBom: Boolean); +var + pw, pe: PWord; + w: Word; + ABuilder: TQStringCatHelperW; +begin + pw := PWord(PQCharW(S)); + pe := pw; + Inc(pe, Length(S)); + ABuilder := TQStringCatHelperW.Create(IntPtr(pe) - IntPtr(pw)); + try + while IntPtr(pw) < IntPtr(pe) do + begin + w := (pw^ shr 8) or (pw^ shl 8); + ABuilder.Cat(@w, 1); + Inc(pw); + end; + if AWriteBom then + AStream.WriteBuffer(#$FE#$FF, 2); + AStream.WriteBuffer(ABuilder.FStart^, Length(S) shl 1); + finally + FreeObject(ABuilder); + end; +end; + +function StrStrA(s1, s2: PQCharA): PQCharA; + function DoSearch: PQCharA; + var + ps1, ps2: PQCharA; + begin + ps1 := s1; + ps2 := s2; + Inc(ps1); + Inc(ps2); + while ps2^ <> 0 do + begin + if ps1^ = ps2^ then + begin + Inc(ps1); + Inc(ps2); + end + else + Break; + end; + if ps2^ = 0 then + Result := s1 + else + Result := nil; + end; + +begin +{$IFDEF MSWINDOWS} + if Assigned(VCStrStr) then + begin + Result := VCStrStr(s1, s2); + Exit; + end; +{$ENDIF} + Result := nil; + if (s1 <> nil) and (s2 <> nil) then + begin + while s1^ <> 0 do + begin + if s1^ = s2^ then + begin + Result := DoSearch; + if Result <> nil then + Exit; + end; + Inc(s1); + end; + end; +end; + +function StrIStrA(s1, s2: PQCharA): PQCharA; + function DoSearch: PQCharA; + var + ps1, ps2: PQCharA; + begin + ps1 := s1; + ps2 := s2; + Inc(ps1); + Inc(ps2); + while ps2^ <> 0 do + begin + if CharUpperA(ps1^) = ps2^ then + begin + Inc(ps1); + Inc(ps2); + end + else + Break; + end; + if ps2^ = 0 then + Result := s1 + else + Result := nil; + end; + +begin + Result := nil; + if (s1 <> nil) and (s2 <> nil) then + begin + while s1^ <> 0 do + begin + if s1^ = s2^ then + begin + Result := DoSearch; + if Result <> nil then + Exit; + end; + Inc(s1); + end; + end; +end; + +function StrStrU(s1, s2: PQCharA): PQCharA; +begin + Result := StrStrA(s1, s2); +end; + +function StrIStrU(s1, s2: PQCharA): PQCharA; +begin + Result := StrIStrA(s1, s2); +end; + +function StrStrW(s1, s2: PQCharW): PQCharW; +var + I: Integer; +begin +{$IFDEF MSWINDOWS} + if Assigned(VCStrStrW) then + begin + Result := VCStrStrW(s1, s2); + Exit; + end; +{$ENDIF} + if (s2 = nil) or (s2^ = #0) then + Result := s1 + else + begin + Result := nil; + while s1^ <> #0 do + begin + if s1^ = s2^ then + begin + I := 1; + while s2[I] <> #0 do + begin + if s1[I] = s2[I] then + Inc(I) + else + Break; + end; + if s2[I] = #0 then + begin + Result := s1; + Break; + end; + end; + Inc(s1); + end; + end; +end; + +function StrIStrW(s1, s2: PQCharW): PQCharW; +var + I: Integer; + ws2: QStringW; +begin + Result := nil; + if (s1 = nil) or (s2 = nil) then + Exit; + ws2 := UpperCase(s2); + s2 := PWideChar(ws2); + while s1^ <> #0 do + begin + if CharUpperW(s1^) = s2^ then + begin + I := 1; + while s2[I] <> #0 do + begin + if CharUpperW(s1[I]) = s2[I] then + Inc(I) + else + Break; + end; + if s2[I] = #0 then + begin + Result := s1; + Break; + end; + end; + Inc(s1); + end; +end; + +function PosW(sub, S: PQCharW; AIgnoreCase: Boolean; + AStartPos: Integer): Integer; +begin + if AStartPos > 0 then + Inc(S, AStartPos - 1); + if AIgnoreCase then + sub := StrIStrW(S, sub) + else + sub := StrStrW(S, sub); + if Assigned(sub) then + Result := ((IntPtr(sub) - IntPtr(S)) shr 1) + 1 + else + Result := 0; +end; + +function PosW(sub, S: QStringW; AIgnoreCase: Boolean; AStartPos: Integer) + : Integer; overload; +begin + Result := PosW(PQCharW(sub), PQCharW(S), AIgnoreCase); +end; + +function StrDupX(const S: PQCharW; ACount: Integer): QStringW; +begin + SetLength(Result, ACount); + Move(S^, PQCharW(Result)^, ACount shl 1); +end; + +function StrDupW(const S: PQCharW; AOffset: Integer; const ACount: Integer) + : QStringW; +var + c, ACharSize: Integer; + p, pds, pd: PQCharW; +begin + c := 0; + p := S + AOffset; + SetLength(Result, 16384); + pd := PQCharW(Result); + pds := pd; + while (p^ <> #0) and (c < ACount) do + begin + ACharSize := CharSizeW(p); + AOffset := pd - pds; + if AOffset + ACharSize = Length(Result) then + begin + SetLength(Result, Length(Result) shl 1); + pds := PQCharW(Result); + pd := pds + AOffset; + end; + Inc(c); + pd^ := p^; + if ACharSize = 2 then + pd[1] := p[1]; + Inc(pd, ACharSize); + Inc(p, ACharSize); + end; + SetLength(Result, pd - pds); +end; + +function StrCmpA(const s1, s2: PQCharA; AIgnoreCase: Boolean): Integer; +var + p1, p2: PQCharA; + c1, c2: QCharA; +begin + p1 := s1; + p2 := s2; + if AIgnoreCase then + begin + while (p1^ <> 0) and (p2^ <> 0) do + begin + if p1^ <> p2^ then + begin + if (p1^ >= Ord('a')) and (p1^ <= Ord('z')) then + c1 := p1^ xor $20 + else + c1 := p1^; + if (p2^ >= Ord('a')) and (p2^ <= Ord('z')) then + c2 := p2^ xor $20 + else + c2 := p2^; + Result := Ord(c1) - Ord(c2); + if Result <> 0 then + Exit; + end; + Inc(p1); + Inc(p2); + end; + Result := Ord(p1^) - Ord(p2^); + end + else + begin + while (p1^ <> 0) and (p2^ <> 0) do + begin + Result := p1^ - p2^; + if Result <> 0 then + Exit; + Inc(p1); + Inc(p2); + end; + Result := Ord(p1^) - Ord(p2^); + end; +end; + +function StrCmpW(const s1, s2: PQCharW; AIgnoreCase: Boolean): Integer; +var + p1, p2: PQCharW; + c1, c2: QCharW; +begin + p1 := s1; + p2 := s2; + if AIgnoreCase then + begin + while (p1^ <> #0) and (p2^ <> #0) do + begin + if p1^ <> p2^ then + begin + if (p1^ >= 'a') and (p1^ <= 'z') then + c1 := WideChar(Word(p1^) xor $20) + else + c1 := p1^; + if (p2^ >= 'a') and (p2^ <= 'z') then + c2 := WideChar(Word(p2^) xor $20) + else + c2 := p2^; + Result := Ord(c1) - Ord(c2); + if Result <> 0 then + Exit; + end; + Inc(p1); + Inc(p2); + end; + Result := Ord(p1^) - Ord(p2^); + end + else + begin + while (p1^ <> #0) and (p2^ <> #0) do + begin + if p1^ <> p2^ then + begin + Result := Ord(p1^) - Ord(p2^); + if Result <> 0 then + Exit; + end; + Inc(p1); + Inc(p2); + end; + Result := Ord(p1^) - Ord(p2^); + end; +end; + +function StrNCmpW(const s1, s2: PQCharW; AIgnoreCase: Boolean; + ALength: Integer): Integer; +var + p1, p2: PQCharW; + c1, c2: QCharW; +begin + p1 := s1; + p2 := s2; + if AIgnoreCase then + begin + while ALength > 0 do + begin + if p1^ <> p2^ then + begin + if (p1^ >= 'a') and (p1^ <= 'z') then + c1 := WideChar(Word(p1^) xor $20) + else + c1 := p1^; + if (p2^ >= 'a') and (p2^ <= 'z') then + c2 := WideChar(Word(p2^) xor $20) + else + c2 := p2^; + Result := Ord(c1) - Ord(c2); + if Result <> 0 then + Exit; + end; + Inc(p1); + Inc(p2); + Dec(ALength); + end; + end + else + begin + while ALength > 0 do + begin + if p1^ <> p2^ then + begin + Result := Ord(p1^) - Ord(p2^); + if Result <> 0 then + Exit; + end; + Inc(p1); + Inc(p2); + Dec(ALength); + end; + end; + if ALength = 0 then + Result := 0 + else + Result := Ord(p1^) - Ord(p2^); +end; + +/// ʹȻԹȽַ +/// һҪȽϵַ +/// ڶҪȽϵַ +/// ȽʱǷԴСд +/// ȽʱǷԿհַ +/// ȽϿȫǵΪȫǷźͶӦİǷȵֵ +function NaturalCompareW(s1, s2: PQCharW; + AIgnoreCase, AIgnoreSpace: Boolean): Integer; +var + N1, N2: Int64; + L1, L2: Integer; + c1, c2: QCharW; + function FetchNumeric(p: PQCharW; var AResult: Int64; + var ALen: Integer): Boolean; + var + ps: PQCharW; + const + Full0: WideChar = #65296; // ȫ0 + Full9: WideChar = #65305; // ȫ9 + begin + AResult := 0; + ps := p; + while p^ <> #0 do + begin + while IsSpaceW(p) do + Inc(p); + if (p^ >= '0') and (p^ <= '9') then // + AResult := AResult * 10 + Ord(p^) - Ord('0') + else if (p^ >= Full0) and (p^ <= Full9) then // ȫ + AResult := AResult * 10 + Ord(p^) - Ord(Full0) + else + Break; + Inc(p); + end; + Result := ps <> p; + ALen := (IntPtr(p) - IntPtr(ps)) shr 1; + end; + function FullToHalfChar(c: Word): QCharW; + begin + if (c = $3000) then // ȫǿո'' + Result := QCharW($20) + else if (c >= $FF01) and (c <= $FF5E) then + Result := QCharW($21 + (c - $FF01)) + else + Result := QCharW(c); + end; + function CompareChar: Integer; + begin + if AIgnoreCase then + begin + c1 := CharUpperW(FullToHalfChar(Ord(s1^))); + c2 := CharUpperW(FullToHalfChar(Ord(s2^))); + end + else + begin + c1 := FullToHalfChar(Ord(s1^)); + c2 := FullToHalfChar(Ord(s2^)); + end; + Result := Ord(c1) - Ord(c2); + end; + +begin + if Assigned(s1) then + begin + if not Assigned(s2) then + begin + Result := 1; + Exit; + end; + while (s1^ <> #0) and (s2^ <> #0) do + begin + if s1^ <> s2^ then + begin + while IsSpaceW(s1) do + Inc(s1); + while IsSpaceW(s1) do + Inc(s2); + // Ƿ + L1 := 0; + L2 := 0; + if FetchNumeric(s1, N1, L1) and FetchNumeric(s2, N2, L2) then + begin + Result := N1 - N2; + if Result <> 0 then + Exit + else + begin + Inc(s1, L1); + Inc(s2, L2); + end; + end + else + begin + Result := CompareChar; + if Result = 0 then + begin + Inc(s1); + Inc(s2); + end + else + Exit; + end; + end + else // ȣʹ֣϶Ҳȵ + begin + Inc(s1); + Inc(s2); + end; + end; + Result := CompareChar; + end + else if Assigned(s2) then + Result := -1 + else + Result := 0; +end; + +function IsHexChar(c: QCharW): Boolean; inline; +begin + Result := ((c >= '0') and (c <= '9')) or ((c >= 'a') and (c <= 'f')) or + ((c >= 'A') and (c <= 'F')); +end; + +function HexValue(c: QCharW): Integer; +begin + if (c >= '0') and (c <= '9') then + Result := Ord(c) - Ord('0') + else if (c >= 'a') and (c <= 'f') then + Result := 10 + Ord(c) - Ord('a') + else + Result := 10 + Ord(c) - Ord('A'); +end; + +function HexChar(V: Byte): QCharW; +begin + if V < 10 then + Result := QCharW(V + Ord('0')) + else + Result := QCharW(V - 10 + Ord('A')); +end; + +function TryStrToGuid(const S: QStringW; var AGuid: TGuid): Boolean; +var + p, ps: PQCharW; + l: Int64; +begin + l := Length(S); + p := PWideChar(S); + if (l = 38) or (l = 36) then + begin + // {0BCBAAFF-15E6-451D-A8E8-0D98AC48C364} + ps := p; + if p^ = '{' then + Inc(p); + if (ParseHex(p, l) <> 8) or (p^ <> '-') then + begin + Result := False; + Exit; + end; + AGuid.D1 := l; + Inc(p); + if (ParseHex(p, l) <> 4) or (p^ <> '-') then + begin + Result := False; + Exit; + end; + AGuid.D2 := l; + Inc(p); + if (ParseHex(p, l) <> 4) or (p^ <> '-') then + begin + Result := False; + Exit; + end; + AGuid.D3 := l; + Inc(p); + // 0102-030405060708 + // ʣµ16ַ + l := 0; + while IsHexChar(p[0]) do + begin + if IsHexChar(p[1]) then + begin + AGuid.D4[l] := (HexValue(p[0]) shl 4) + HexValue(p[1]); + Inc(l); + Inc(p, 2); + end + else + begin + Result := False; + Exit; + end; + end; + if (l <> 2) or (p^ <> '-') then + begin + Result := False; + Exit; + end; + Inc(p); + while IsHexChar(p[0]) do + begin + if IsHexChar(p[1]) then + begin + AGuid.D4[l] := (HexValue(p[0]) shl 4) + HexValue(p[1]); + Inc(l); + Inc(p, 2); + end + else + begin + Result := False; + Exit; + end; + end; + if (l = 8) then + begin + if ps^ = '{' then + Result := (p[0] = '}') and (p[1] = #0) + else + Result := (p[0] = #0); + end + else + Result := False; + end + else + Result := False; +end; + +function TryStrToIPV4(const S: QStringW; var AIPV4: +{$IFDEF MSWINDOWS}Integer{$ELSE}Cardinal{$ENDIF}): Boolean; +var + p: PQCharW; + dc: Integer; + pd: PByte; +begin + dc := 0; + AIPV4 := 0; + p := PQCharW(S); + pd := PByte(@AIPV4); + while p^ <> #0 do + begin + if (p^ >= '0') and (p^ <= '9') then + pd^ := pd^ * 10 + Ord(p^) - Ord('0') + else if p^ = '.' then + begin + Inc(dc); + if dc > 3 then + Break; + Inc(pd); + end + else + Break; + Inc(p); + end; + Result := (dc = 3) and (p^ = #0); +end; + +function StringReplaceW(const S, Old, New: QStringW; AFlags: TReplaceFlags) + : QStringW; +var + ps, pse, pds, pr, pd, po, pn: PQCharW; + l, LO, LN, LS, LR: Integer; + AReplaceOnce: Boolean; +begin + LO := Length(Old); + LN := Length(New); + LS := Length(S); + if (LO > 0) and (LS >= LO) then + begin + AReplaceOnce := not(rfReplaceAll in AFlags); + // LO=LN򲻱LR=LSȫ滻Ҳԭ + // LOLNLR=LSһζ滻Ҳԭ + if LO >= LN then + LR := LS + else if AReplaceOnce then + LR := LS + (LN - LO) + else + LR := LS + 1 + LS * LN div LO; + SetLength(Result, LR); + ps := PQCharW(S); + pse := ps + LS; + pd := PQCharW(Result); + pds := pd; + po := PQCharW(Old); + pn := PQCharW(New); + repeat + if rfIgnoreCase in AFlags then + pr := StrIStrW(ps, po) + else + pr := StrStrW(ps, po); + if pr <> nil then + begin + l := IntPtr(pr) - IntPtr(ps); + Move(ps^, pd^, l); + Inc(pd, l shr 1); + Inc(pr, LO); + Move(pn^, pd^, LN shl 1); + Inc(pd, LN); + ps := pr; + end; + until (pr = nil) or AReplaceOnce; + // ʣಿֺϲĿ + l := IntPtr(pse) - IntPtr(ps); + Move(ps^, pd^, l); + Inc(pd, l shr 1); + SetLength(Result, pd - pds); + end + else + Result := S; +end; + +function StringReplaceW(const S: QStringW; const AChar: QCharW; + AFrom, ACount: Integer): QStringW; +var + p, pd: PQCharW; + l: Integer; +begin + l := Length(S); + SetLength(Result, l); + if (l > 0) and (l > AFrom + 1) then + begin + p := PQCharW(S); + pd := PQCharW(Result); + while (p^ <> #0) and (AFrom > 0) do + begin + pd^ := p^; + if (p^ > #$D800) and (p^ <= #$DFFF) then + begin + Inc(pd); + Inc(p); + pd^ := p^; + end; + Inc(p); + Inc(pd); + Dec(AFrom); + end; + while (p^ <> #0) and (ACount > 0) do + begin + pd^ := AChar; + if (p^ > #$D800) and (p^ <= #$DFFF) then + Inc(p); + Inc(p); + Inc(pd); + Dec(ACount); + end; + while p^ <> #0 do + begin + pd^ := p^; + Inc(p); + Inc(pd); + end; + end; +end; + +function StringReplaceWithW(const S, AStartTag, AEndTag, AReplaced: QStringW; + AWithTag, AIgnoreCase: Boolean; AMaxTimes: Cardinal): QStringW; +var + po, pe, pws, pwe, pd, pStart, pEnd, pReplaced: PQCharW; + l, DL, LS, LE, LR: Integer; + StrStrFunc: TStrStrFunction; +begin + l := Length(S); + LS := Length(AStartTag); + LE := Length(AEndTag); + if (l >= LS + LE) and (AMaxTimes > 0) then + begin + LR := Length(AReplaced); + po := PQCharW(S); + pe := po + l; + pStart := PQCharW(AStartTag); + pEnd := PQCharW(AEndTag); + pReplaced := PQCharW(AReplaced); + if LR > l then + SetLength(Result, l * LR) // ÿ滻ΪĿ,Ȼⲻ + else + SetLength(Result, l); + pd := PQCharW(Result); + if AIgnoreCase then + StrStrFunc := StrIStrW + else + StrStrFunc := StrStrW; + repeat + pws := StrStrFunc(po, pStart); + if pws = nil then + begin + DL := (pe - po); + Move(po^, pd^, DL shl 1); + SetLength(Result, pd - PQCharW(Result) + DL); + Exit; + end + else + begin + pwe := StrStrFunc(pws + LS, pEnd); + if pwe = nil then // ûҵβ + begin + DL := pe - po; + Move(po^, pd^, DL shl 1); + SetLength(Result, pd - PQCharW(Result) + DL); + Exit; + end + else + begin + DL := pws - po; + if AWithTag then + begin + Move(po^, pd^, (LS + DL) shl 1); + Inc(pd, LS + DL); + Move(pReplaced^, pd^, LR shl 1); + Inc(pd, LR); + Move(pwe^, pd^, LE shl 1); + Inc(pd, LE); + end + else + begin + Move(po^, pd^, DL shl 1); + Inc(pd, DL); + Move(pReplaced^, pd^, LR shl 1); + Inc(pd, LR); + end; + po := pwe + LE; + Dec(AMaxTimes); + end; + end; + until (AMaxTimes = 0) and (IntPtr(po) < IntPtr(pe)); + if IntPtr(po) < IntPtr(pe) then + begin + DL := pe - po; + Move(po^, pd^, DL shl 1); + Inc(pd, DL); + SetLength(Result, pd - PQCharW(Result)); + end; + end + else + Result := S; +end; + +function StringReplicateW(const S: QStringW; ACount: Integer): QStringW; +var + l: Integer; + p, ps, pd: PQCharW; +begin + l := Length(S); + if (l > 0) and (ACount > 0) then + begin + SetLength(Result, ACount * l); + ps := PQCharW(S); + pd := PQCharW(Result); + for l := 0 to ACount - 1 do + begin + p := ps; + while p^ <> #0 do + begin + pd^ := p^; + Inc(pd); + Inc(p); + end; + end; + end + else + SetLength(Result, 0); +end; + +function FilterCharW(const S: QStringW; AcceptChars: QStringW) + : QStringW; overload; +var + ps, pd, pc, pds: PQCharW; + l: Integer; +begin + SetLength(Result, Length(S)); + if Length(S) > 0 then + begin + ps := PQCharW(S); + pd := PQCharW(Result); + pds := pd; + pc := PQCharW(AcceptChars); + while ps^ <> #0 do + begin + if CharInW(ps, pc, @l) then + begin + pd^ := ps^; + Inc(ps); + Inc(pd); + if l > 1 then + begin + pd^ := ps^; + Inc(ps); + Inc(pd); + end; + end + else + Inc(ps); + end; + SetLength(Result, (IntPtr(pd) - IntPtr(pds)) shr 1); + end; +end; + +function FilterCharW(const S: QStringW; AOnValidate: TQFilterCharEvent; + ATag: Pointer): QStringW; overload; +var + ps, pd, pds: PQCharW; + l, I: Integer; + Accept: Boolean; +begin + if (Length(S) > 0) and Assigned(AOnValidate) then + begin + SetLength(Result, Length(S)); + ps := PQCharW(S); + pd := PQCharW(Result); + pds := pd; + I := 0; + while ps^ <> #0 do + begin + Accept := True; + if CharSizeW(ps) = 2 then + begin + l := Ord(ps^); + Inc(ps); + l := (l shl 16) or Ord(ps^); + AOnValidate(l, I, Accept, ATag); + end + else + AOnValidate(Ord(ps^), I, Accept, ATag); + if Accept then + begin + pd^ := ps^; + Inc(pd); + end; + Inc(ps); + Inc(I); + end; + SetLength(Result, (IntPtr(pd) - IntPtr(pds)) shr 1); + end + else + SetLength(Result, 0); +end; +{$IFDEF UNICODE} + +function FilterCharW(const S: QStringW; AOnValidate: TQFilterCharEventA; + ATag: Pointer): QStringW; overload; +var + ps, pd, pds: PQCharW; + l, I: Integer; + Accept: Boolean; +begin + if (Length(S) > 0) and Assigned(AOnValidate) then + begin + SetLength(Result, Length(S)); + ps := PQCharW(S); + pd := PQCharW(Result); + pds := pd; + I := 0; + while ps^ <> #0 do + begin + Accept := True; + if CharSizeW(ps) = 2 then + begin + l := Ord(ps^); + Inc(ps); + l := (l shl 16) or Ord(ps^); + AOnValidate(l, I, Accept, ATag); + end + else + AOnValidate(Ord(ps^), I, Accept, ATag); + Inc(I); + if Accept then + begin + pd^ := ps^; + Inc(pd); + end; + Inc(ps); + end; + SetLength(Result, (IntPtr(pd) - IntPtr(pds)) shr 1); + end + else + SetLength(Result, 0); +end; +{$ENDIF} + +function FilterNoNumberW(const S: QStringW; Accepts: TQNumberTypes): QStringW; +var + p, pd, pds: PQCharW; + d, e: Integer; + AIsHex: Boolean; + procedure NegPosCheck; + begin + if ((p^ = '+') and (nftPositive in Accepts)) or + ((p^ = '-') and (nftNegative in Accepts)) then + begin + pd^ := p^; + Inc(p); + Inc(pd); + end; + end; + +begin + SetLength(Result, Length(S)); + p := PQCharW(S); + pd := PQCharW(Result); + pds := pd; + AIsHex := False; + NegPosCheck; + if nftHexPrec in Accepts then // Check Hex prec + begin + if (p^ = '0') and (nftCHex in Accepts) then // C Style + begin + Inc(p); + if (p^ = 'x') or (p^ = 'X') then + begin + pd^ := '0'; + Inc(pd); + pd^ := p^; + Inc(pd); + Inc(p); + AIsHex := True; + end + else + Dec(p); + end + else if (p^ = '$') and (nftDelphiHex in Accepts) then + begin + pd^ := p^; + Inc(p); + Inc(pd); + AIsHex := True; + end + else if (p^ = '&') and (nftBasicHex in Accepts) then + begin + Inc(p); + if Ord(p^) in [Ord('h'), Ord('H')] then + begin + pd^ := '&'; + Inc(pd); + pd^ := p^; + Inc(pd); + Inc(p); + AIsHex := True; + end + else + Dec(p); + end; + end; + d := 0; + e := 0; + while p^ <> #0 do + begin + if Ord(p^) in [Ord('0') .. Ord('9')] then + begin + pd^ := p^; + Inc(pd); + end + else if (p^ = '.') and (not AIsHex) then + begin + Inc(d); + if (d = 1) and (nftFloat in Accepts) then + begin + pd^ := p^; + Inc(pd); + end; + end + else if (Ord(p^) in [Ord('e'), Ord('E')]) and (not AIsHex) then + begin + Inc(e); + if (e = 1) and (nftFloat in Accepts) then + begin + if d <= 1 then + begin + pd^ := p^; + Inc(pd); + d := 0; + NegPosCheck; + end; + end; + end + else if AIsHex and ((Ord(p^) in [Ord('a') .. Ord('f')]) or + (Ord(p^) in [Ord('A') .. Ord('F')])) then + begin + pd^ := p^; + Inc(pd); + end; + Inc(p); + end; + SetLength(Result, (IntPtr(pd) - IntPtr(pds)) shr 1); +end; + +function MemScan(S: Pointer; len_s: Integer; sub: Pointer; + len_sub: Integer): Pointer; +var + pb_s, pb_sub, pc_sub, pc_s: PByte; + remain: Integer; +begin + if len_s > len_sub then + begin + pb_s := S; + pb_sub := sub; + Result := nil; + while len_s >= len_sub do + begin + if pb_s^ = pb_sub^ then + begin + remain := len_sub - 1; + pc_sub := pb_sub; + pc_s := pb_s; + Inc(pc_s); + Inc(pc_sub); + if BinaryCmp(pc_s, pc_sub, remain) = 0 then + begin + Result := pb_s; + Break; + end; + end; + Inc(pb_s); + end; + end + else if len_s = len_sub then + begin + if CompareMem(S, sub, len_s) then + Result := S + else + Result := nil; + end + else + Result := nil; +end; + +function BinaryCmp(const p1, p2: Pointer; len: Integer): Integer; + function CompareByByte: Integer; + var + b1, b2: PByte; + begin + if (len <= 0) or (p1 = p2) then + Result := 0 + else + begin + b1 := p1; + b2 := p2; + Result := 0; + while len > 0 do + begin + if b1^ <> b2^ then + begin + Result := b1^ - b2^; + Exit; + end; + Inc(b1); + Inc(b2); + end; + end; + end; + +begin +{$IFDEF MSWINDOWS} + if Assigned(VCMemCmp) then + Result := VCMemCmp(p1, p2, len) + else + Result := CompareByByte; +{$ELSE} + Result := memcmp(p1, p2, len); +{$ENDIF} +end; + +procedure SkipHex(var S: PQCharW); +begin + while ((S^ >= '0') and (S^ <= '9')) or ((S^ >= 'a') and (S^ <= 'f')) or + ((S^ >= 'A') and (S^ <= 'F')) do + Inc(S); +end; + +procedure SkipDec(var S: PQCharW); +begin + while (S^ >= '0') and (S^ <= '9') do + Inc(S); +end; + +function ParseHex(var p: PQCharW; var Value: Int64): Integer; +var + ps: PQCharW; +begin + Value := 0; + ps := p; + while IsHexChar(p^) do + begin + Value := (Value shl 4) + HexValue(p^); + Inc(p); + end; + Result := p - ps; +end; + +function LeftStrCount(const S: QStringW; const sub: QStringW; + AIgnoreCase: Boolean): Integer; +var + ps, psub: PQCharW; + l: Integer; +begin + l := Length(sub); + Result := 0; + if (l > 0) and (Length(S) >= l) then + begin + ps := PQCharW(S); + psub := PQCharW(sub); + if AIgnoreCase then + begin + repeat + ps := StrIStrW(ps, psub); + if ps <> nil then + begin + Inc(Result); + Inc(ps, l); + end; + until ps = nil; + end + else + begin + repeat + ps := StrStrW(ps, psub); + if ps <> nil then + begin + Inc(Result); + Inc(ps, l); + end; + until ps = nil; + end; + end; +end; + +function RightStrCount(const S: QStringW; const sub: QStringW; + AIgnoreCase: Boolean): Integer; +var + ps, pe, psub: PQCharW; + l: Integer; +begin + l := Length(sub); + Result := 0; + if Length(S) > l then + begin + ps := PQCharW(S); + pe := ps + Length(S) - 1; + psub := PQCharW(sub); + while pe >= ps do + begin + if StartWithW(pe, psub, AIgnoreCase) then + begin + Inc(Result); + Dec(pe, l); + end + else + Dec(pe); + end; + end; +end; + +function ParseInt(var S: PQCharW; var ANum: Int64): Integer; +var + ps: PQCharW; + ANeg: Boolean; +begin + ps := S; + // 16ƿʼַ + if S[0] = '$' then + begin + Inc(S); + Result := ParseHex(S, ANum); + end + else if (S[0] = '0') and ((S[1] = 'x') or (S[1] = 'X')) then + begin + Inc(S, 2); + Result := ParseHex(S, ANum); + end + else + begin + if (S^ = '-') then + begin + ANeg := True; + Inc(S); + end + else + begin + ANeg := False; + if S^ = '+' then + Inc(S); + end; + ANum := 0; + while (S^ >= '0') and (S^ <= '9') do + begin + ANum := ANum * 10 + Ord(S^) - Ord('0'); + if ANum < 0 then // + begin + Result := 0; + S := ps; + Exit; + end; + Inc(S); + end; + if ANeg then + ANum := -ANum; + Result := S - ps; + end; +end; + +function ParseNumeric(var S: PQCharW; var ANum: Extended): Boolean; +var + ps: PQCharW; + function ParseHexInt: Boolean; + var + iVal: Int64; + begin + iVal := 0; + while IsHexChar(S^) do + begin + iVal := (iVal shl 4) + HexValue(S^); + Inc(S); + end; + Result := (S <> ps); + ANum := iVal; + end; + + function ParseDec: Boolean; + var + ACount: Integer; + iVal: Int64; + APow: Extended; + ANeg: Boolean; + begin + try + ANeg := S^ = '-'; + if ANeg then + Inc(S); + Result := ParseInt(S, iVal) > 0; + if not Result then + Exit; + if ANeg then + ANum := -iVal + else + ANum := iVal; + if S^ = '.' then // С + begin + Inc(S); + ACount := ParseInt(S, iVal); + if ACount > 0 then + begin + if (ANum < 0) or ANeg then + ANum := ANum - iVal / IntPower(10, ACount) + else + ANum := ANum + iVal / IntPower(10, ACount); + end; + end; + if (S^ = 'e') or (S^ = 'E') then + begin + Inc(S); + if ParseNumeric(S, APow) then + begin + ANum := ANum * Power(10, APow); + + end; + end; + Result := (S <> ps); + except + on e: EOverflow do + Result := False; + end; + end; + +begin + ps := S; + if (S^ = '$') or (S^ = '&') then + begin + Inc(S); + Result := ParseHexInt; + Exit; + end + else if (S[0] = '0') and ((S[1] = 'x') or (S[1] = 'X')) then + begin + Inc(S, 2); + Result := ParseHexInt; + Exit; + end + else + Result := ParseDec; + if not Result then + S := ps; +end; + +function NameOfW(const S: QStringW; ASpliter: QCharW): QStringW; +var + p: PQCharW; +begin + p := PQCharW(S); + Result := DecodeTokenW(p, [ASpliter], WideChar(0), False); +end; + +function ValueOfW(const S: QStringW; ASpliter: QCharW): QStringW; +var + p: PQCharW; + l: Integer; +begin + p := PQCharW(S); + if p^ = ASpliter then + begin + l := Length(S); + Dec(l); + SetLength(Result, l); + Inc(p); + Move(p^, PQCharW(Result)^, l shl 1); + end + else + begin + DecodeTokenW(p, [ASpliter], WideChar(0), False); + if p^ <> #0 then + Result := p + else + Result := S; + end; +end; + +function IndexOfNameW(AList: TStrings; const AName: QStringW; + ASpliter: QCharW): Integer; +var + I: Integer; +begin + Result := -1; + for I := 0 to AList.count - 1 do + begin + if NameOfW(AList[I], ASpliter) = AName then + begin + Result := I; + Break; + end; + end; +end; + +function IndexOfValueW(AList: TStrings; const AValue: QStringW; + ASpliter: QCharW): Integer; +var + I: Integer; +begin + Result := -1; + for I := 0 to AList.count - 1 do + begin + if ValueOfW(AList[I], ASpliter) = AValue then + begin + Result := I; + Break; + end; + end; +end; + +function DeleteCharW(const ASource, ADeletes: QStringW): QStringW; +var + ps, pd: PQCharW; + l, ACharLen: Integer; +begin + l := Length(ASource); + if (l > 0) and (Length(ADeletes) > 0) then + begin + SetLength(Result, l); + ps := PQCharW(ASource); + pd := PQCharW(Result); + while l > 0 do + begin + if not CharInW(ps, PQCharW(ADeletes), @ACharLen) then + begin + pd^ := ps^; + Inc(pd); + ACharLen := CharSizeW(ps); + end; + Inc(ps, ACharLen); + Dec(l, ACharLen); + end; + SetLength(Result, pd - PQCharW(Result)); + end + else + Result := ASource; +end; + +function DeleteRightW(const S, ADelete: QStringW; AIgnoreCase: Boolean = False; + ACount: Integer = MaxInt): QStringW; +var + ps, pd, pe: PQCharW; + LS, LD: Integer; +begin + LS := Length(S); + LD := Length(ADelete); + if LS < LD then + Result := S + else + begin + pe := PQCharW(S) + Length(S); + pd := PQCharW(ADelete); + if AIgnoreCase then + begin + while LS >= LD do + begin + ps := pe - LD; + if StrIStrW(ps, pd) = ps then + begin + pe := ps; + Dec(LS, LD); + end + else + Break; + end; + end + else + begin + while LS >= LD do + begin + ps := pe - LD; + if CompareMem(ps, pd, LD shl 1) then + begin + pe := ps; + Dec(LS, LD); + end + else + Break; + end; + end; + SetLength(Result, LS); + if LS > 0 then + Move(PWideChar(S)^, PQCharW(Result)^, LS shl 1); + end; +end; + +function DeleteLeftW(const S, ADelete: QStringW; AIgnoreCase: Boolean = False; + ACount: Integer = MaxInt): QStringW; +var + ps, pd: PQCharW; + LS, LD: Integer; +begin + LS := Length(S); + LD := Length(ADelete); + if LS < LD then + Result := S + else + begin + ps := PQCharW(S); + pd := PQCharW(ADelete); + if AIgnoreCase then + begin + while LS >= LD do + begin + if StartWithW(ps, pd, True) then + begin + Inc(ps, LD); + Dec(LS, LD); + end + else + Break; + end; + end + else + begin + while LS >= LD do + begin + if CompareMem(ps, pd, LD shl 1) then + begin + Inc(ps, LD); + Dec(LS, LD); + end + else + Break; + end; + end; + SetLength(Result, LS); + if LS > 0 then + Move(ps^, PQCharW(Result)^, LS shl 1); + end; +end; + +function ContainsCharW(const S, ACharList: QStringW): Boolean; +var + ps: PQCharW; + l: Integer; +begin + l := Length(S); + Result := False; + if (l > 0) then + begin + if Length(ACharList) > 0 then + begin + ps := PQCharW(S); + while l > 0 do + begin + if CharInW(ps, PQCharW(ACharList)) then + begin + Result := True; + Break; + end; + Inc(ps); + Dec(l); + end; + end; + end; +end; + +procedure StrCpyW(d: PQCharW; S: PQCharW; ACount: Integer); +begin + while (S^ <> #0) and (ACount <> 0) do + begin + d^ := S^; + Inc(d); + Inc(S); + Dec(ACount); + end; +end; + +function HtmlEscape(const S: QStringW): QStringW; +var + p, pd: PQCharW; + AFound: Boolean; + I: Integer; +begin + if Length(S) > 0 then + begin + System.SetLength(Result, Length(S) shl 3); // ת崮8ַ*8϶ + p := PWideChar(S); + pd := PWideChar(Result); + while p^ <> #0 do + begin + AFound := False; + for I := 0 to 92 do + begin + if HtmlEscapeChars[I shl 1] = p^ then + begin + AFound := True; + StrCpyW(pd, PQCharW(HtmlEscapeChars[(I shl 1) + 1])); + Break; + end; + end; // end for + if not AFound then + begin + pd^ := p^; + Inc(pd); + end; // end if + Inc(p); + end; // end while + SetLength(Result, pd - PQCharW(Result)); + end // end if + else + Result := ''; +end; + +function HtmlUnescape(const S: QStringW): QStringW; +var + p, pd, ps: PQCharW; + AFound: Boolean; + I, l: Integer; +begin + if Length(S) > 0 then + begin + System.SetLength(Result, Length(S)); + p := PQCharW(S); + pd := PQCharW(Result); + while p^ <> #0 do + begin + if p^ = '&' then + begin + if p[1] = '#' then + begin + ps := p; + Inc(p, 2); + l := 0; + if p^ = 'x' then + begin + Inc(p); + while IsHexChar(p^) do + begin + l := l shl 4 + HexValue(p^); + Inc(p); + end; + end + else + begin + while (p^ >= '0') and (p^ <= '9') do + begin + l := l * 10 + Ord(p^) - Ord('0'); + Inc(p); + end; + end; + if p^ = ';' then + begin + pd^ := QCharW(l); + Inc(pd); + end + else + begin + pd^ := ps^; + Inc(pd); + p := ps; + end; + end + else + begin + AFound := False; + for I := 0 to 91 do + begin + if StrStrW(p, PWideChar(HtmlEscapeChars[I shl 1 + 1])) = p then + begin + AFound := True; + StrCpyW(pd, PQCharW(HtmlEscapeChars[(I shl 1)])); + Break; + end; + end; // end for + if AFound then + begin + Inc(p, Length(HtmlEscapeChars[I shl 1 + 1])); + continue; + end + else + begin + pd^ := p^; + Inc(pd); + end; // end if + end; // end else + end // end else + else + begin + pd^ := p^; + Inc(pd); + end; + Inc(p); + end; // end while + SetLength(Result, pd - PWideChar(Result)); + end // end if + else + Result := ''; +end; + +function HtmlTrimText(const S: QStringW): QStringW; +var + ps, pe: PQCharW; + l: Integer; +begin + if Length(S) > 0 then + begin + ps := PQCharW(S); + pe := ps + System.Length(S) - 1; + while IsSpaceW(ps) do + Inc(ps); + while IsSpaceW(pe) do + Dec(pe); + l := pe - ps + 1; + SetLength(Result, l); + Move(ps^, PQCharW(Result)^, l shl 1); + end + else + Result := ''; +end; + +// һЩ +function ParseDateTime(S: PWideChar; var AResult: TDateTime): Boolean; +var + Y, M, d, H, N, Sec, MS: Word; + AQuoter: WideChar; + ADate: TDateTime; + function ParseNum(var N: Word): Boolean; + var + neg: Boolean; + ps: PQCharW; + begin + N := 0; + ps := S; + if S^ = '-' then + begin + neg := True; + Inc(S); + end + else + neg := False; + while S^ <> #0 do + begin + if (S^ >= '0') and (S^ <= '9') then + begin + N := N * 10 + Ord(S^) - 48; + Inc(S); + end + else + Break; + end; + if neg then + N := -N; + Result := ps <> S; + end; + +begin + if (S^ = '"') or (S^ = '''') then + begin + AQuoter := S^; + Inc(S); + end + else + AQuoter := #0; + Result := ParseNum(Y); + if not Result then + Exit; + if (S^ = '-') or (S^ = '/') then + begin + Inc(S); + Result := ParseNum(M); + if (not Result) or ((S^ <> '-') and (S^ <> '/')) then + Exit; + Inc(S); + Result := ParseNum(d); + if (not Result) or ((S^ <> 'T') and (S^ <> ' ') and (S^ <> #0)) then + Exit; + if S^ <> #0 then + Inc(S); + if d > 31 then // D -> Y + begin + if M > 12 then // M/D/Y M -> D, D->Y, Y->M + Result := TryEncodeDate(d, Y, M, ADate) + else // D/M/Y + Result := TryEncodeDate(d, M, Y, ADate); + end + else + Result := TryEncodeDate(Y, M, d, ADate); + if not Result then + Exit; + SkipSpaceW(S); + if S^ <> #0 then + begin + if not ParseNum(H) then // ûʱֵ + begin + AResult := ADate; + Exit; + end; + if S^ <> ':' then + begin + if H in [0 .. 23] then + AResult := ADate + EncodeTime(H, 0, 0, 0) + else + Result := False; + Exit; + end; + Inc(S); + end + else + begin + AResult := ADate; + Exit; + end; + end + else if S^ = ':' then + begin + ADate := 0; + H := Y; + Inc(S); + end + else + begin + Result := False; + Exit; + end; + if H > 23 then + begin + Result := False; + Exit; + end; + if not ParseNum(N) then + begin + if AQuoter <> #0 then + begin + if S^ = AQuoter then + AResult := ADate + EncodeTime(H, 0, 0, 0) + else + Result := False; + end + else + AResult := ADate + EncodeTime(H, 0, 0, 0); + Exit; + end + else if N > 59 then + begin + Result := False; + Exit; + end; + Sec := 0; + MS := 0; + if S^ = ':' then + begin + Inc(S); + if not ParseNum(Sec) then + begin + if AQuoter <> #0 then + begin + if S^ = AQuoter then + AResult := ADate + EncodeTime(H, N, 0, 0) + else + Result := False; + end + else + AResult := ADate + EncodeTime(H, N, 0, 0); + Exit; + end + else if Sec > 59 then + begin + Result := False; + Exit; + end; + if S^ = '.' then + begin + Inc(S); + if not ParseNum(MS) then + begin + if AQuoter <> #0 then + begin + if AQuoter = S^ then + AResult := ADate + EncodeTime(H, N, Sec, 0) + else + Result := False; + end + else + AResult := ADate + EncodeTime(H, N, Sec, 0); + Exit; + end + else if MS >= 1000 then // 1000΢ΪλʱģתΪ + begin + while MS >= 1000 do + MS := MS div 10; + end; + if AQuoter <> #0 then + begin + if AQuoter = S^ then + AResult := ADate + EncodeTime(H, N, Sec, MS) + else + Result := False; + Exit; + end + else + AResult := ADate + EncodeTime(H, N, Sec, MS); + end + else + begin + if AQuoter <> #0 then + begin + if AQuoter = S^ then + AResult := ADate + EncodeTime(H, N, Sec, 0) + else + Result := False; + end + else + AResult := ADate + EncodeTime(H, N, Sec, 0) + end; + end + else + begin + if AQuoter <> #0 then + begin + if AQuoter = S^ then + AResult := ADate + EncodeTime(H, N, 0, 0) + else + Result := False; + end + else + AResult := ADate + EncodeTime(H, N, 0, 0); + end; +end; + +function ParseWebTime(p: PWideChar; var AResult: TDateTime): Boolean; +var + I: Integer; + Y, M, d, H, N, S: Integer; +const + MonthNames: array [0 .. 11] of QStringW = ('Jan', 'Feb', 'Mar', 'Apr', 'May', + 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); + Comma: PWideChar = ','; + Digits: PWideChar = '0123456789'; +begin + // ڣֱͨڼҪ + SkipUntilW(p, Comma, WideChar(0)); + if p^ = #0 then + begin + Result := False; + Exit; + end + else + Inc(p); + SkipUntilW(p, Digits, WideChar(0)); + d := 0; + // + while (p^ >= '0') and (p^ <= '9') do + begin + d := d * 10 + Ord(p^) - Ord('0'); + Inc(p); + end; + if (d < 1) or (d > 31) then + begin + Result := False; + Exit; + end; + SkipSpaceW(p); + M := 0; + for I := 0 to 11 do + begin + if StartWithW(p, PWideChar(MonthNames[I]), True) then + begin + M := I + 1; + Break; + end; + end; + if (M < 1) or (M > 12) then + begin + Result := False; + Exit; + end; + while (p^ <> #0) and ((p^ < '0') or (p^ > '9')) do + Inc(p); + Y := 0; + while (p^ >= '0') and (p^ <= '9') do + begin + Y := Y * 10 + Ord(p^) - Ord('0'); + Inc(p); + end; + while p^ = ' ' do + Inc(p); + H := 0; + while (p^ >= '0') and (p^ <= '9') do + begin + H := H * 10 + Ord(p^) - Ord('0'); + Inc(p); + end; + while p^ = ':' do + Inc(p); + N := 0; + while (p^ >= '0') and (p^ <= '9') do + begin + N := N * 10 + Ord(p^) - Ord('0'); + Inc(p); + end; + while p^ = ':' do + Inc(p); + S := 0; + while (p^ >= '0') and (p^ <= '9') do + begin + S := S * 10 + Ord(p^) - Ord('0'); + Inc(p); + end; + while p^ = ':' do + Inc(p); + Result := TryEncodeDateTime(Y, M, d, H, N, S, 0, AResult); +end; + +function RollupSize(ASize: Int64): QStringW; +var + AIdx, R1, s1: Int64; + AIsNeg: Boolean; +const + Units: array [0 .. 3] of QStringW = ('GB', 'MB', 'KB', 'B'); +begin + AIsNeg := (ASize < 0); + AIdx := 3; + R1 := 0; + if AIsNeg then + ASize := -ASize; + Result := ''; + while (AIdx >= 0) do + begin + s1 := ASize mod 1024; + ASize := ASize shr 10; + if (ASize = 0) or (AIdx = 0) then + begin + R1 := R1 * 100 div 1024; + if R1 > 0 then + begin + if R1 >= 10 then + Result := IntToStr(s1) + '.' + IntToStr(R1) + Units[AIdx] + else + Result := IntToStr(s1) + '.' + '0' + IntToStr(R1) + Units[AIdx]; + end + else + Result := IntToStr(s1) + Units[AIdx]; + Break; + end; + R1 := s1; + Dec(AIdx); + end; + if AIsNeg then + Result := '-' + Result; +end; + +function RollupTime(ASeconds: Int64; AHideZero: Boolean): QStringW; +var + H, N, d: Integer; +begin + if ASeconds = 0 then + begin + if AHideZero then + Result := '' + else + Result := '0' + SSecondName; + end + else + begin + Result := ''; + d := ASeconds div 86400; + ASeconds := ASeconds mod 86400; + H := ASeconds div 3600; + ASeconds := ASeconds mod 3600; + N := ASeconds div 60; + ASeconds := ASeconds mod 60; + if d > 0 then + Result := IntToStr(d) + SDayName + else + Result := ''; + if H > 0 then + Result := Result + IntToStr(H) + SHourName; + if N > 0 then + Result := Result + IntToStr(N) + SMinuteName; + if ASeconds > 0 then + Result := Result + IntToStr(ASeconds) + SSecondName; + end; +end; +{ QStringA } + +function QStringA.From(p: PQCharA; AOffset, ALen: Integer): PQStringA; +begin + SetLength(ALen); + Inc(p, AOffset); + Move(p^, PQCharA(@FValue[1])^, ALen); + Result := @Self; +end; + +function QStringA.From(const S: QStringA; AOffset: Integer): PQStringA; +begin + Result := From(PQCharA(S), AOffset, S.Length); +end; + +function QStringA.GetChars(AIndex: Integer): QCharA; +begin + if (AIndex < 0) or (AIndex >= Length) then + raise Exception.CreateFmt(SOutOfIndex, [AIndex, 0, Length - 1]); + Result := FValue[AIndex + 1]; +end; + +function QStringA.GetData: PByte; +begin + Result := @FValue[1]; +end; + +class operator QStringA.Implicit(const S: QStringW): QStringA; +begin + Result := qstring.AnsiEncode(S); +end; + +class operator QStringA.Implicit(const S: QStringA): Pointer; +begin + Result := PQCharA(@S.FValue[1]); +end; + +function QStringA.GetIsUtf8: Boolean; +begin + if System.Length(FValue) > 0 then + Result := (FValue[0] = 1) + else + Result := False; +end; + +function QStringA.GetLength: Integer; +begin + // QStringA.FValue[0]ͣ0-ANSI,1-UTF8ĩβַ\0 + Result := System.Length(FValue); + if Result >= 2 then + Dec(Result, 2) + else + Result := 0; +end; + +class operator QStringA.Implicit(const S: QStringA): TBytes; +var + l: Integer; +begin + l := System.Length(S.FValue) - 1; + System.SetLength(Result, l); + if l > 0 then + Move(S.FValue[1], Result[0], l); +end; + +procedure QStringA.SetChars(AIndex: Integer; const Value: QCharA); +begin + if (AIndex < 0) or (AIndex >= Length) then + raise Exception.CreateFmt(SOutOfIndex, [AIndex, 0, Length - 1]); + FValue[AIndex + 1] := Value; +end; + +procedure QStringA.SetLength(const Value: Integer); +begin + if Value < 0 then + begin + if System.Length(FValue) > 0 then + System.SetLength(FValue, 1) + else + begin + System.SetLength(FValue, 1); + FValue[0] := 0; // ANSI + end; + end + else + begin + System.SetLength(FValue, Value + 2); + FValue[Value + 1] := 0; + end; +end; + +class operator QStringA.Implicit(const ABytes: TBytes): QStringA; +var + l: Integer; +begin + l := System.Length(ABytes); + Result.Length := l; + if l > 0 then + Move(ABytes[0], Result.FValue[1], l); +end; + +class operator QStringA.Implicit(const S: QStringA): QStringW; +begin + Result := AnsiDecode(S); +end; + +function BinToHex(p: Pointer; l: Integer; ALowerCase: Boolean): QStringW; +const + B2HConvert: array [0 .. 15] of QCharW = ('0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); + B2HConvertL: array [0 .. 15] of QCharW = ('0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); +var + pd: PQCharW; + pb: PByte; +begin + SetLength(Result, l shl 1); + pd := PQCharW(Result); + pb := p; + if ALowerCase then + begin + while l > 0 do + begin + pd^ := B2HConvertL[pb^ shr 4]; + Inc(pd); + pd^ := B2HConvertL[pb^ and $0F]; + Inc(pd); + Inc(pb); + Dec(l); + end; + end + else + begin + while l > 0 do + begin + pd^ := B2HConvert[pb^ shr 4]; + Inc(pd); + pd^ := B2HConvert[pb^ and $0F]; + Inc(pd); + Inc(pb); + Dec(l); + end; + end; +end; + +function BinToHex(const ABytes: TBytes; ALowerCase: Boolean): QStringW; +begin + Result := BinToHex(@ABytes[0], Length(ABytes), ALowerCase); +end; + +procedure HexToBin(const S: QStringW; var AResult: TBytes); +var + l: Integer; + p, ps: PQCharW; + pd: PByte; +begin + l := System.Length(S); + SetLength(AResult, l shr 1); + p := PQCharW(S); + ps := p; + pd := @AResult[0]; + while p - ps < l do + begin + if IsHexChar(p[0]) and IsHexChar(p[1]) then + begin + pd^ := (HexValue(p[0]) shl 4) + HexValue(p[1]); + Inc(pd); + Inc(p, 2); + end + else + begin + SetLength(AResult, 0); + Exit; + end; + end; +end; + +function HexToBin(const S: QStringW): TBytes; +begin + HexToBin(S, Result); +end; + +procedure FreeObject(AObject: TObject); +begin +{$IFDEF AUTOREFCOUNT} + AObject.DisposeOf; +{$ELSE} + AObject.Free; +{$ENDIF} +end; + +function HashOf(p: Pointer; l: Integer): Cardinal; +{$IFDEF WIN32} +label A00; +begin + asm + push ebx + xor eax, eax + mov edx, p + mov ebx,edx + add ebx,l + A00: + imul eax,131 + movzx ecx, BYTE ptr [edx] + inc edx + add eax, ecx + cmp ebx, edx + jne A00 + pop ebx + mov Result,eax + end; +{$ELSE} +var + pe: PByte; + ps: PByte absolute p; +const + seed = 131; // 31 131 1313 13131 131313 etc.. +begin + pe := p; + Inc(pe, l); + Result := 0; + while IntPtr(ps) < IntPtr(pe) do + begin + Result := Result * seed + ps^; + Inc(ps); + end; + Result := Result and $7FFFFFFF; +{$ENDIF} +end; + +class operator QStringA.Implicit(const S: PQCharA): QStringA; +var + p: PQCharA; +begin + if S <> nil then + begin + p := S; + while p^ <> 0 do + Inc(p); + Result.Length := IntPtr(p) - IntPtr(S); + Move(S^, PQCharA(Result)^, Result.Length); + end + else + Result.Length := 0; +end; +{$IFNDEF NEXTGEN} + +class operator QStringA.Implicit(const S: AnsiString): QStringA; +begin + Result.From(PQCharA(S), 0, System.Length(S)); +end; + +class operator QStringA.Implicit(const S: QStringA): AnsiString; +begin + System.SetLength(Result, S.Length); + if S.Length > 0 then + Move(PQCharA(S)^, PAnsiChar(Result)^, S.Length); +end; +{$ENDIF} + +function QStringA.Cat(p: PQCharA; ALen: Integer): PQStringA; +var + l: Integer; +begin + l := Length; + SetLength(l + ALen); + Move(p^, FValue[1 + l], ALen); + Result := @Self; +end; + +function QStringA.Cat(const S: QStringA): PQStringA; +begin + Result := Cat(PQCharA(S), S.Length); +end; + +{ TQStringCatHelperW } + +function TQStringCatHelperW.Back(ALen: Integer): TQStringCatHelperW; +begin + Result := Self; + Dec(FDest, ALen); + if FDest < FStart then + FDest := FStart; +end; + +function TQStringCatHelperW.BackIf(const S: PQCharW): TQStringCatHelperW; +var + ps: PQCharW; +begin + Result := Self; + ps := FStart; + while FDest > ps do + begin + if (FDest[-1] >= #$DC00) and (FDest[-1] <= #$DFFF) then + begin + if CharInW(FDest - 2, S) then + Dec(FDest, 2) + else + Break; + end + else if CharInW(FDest - 1, S) then + Dec(FDest) + else + Break; + end; +end; + +function TQStringCatHelperW.Cat(const S: QStringW): TQStringCatHelperW; +begin + Result := Cat(PQCharW(S), Length(S)); +end; + +function TQStringCatHelperW.Cat(p: PQCharW; len: Integer): TQStringCatHelperW; +begin + Result := Self; + if len < 0 then + begin + while p^ <> #0 do + begin + if Position >= FSize then + NeedSize(FSize + FBlockSize); + FDest^ := p^; + Inc(p); + Inc(FDest); + end; + end + else + begin + NeedSize(-len); + Move(p^, FDest^, len shl 1); + Inc(FDest, len); + end; +end; + +function TQStringCatHelperW.Cat(c: QCharW): TQStringCatHelperW; +begin + if Position >= FSize then + NeedSize(-1); + FDest^ := c; + Inc(FDest); + Result := Self; +end; + +function TQStringCatHelperW.Cat(const V: Double): TQStringCatHelperW; +begin + Result := Cat(FloatToStr(V)); +end; + +function TQStringCatHelperW.Cat(const V: Int64): TQStringCatHelperW; +begin + Result := Cat(IntToStr(V)); +end; + +function TQStringCatHelperW.Cat(const V: Boolean): TQStringCatHelperW; +begin + Result := Cat(BoolToStr(V, True)); +end; + +function TQStringCatHelperW.Cat(const V: TGuid): TQStringCatHelperW; +begin + Result := Cat(GuidToString(V)); +end; + +function TQStringCatHelperW.Cat(const V: Currency): TQStringCatHelperW; +begin + Result := Cat(CurrToStr(V)); +end; + +constructor TQStringCatHelperW.Create(ASize: Integer); +begin + inherited Create; + if ASize < 8192 then + ASize := 8192 + else if (ASize and $3FF) <> 0 then + ASize := ((ASize shr 10) + 1) shr 1; + FBlockSize := ASize; + NeedSize(FBlockSize); +end; + +constructor TQStringCatHelperW.Create; +begin + inherited Create; + FBlockSize := 8192; + NeedSize(FBlockSize); +end; + +function TQStringCatHelperW.GetChars(AIndex: Integer): QCharW; +begin + Result := FStart[AIndex]; +end; + +function TQStringCatHelperW.GetPosition: Integer; +begin + Result := FDest - FStart; +end; + +function TQStringCatHelperW.GetValue: QStringW; +var + l: Integer; +begin + l := Position; + SetLength(Result, l); + Move(FStart^, PQCharW(Result)^, l shl 1); +end; + +procedure TQStringCatHelperW.LoadFromFile(const AFileName: QStringW); +begin + Reset; + Cat(LoadTextW(AFileName)); +end; + +procedure TQStringCatHelperW.LoadFromStream(const AStream: TStream); +begin + Reset; + Cat(LoadTextW(AStream)); +end; + +procedure TQStringCatHelperW.NeedSize(ASize: Integer); +var + Offset: Integer; +begin + Offset := FDest - FStart; + if ASize < 0 then + ASize := Offset - ASize; + if ASize > FSize then + begin +{$IFDEF DEBUG} + Inc(FAllocTimes); +{$ENDIF} + FSize := ((ASize + FBlockSize) div FBlockSize) * FBlockSize; + SetLength(FValue, FSize); + FStart := PQCharW(@FValue[0]); + FDest := FStart + Offset; + end; +end; + +function TQStringCatHelperW.Replicate(const S: QStringW; count: Integer) + : TQStringCatHelperW; +var + ps: PQCharW; + l: Integer; +begin + Result := Self; + if count > 0 then + begin + ps := PQCharW(S); + l := Length(S); + while count > 0 do + begin + Cat(ps, l); + Dec(count); + end; + end; +end; + +procedure TQStringCatHelperW.Reset; +begin + FDest := FStart; +end; + +procedure TQStringCatHelperW.SetPosition(const Value: Integer); +begin + if Value <= 0 then + FDest := FStart + else if Value > Length(FValue) then + begin + NeedSize(Value); + FDest := FStart + Value; + end + else + FDest := FStart + Value; +end; + +procedure TQStringCatHelperW.TrimRight; +var + pd: PQCharW; +begin + pd := FDest; + Dec(pd); + while FStart < pd do + begin + if IsSpaceW(pd) then + Dec(pd) + else + Break; + end; + Inc(pd); + FDest := pd; +end; + +function TQStringCatHelperW.Cat(const V: Variant): TQStringCatHelperW; +begin + Result := Cat(VarToStr(V)); +end; + +{ TQPtr } + +class function TQPtr.Bind(AObject: TObject): IQPtr; +begin + Result := TQPtr.Create(AObject); +end; + +class function TQPtr.Bind(AData: Pointer; AOnFree: TQPtrFreeEventG): IQPtr; +var + ATemp: TQPtr; +begin + ATemp := TQPtr.Create(AData); + ATemp.FOnFree.Method.Data := nil; + ATemp.FOnFree.OnFreeG := AOnFree; + Result := ATemp; +end; + +class function TQPtr.Bind(AData: Pointer; AOnFree: TQPtrFreeEvent): IQPtr; +var + ATemp: TQPtr; +begin + ATemp := TQPtr.Create(AData); +{$IFDEF NEXTGEN} + PQPtrFreeEvent(@ATemp.FOnFree.OnFree)^ := AOnFree; +{$ELSE} + ATemp.FOnFree.OnFree := AOnFree; +{$ENDIF} + Result := ATemp; +end; + +{$IFDEF UNICODE} + +class function TQPtr.Bind(AData: Pointer; AOnFree: TQPtrFreeEventA): IQPtr; +var + ATemp: TQPtr; +begin + ATemp := TQPtr.Create(AData); + ATemp.FOnFree.Method.Data := Pointer(-1); + TQPtrFreeEventA(ATemp.FOnFree.OnFreeA) := AOnFree; + Result := ATemp; +end; +{$ENDIF} + +constructor TQPtr.Create(AObject: Pointer); +begin + inherited Create; + FObject := AObject; +end; + +destructor TQPtr.Destroy; +begin + if Assigned(FObject) then + begin + if Assigned(FOnFree.OnFree) then + begin + if FOnFree.Method.Data = nil then + FOnFree.OnFreeG(FObject) +{$IFDEF UNICODE} + else if FOnFree.Method.Data = Pointer(-1) then + TQPtrFreeEventA(FOnFree.OnFreeA)(FObject) +{$ENDIF} + else +{$IFDEF NEXTGEN} + begin + PQPtrFreeEvent(FOnFree.OnFree)^(FObject); + PQPtrFreeEvent(FOnFree.OnFree)^ := nil; + end; +{$ELSE} + FOnFree.OnFree(FObject); +{$ENDIF} + end + else + FreeAndNil(FObject); + end; + inherited; +end; + +function TQPtr.Get: Pointer; +begin + Result := FObject; +end; + +// 2007ԭӲӿ +{$IF RTLVersion<24} + +function AtomicCmpExchange(var Target: Integer; Value: Integer; + Comparand: Integer): Integer; inline; +begin +{$IFDEF MSWINDOWS} + Result := InterlockedCompareExchange(Target, Value, Comparand); +{$ELSE} + Result := TInterlocked.CompareExchange(Target, Value, Comparand); +{$ENDIF} +end; + +function AtomicCmpExchange(var Target: Pointer; Value: Pointer; + Comparand: Pointer): Pointer; inline; +begin +{$IFDEF MSWINDOWS} + Result := Pointer(InterlockedCompareExchange(PInteger(Target)^, + Integer(Value), Integer(Comparand))); +{$ELSE} + Result := TInterlocked.CompareExchange(Target, Value, Comparand); +{$ENDIF} +end; + +function AtomicIncrement(var Target: Integer; const Value: Integer) + : Integer; inline; +begin +{$IFDEF MSWINDOWS} + if Value = 1 then + Result := InterlockedIncrement(Target) + else if Value = -1 then + Result := InterlockedDecrement(Target) + else + Result := InterlockedExchangeAdd(Target, Value); +{$ELSE} + if Value = 1 then + Result := TInterlocked.Increment(Target) + else if Value = -1 then + Result := TInterlocked.Decrement(Target) + else + Result := TInterlocked.Add(Target, Value); +{$ENDIF} +end; + +function AtomicDecrement(var Target: Integer): Integer; inline; +begin + // Result := InterlockedDecrement(Target); + Result := AtomicIncrement(Target, -1); +end; + +function AtomicExchange(var Target: Integer; Value: Integer): Integer; +begin +{$IFDEF MSWINDOWS} + Result := InterlockedExchange(Target, Value); +{$ELSE} + Result := TInterlocked.Exchange(Target, Value); +{$ENDIF} +end; + +function AtomicExchange(var Target: Pointer; Value: Pointer): Pointer; +begin +{$IFDEF MSWINDOWS} +{$IF RTLVersion>19} + Result := InterlockedExchangePointer(Target, Value); +{$ELSE} + Result := Pointer(IntPtr(InterlockedExchange(IntPtr(Target), IntPtr(Value)))); +{$IFEND} +{$ELSE} + Result := TInterlocked.Exchange(Target, Value); +{$ENDIF} +end; +{$IFEND 0 then + Result := Cat(@ABytes[0], Length(ABytes)) + else + Result := Self; +end; + +function TQBytesCatHelper.Cat(const AData: Pointer; const ALen: Integer) + : TQBytesCatHelper; +begin + Result := Self; + NeedSize(-ALen); + Move(AData^, FDest^, ALen); + Inc(FDest, ALen); +end; + +function TQBytesCatHelper.Cat(const V: TGuid): TQBytesCatHelper; +begin + Result := Cat(@V, SizeOf(TGuid)); +end; + +constructor TQBytesCatHelper.Create(ASize: Integer); +begin + inherited Create; + FBlockSize := ASize; + NeedSize(FBlockSize); +end; + +constructor TQBytesCatHelper.Create; +begin + inherited Create; + FBlockSize := 8192; + NeedSize(FBlockSize); +end; + +function TQBytesCatHelper.GetBytes(AIndex: Integer): Byte; +begin + Result := FValue[AIndex]; +end; + +function TQBytesCatHelper.GetPosition: Integer; +begin + Result := IntPtr(FDest) - IntPtr(FStart); +end; + +procedure TQBytesCatHelper.NeedSize(ASize: Integer); +var + Offset: Integer; +begin + Offset := IntPtr(FDest) - IntPtr(FStart); + if ASize < 0 then + ASize := Offset - ASize; + if ASize > FSize then + begin + FSize := ((ASize + FBlockSize) div FBlockSize) * FBlockSize; + SetLength(FValue, FSize); + FStart := @FValue[0]; + FDest := PByte(IntPtr(FStart) + Offset); + end; +end; + +function TQBytesCatHelper.Replicate(const ABytes: TBytes; ACount: Integer) + : TQBytesCatHelper; +var + l: Integer; +begin + Result := Self; + l := Length(ABytes); + if l > 0 then + begin + NeedSize(-l * ACount); + while ACount > 0 do + begin + Move(ABytes[0], FDest^, l); + Inc(FDest, l); + Dec(ACount); + end; + end; +end; + +procedure TQBytesCatHelper.Reset; +begin + FDest := FStart; +end; + +procedure TQBytesCatHelper.SetCapacity(const Value: Integer); +begin + if FSize <> Value then + NeedSize(Value); +end; + +procedure TQBytesCatHelper.SetPosition(const Value: Integer); +begin + if Value <= 0 then + FDest := FStart + else if Value > Length(FValue) then + begin + NeedSize(Value); + FDest := Pointer(IntPtr(FStart) + Value); + end + else + FDest := Pointer(IntPtr(FStart) + Value); +end; + +function NewId: TGuid; +begin + CreateGUID(Result); +end; + +function SameId(const V1, V2: TGuid): Boolean; +var + I1: array [0 .. 1] of Int64 absolute V1; + I2: array [0 .. 1] of Int64 absolute V2; +begin + Result := (I1[0] = I2[0]) and (I1[1] = I2[1]); +end; + +function StrLikeX(var S: PQCharW; pat: PQCharW; AIgnoreCase: Boolean): PQCharW; +const + CHAR_DIGITS = -1; + CHAR_NODIGITS = -2; + CHAR_SPACES = -3; + CHAR_NOSPACES = -4; +var + Accept: Boolean; + ACharCode, AEndCode: Integer; + AToken: QStringW; + ps, pt, os: PQCharW; + // >0 ַ + // <0 ⷶΧ + function Unescape(var t: PQCharW): Integer; + begin + if t^ = '\' then + begin + Inc(t); + case t^ of + 'b': + begin + Inc(t); + Result := 7; + end; + 'd': + begin + Inc(t); + Result := CHAR_DIGITS; + end; + 'D': + begin + Inc(t); + Result := CHAR_NODIGITS; + end; + 'r': + begin + Inc(t); + Result := 13; + end; + 'n': + begin + Inc(t); + Result := 10; + end; + 't': + begin + Inc(t); + Result := 9; + end; + 'f': // \f + begin + Inc(t); + Result := 12; + end; + 'v': // \v + begin + Inc(t); + Result := 11; + end; + 's': // հַ + begin + Inc(t); + Result := CHAR_SPACES; + end; + 'S': // ǿհ + begin + Inc(t); + Result := CHAR_NOSPACES; + end; + 'u': // Unicodeַ + begin + if IsHexChar(t[1]) and IsHexChar(t[2]) and IsHexChar(t[3]) and + IsHexChar(t[4]) then + begin + Result := (HexValue(t[1]) shl 12) or (HexValue(t[2]) shl 8) or + (HexValue(t[3]) shl 4) or HexValue(t[4]); + Inc(t, 5); + end + else + raise Exception.CreateFmt(SCharNeeded, + ['0-9A-Fa-f', StrDupW(t, 0, 4)]); + end + else + begin + Inc(t); + Result := Ord(S^); + end; + end; + end + else + begin + Result := Ord(t^); + end + end; + + function IsDigit: Boolean; + begin + Result := ((S^ >= '0') and (S^ <= '9')) or + ((S^ >= #65296) and (S^ <= #65305)); + end; + function IsMatch(AStart, AEnd: Integer): Boolean; + var + ACode: Integer; + begin + case AStart of + CHAR_DIGITS: + Result := IsDigit; + CHAR_NODIGITS: + Result := not IsDigit; + CHAR_SPACES: + Result := IsSpaceW(S); + CHAR_NOSPACES: + Result := not IsSpaceW(S) + else + begin + ACode := Ord(S^); + Result := (ACode >= AStart) and (ACode <= AEnd); + if (not Result) and AIgnoreCase then + begin + ACode := Ord(CharUpperW(S^)); + Result := (ACode >= AStart) and (ACode <= AEnd); + end; + // չַҪת + if Result and ((ACode >= $D800) and (ACode <= $DFFF)) then + begin + Inc(S); + if pat^ = '\' then + begin + ACode := Unescape(pat); + Result := Ord(S^) = ACode; + end + else + Result := False; + end; + end; + end; + end; + + function IsIn: Boolean; + const + SetEndChar: PQCharW = ']'; + begin + Result := False; + while (pat^ <> #0) and (pat^ <> ']') do + begin + ACharCode := Unescape(pat); + if pat^ = '-' then // a-zַΧ + begin + Inc(pat); + if pat^ <> ']' then + AEndCode := Unescape(pat) + else + begin + raise Exception.Create(SRangeEndNeeded); + end; + end + else + AEndCode := ACharCode; + Result := IsMatch(ACharCode, AEndCode); + if Result then // еĻԵж + begin + Inc(S); + SkipUntilW(pat, SetEndChar); + if pat^ <> ']' then + raise Exception.CreateFmt(SCharNeeded, [']']); + end + else + Inc(pat); + end; + end; + +begin + // SQL Like ﷨ + // _ һַ + // % * ַ + // [ַб] бַ + // [^ַб]/[!ַб] бַ + // ΪQDACչ + // \ ת + // \d ֣ȫǺͰǣ + // \D ֣ȫǣ + // \s հַ + // \S ǿհַ + os := S; + Result := nil; + while (pat^ <> #0) and (S^ <> #0) do + begin + case pat^ of + '_': + begin + Inc(S, CharSizeW(S)); + Inc(pat); + end; + '[': // ַб + begin + Inc(pat); + if (pat^ = '!') or (pat^ = '^') then + begin + Inc(pat); + Accept := not IsIn; + end + else + Accept := IsIn; + if pat^ = ']' then + begin + Inc(pat); + end; + if not Accept then + Exit; + end; + '\': + begin + ACharCode := Unescape(pat); + if not IsMatch(ACharCode, ACharCode) then + Exit + else + Inc(S); + end; + '*', '%': + begin + Inc(pat); + // ƥⳤȵַ + if pat^ = #0 then + begin + Result := os; + while S^ <> #0 do + Inc(S); + Exit; + end + else + begin + // *%Ϊ + while (pat^ = '%') or (pat^ = '*') do + Inc(pat); + ps := pat; + // ҵһƥ߽ + while (pat^ <> #0) and (pat^ <> '*') do + Inc(pat); + // ƥӴʣಿ + AToken := StrDupX(ps, (IntPtr(pat) - IntPtr(ps)) shr 1); + repeat + pt := S; + ps := StrLikeX(S, PQCharW(AToken), AIgnoreCase); + if ps <> nil then + begin + if (pat^ <> #0) and (StrLikeX(S, pat, AIgnoreCase) = nil) then + begin + Inc(pt); + S := pt; + end + else + begin + Result := os; + while S^ <> #0 do + Inc(S); + Exit; + end; + end + else + begin + Inc(pt); + S := pt; + end; + until (S^ = #0); + // ûƥ䵽˵ʧ + Exit; + end; + end + else // ַͨıȽ + begin + if not IsMatch(Ord(pat^), Ord(pat^)) then + Exit; + Inc(S); + Inc(pat); + end; + end; + end; + if (pat^ = '%') or (pat^ = '*') then // ģʽƥ + Inc(pat); + if pat^ = #0 then + Result := os; +end; + +function StrLikeW(S, pat: PQCharW; AIgnoreCase: Boolean): Boolean; +var + ps: PQCharW; +begin + ps := S; + Result := (StrLikeX(S, pat, AIgnoreCase) = ps) and (S^ = #0); +end; + +{ TQPagedList } + +function TQPagedList.Add(const p: Pointer): Integer; +begin + if Assigned(FOnCompare) then + begin + Find(p, Result); + Insert(Result, p); + end + else + begin + Result := FCount; + Insert(FCount, p); + end; +end; +{$IF RTLVersion<26} + +procedure TQPagedList.Assign(ListA: TList; AOperator: TListAssignOp; + ListB: TList); +var + I: Integer; + LTemp: TQPagedList; + LSource: TList; +begin + // ListB given? + if ListB <> nil then + begin + LSource := ListB; + Assign(ListA); + end + else + LSource := ListA; + + // on with the show + case AOperator of + + // 12345, 346 = 346 : only those in the new list + laCopy: + begin + Clear; + for I := 0 to LSource.count - 1 do + Add(LSource[I]); + end; + + // 12345, 346 = 34 : intersection of the two lists + laAnd: + for I := count - 1 downto 0 do + if LSource.IndexOf(Items[I]) = -1 then + Delete(I); + + // 12345, 346 = 123456 : union of the two lists + laOr: + for I := 0 to LSource.count - 1 do + if IndexOf(LSource[I]) = -1 then + Add(LSource[I]); + + // 12345, 346 = 1256 : only those not in both lists + laXor: + begin + LTemp := TQPagedList.Create; // Temp holder of 4 byte values + try + for I := 0 to LSource.count - 1 do + if IndexOf(LSource[I]) = -1 then + LTemp.Add(LSource[I]); + for I := count - 1 downto 0 do + if LSource.IndexOf(Items[I]) <> -1 then + Delete(I); + I := count + LTemp.count; + if Capacity < I then + Capacity := I; + for I := 0 to LTemp.count - 1 do + Add(LTemp[I]); + finally + LTemp.Free; + end; + end; + + // 12345, 346 = 125 : only those unique to source + laSrcUnique: + for I := count - 1 downto 0 do + if LSource.IndexOf(Items[I]) <> -1 then + Delete(I); + + // 12345, 346 = 6 : only those unique to dest + laDestUnique: + begin + LTemp := TQPagedList.Create; + try + for I := LSource.count - 1 downto 0 do + if IndexOf(LSource[I]) = -1 then + LTemp.Add(LSource[I]); + Assign(LTemp); + finally + LTemp.Free; + end; + end; + end; +end; +{$IFEND} + +procedure TQPagedList.Assign(ListA: TQPagedList; AOperator: TListAssignOp; + ListB: TQPagedList); +var + I: Integer; + LTemp, LSource: TQPagedList; +begin + // ListB given? + if ListB <> nil then + begin + LSource := ListB; + Assign(ListA); + end + else + LSource := ListA; + case AOperator of + // 12345, 346 = 346 : only those in the new list + laCopy: + begin + Clear; + for I := 0 to LSource.count - 1 do + Add(LSource[I]); + end; + // 12345, 346 = 34 : intersection of the two lists + laAnd: + for I := count - 1 downto 0 do + if LSource.IndexOf(Items[I]) = -1 then + Delete(I); + // 12345, 346 = 123456 : union of the two lists + laOr: + for I := 0 to LSource.count - 1 do + if IndexOf(LSource[I]) = -1 then + Add(LSource[I]); + // 12345, 346 = 1256 : only those not in both lists + laXor: + begin + LTemp := TQPagedList.Create; // Temp holder of 4 byte values + try + for I := 0 to LSource.count - 1 do + if IndexOf(LSource[I]) = -1 then + LTemp.Add(LSource[I]); + for I := count - 1 downto 0 do + if LSource.IndexOf(Items[I]) <> -1 then + Delete(I); + I := count + LTemp.count; + if Capacity < I then + Capacity := I; + for I := 0 to LTemp.count - 1 do + Add(LTemp[I]); + finally + LTemp.Free; + end; + end; + + // 12345, 346 = 125 : only those unique to source + laSrcUnique: + for I := count - 1 downto 0 do + if LSource.IndexOf(Items[I]) <> -1 then + Delete(I); + + // 12345, 346 = 6 : only those unique to dest + laDestUnique: + begin + LTemp := TQPagedList.Create; + try + for I := LSource.count - 1 downto 0 do + if IndexOf(LSource[I]) = -1 then + LTemp.Add(LSource[I]); + Assign(LTemp); + finally + LTemp.Free; + end; + end; + end; +end; + +procedure TQPagedList.CheckLastPage; +begin + while (FLastUsedPage > 0) and (FPages[FLastUsedPage].FUsedCount = 0) do + Dec(FLastUsedPage); +end; + +procedure TQPagedList.Clear; +var + I: Integer; + J: Integer; +begin + for I := 0 to High(FPages) do + begin + for J := 0 to FPages[I].FUsedCount - 1 do + DoDelete(FPages[I].FItems[J]); + FPages[I].FUsedCount := 0; + end; + FFirstDirtyPage := 1; + if Length(FPages) > 0 then + FLastUsedPage := 0 + else + FLastUsedPage := -1; + FCount := 0; +end; + +procedure TQPagedList.Pack; +var + ASource, ADest, AToMove: Integer; + procedure PackPages(AStartPage: Integer); + var + I: Integer; + begin + for I := AStartPage to High(FPages) do + FreeAndNil(FPages[I]); + SetLength(FPages, AStartPage); + FLastUsedPage := High(FPages); + end; + +begin + if count > 0 then + begin + if Length(FPages) = 1 then // ֻ1ҳҪ + Exit; + ADest := 0; + for ASource := 1 to High(FPages) do + begin + if FPages[ADest].FUsedCount < FPageSize then + begin + AToMove := FPages[ASource].FUsedCount; + if AToMove > FPageSize - FPages[ADest].FUsedCount then + AToMove := FPageSize - FPages[ADest].FUsedCount; + if AToMove > 0 then + begin + System.Move(FPages[ASource].FItems[0], + FPages[ADest].FItems[FPages[ADest].FUsedCount], + AToMove * SizeOf(Pointer)); + Inc(FPages[ADest].FUsedCount, AToMove); + if FPages[ASource].FUsedCount > AToMove then + System.Move(FPages[ASource].FItems[AToMove], + FPages[ASource].FItems[0], (FPages[ASource].FUsedCount - AToMove) + * SizeOf(Pointer)); + Dec(FPages[ASource].FUsedCount, AToMove); + FPages[ASource].FStartIndex := FPages[ADest].FStartIndex + + FPages[ADest].FUsedCount; + Inc(ADest); + end; + end; + end; + if FPages[ADest].FUsedCount = 0 then + PackPages(ADest) + else + PackPages(ADest + 1); + end + else + PackPages(0); +end; + +constructor TQPagedList.Create(APageSize: Integer); +begin + inherited Create; + if APageSize <= 0 then + APageSize := 4096; + FPageSize := APageSize; + FLastUsedPage := -1; +end; + +constructor TQPagedList.Create; +begin + Create(4096); +end; + +procedure TQPagedList.Delete(AIndex: Integer); +var + APage: Integer; +begin + APage := FindPage(AIndex); + if APage >= 0 then + begin + Dec(AIndex, FPages[APage].FStartIndex); + DoDelete(FPages[APage].FItems[AIndex]); + System.Move(FPages[APage].FItems[AIndex + 1], FPages[APage].FItems[AIndex], + SizeOf(Pointer) * (FPages[APage].FUsedCount - AIndex - 1)); + Dec(FPages[APage].FUsedCount); + CheckLastPage; + Dec(FCount); + Dirty(APage + 1); + end; +end; + +destructor TQPagedList.Destroy; +var + I: Integer; +begin + Clear; + for I := 0 to High(FPages) do + FreeObject(FPages[I]); +{$IFDEF UNICODE} + if Assigned(FOnCompare) and (TMethod(FOnCompare).Data = Pointer(-1)) then + TQPagedListSortCompareA(TMethod(FOnCompare).Code) := nil; +{$ENDIF} + inherited; +end; + +procedure TQPagedList.Dirty(APage: Integer); +begin + if APage < FFirstDirtyPage then + FFirstDirtyPage := APage; +end; + +function TQPagedList.DoCompare(p1, p2: Pointer): Integer; +begin + case IntPtr(TMethod(FOnCompare).Data) of + 0: // ȫֺ + TQPagedListSortCompareG(TMethod(FOnCompare).Code)(p1, p2, Result); +{$IFDEF UNICODE} + -1: // + TQPagedListSortCompareA(TMethod(FOnCompare).Code)(p1, p2, Result) +{$ENDIF} + else + FOnCompare(p1, p2, Result); + end; +end; + +procedure TQPagedList.DoDelete(const p: Pointer); +begin + if (p <> nil) and (ClassType <> TQPagedList) then + Notify(p, lnDeleted); +end; + +procedure TQPagedList.Exchange(AIndex1, AIndex2: Integer); +var + p1, p2: Integer; + t: Pointer; +begin + p1 := FindPage(AIndex1); + p2 := FindPage(AIndex2); + if (p1 <> -1) and (p2 <> -1) then + begin + Dec(AIndex1, FPages[p1].FStartIndex); + Dec(AIndex2, FPages[p2].FStartIndex); + t := FPages[p1].FItems[AIndex1]; + FPages[p1].FItems[AIndex1] := FPages[p2].FItems[AIndex2]; + FPages[p2].FItems[AIndex2] := t; + end; +end; + +function TQPagedList.Expand: TQPagedList; +begin + // ֻΪTListӿڱTQPagedListҪ + Result := Self; +end; + +function TQPagedList.Extract(Item: Pointer): Pointer; +begin + Result := ExtractItem(Item, FromBeginning); +end; + +function TQPagedList.ExtractItem(Item: Pointer; Direction: TDirection): Pointer; +var + I: Integer; +begin + Result := nil; + I := IndexOfItem(Item, Direction); + if I >= 0 then + begin + Result := Item; + Remove(I); + if ClassType <> TQPagedList then + Notify(Result, lnExtracted); + end; +end; + +function TQPagedList.Find(const p: Pointer; var AIdx: Integer): Boolean; +var + l, H, I, c: Integer; +begin + Result := False; + l := 0; + H := FCount - 1; + while l <= H do + begin + I := (l + H) shr 1; + c := DoCompare(Items[I], p); + if c < 0 then + l := I + 1 + else + begin + H := I - 1; + if c = 0 then + Result := True; + end; + end; + AIdx := l; +end; + +function TQPagedList.FindPage(AIndex: Integer): Integer; +var + l, H, I, AMax: Integer; +begin + l := 0; + if (FFirstDirtyPage < Length(FPages)) and + (AIndex >= FPages[FFirstDirtyPage - 1].FStartIndex + + FPages[FFirstDirtyPage - 1].FUsedCount) then + begin + for I := FFirstDirtyPage to High(FPages) do + begin + FPages[I].FStartIndex := FPages[I - 1].FStartIndex + FPages[I - 1] + .FUsedCount; + if FPages[I].FStartIndex > AIndex then + begin + Result := I - 1; + FFirstDirtyPage := I + 1; + Exit; + end + else if FPages[I].FStartIndex = AIndex then + begin + Result := I; + FFirstDirtyPage := I + 1; + Exit; + end; + end; + H := High(FPages); + end + else + H := FFirstDirtyPage - 1; + while l <= H do + begin + I := (l + H) shr 1; + AMax := FPages[I].FStartIndex + FPages[I].FUsedCount - 1; // + if AIndex > AMax then + l := I + 1 + else + begin + H := I - 1; + if (AIndex >= FPages[I].FStartIndex) and (AIndex <= AMax) then + begin + Result := I; + Exit; + end; + end; + end; + Result := -1; +end; + +function TQPagedList.First: Pointer; +begin + Result := Items[0]; +end; + +function TQPagedList.GetCapacity: Integer; +begin + Result := Length(FPages) * FPageSize; +end; + +function TQPagedList.GetEnumerator: TQPagedListEnumerator; +begin + Result := TQPagedListEnumerator.Create(Self); +end; + +function TQPagedList.GetItems(AIndex: Integer): Pointer; +var + p: Integer; +begin + p := FindPage(AIndex); + if p <> -1 then + begin + Dec(AIndex, FPages[p].FStartIndex); + Result := FPages[p].FItems[AIndex]; + end + else + raise Exception.Create('Խ:' + IntToStr(AIndex)); +end; + +function TQPagedList.GetList: TPointerList; +var + I, J, K: Integer; +begin + SetLength(Result, count); + K := 0; + for I := 0 to High(FPages) do + begin + for J := 0 to FPages[I].FUsedCount - 1 do + begin + Result[K] := FPages[I].FItems[J]; + Inc(K); + end; + end; +end; + +function TQPagedList.IndexOf(Item: Pointer): Integer; +var + I, J: Integer; +begin + Result := -1; + for I := 0 to High(FPages) do + begin + for J := 0 to FPages[I].FUsedCount do + begin + if FPages[I].FItems[J] = Item then + begin + Result := FPages[I].FStartIndex + J; + Exit; + end; + end; + end; +end; + +function TQPagedList.IndexOfItem(Item: Pointer; Direction: TDirection): Integer; +var + I, J: Integer; +begin + if Direction = FromBeginning then + Result := IndexOf(Item) + else + begin + Result := -1; + for I := High(FPages) downto 0 do + begin + for J := FPages[I].FUsedCount - 1 downto 0 do + begin + if FPages[I].FItems[J] = Item then + begin + Result := FPages[I].FStartIndex + J; + Exit; + end; + end; + end; + end; +end; + +procedure TQPagedList.Insert(AIndex: Integer; const p: Pointer); +begin + if Assigned(FOnCompare) then + Find(p, AIndex); + InternalInsert(AIndex, p) +end; + +procedure TQPagedList.InternalInsert(AIndex: Integer; const p: Pointer); +var + APage, ANewPage, AMoved: Integer; +begin + if AIndex >= count then // ĩβ + begin + APage := FLastUsedPage; + if (APage < 0) or (FPages[APage].FUsedCount = FPageSize) then + begin + Inc(APage); + if APage >= Length(FPages) then + begin + SetLength(FPages, Length(FPages) + 1); + FPages[APage] := TQListPage.Create(FPageSize); + FPages[APage].FStartIndex := count; + end; + Inc(FLastUsedPage); + if APage = 0 then + FFirstDirtyPage := 1; + end; + FPages[APage].FItems[FPages[APage].FUsedCount] := p; + Inc(FPages[APage].FUsedCount); + end + else if AIndex <= 0 then + begin + if FPages[0].FUsedCount < FPageSize then + begin + System.Move(FPages[0].FItems[0], FPages[0].FItems[1], + FPages[0].FUsedCount * SizeOf(Pointer)); + FPages[0].FItems[0] := p; + Inc(FPages[0].FUsedCount); + end + else // ǰҳˣҪҳ + begin + SetLength(FPages, Length(FPages) + 1); + FLastUsedPage := High(FPages); + System.Move(FPages[0], FPages[1], SizeOf(TQListPage) * High(FPages)); + FPages[0] := TQListPage.Create(FPageSize); + FPages[0].FUsedCount := 1; + FPages[0].FItems[0] := p; + end; + Dirty(1); + end + else + begin; + APage := FindPage(AIndex); + if (FPages[APage].FUsedCount = FPageSize) then + begin + if (High(FPages) = APage) or (FPages[APage + 1].FUsedCount = FPageSize) + then + // һҳҲ + begin + SetLength(FPages, Length(FPages) + 1); + FLastUsedPage := High(FPages); + ANewPage := APage + 1; + System.Move(FPages[ANewPage], FPages[ANewPage + 1], + SizeOf(TQListPage) * (High(FPages) - ANewPage)); + FPages[ANewPage] := TQListPage.Create(FPageSize); + FPages[ANewPage].FStartIndex := AIndex + 1; + Dec(AIndex, FPages[APage].FStartIndex); + AMoved := FPages[APage].FUsedCount - AIndex; + System.Move(FPages[APage].FItems[AIndex], FPages[ANewPage].FItems[0], + AMoved * SizeOf(Pointer)); + FPages[ANewPage].FUsedCount := AMoved; + Dec(FPages[APage].FUsedCount, AMoved - 1); + FPages[APage].FItems[AIndex] := p; + Dirty(ANewPage + 1); + end + else // ǰҳһҳ + begin + ANewPage := APage + 1; + System.Move(FPages[ANewPage].FItems[0], FPages[ANewPage].FItems[1], + FPages[ANewPage].FUsedCount * SizeOf(Pointer)); + FPages[ANewPage].FItems[0] := FPages[APage].FItems[FPageSize - 1]; + Inc(FPages[ANewPage].FUsedCount); + Dirty(ANewPage + 1); + Dec(AIndex, FPages[APage].FStartIndex); + AMoved := (FPages[APage].FUsedCount - AIndex); + System.Move(FPages[APage].FItems[AIndex], + FPages[APage].FItems[AIndex + 1], AMoved * SizeOf(Pointer)); + FPages[APage].FItems[AIndex] := p; + end; + end + else + begin + Dec(AIndex, FPages[APage].FStartIndex); + if AIndex >= FPages[APage].FUsedCount then + FPages[APage].FItems[AIndex] := p + else + begin + AMoved := (FPages[APage].FUsedCount - AIndex); + System.Move(FPages[APage].FItems[AIndex], + FPages[APage].FItems[AIndex + 1], AMoved * SizeOf(TQListPage)); + FPages[APage].FItems[AIndex] := p; + end; + Inc(FPages[APage].FUsedCount); + Dirty(APage + 1); + end; + end; + Inc(FCount); + if (p <> nil) and (ClassType <> TQPagedList) then + Notify(p, lnAdded); +end; + +function TQPagedList.Last: Pointer; +begin + Result := Items[count - 1]; +end; + +procedure TQPagedList.Move(AFrom, ATo: Integer); +begin + MoveTo(AFrom, ATo); +end; + +procedure TQPagedList.MoveTo(AFrom, ATo: Integer); +var + ATemp: Pointer; +begin + if AFrom <> ATo then + begin + ATemp := Items[AFrom]; + Remove(AFrom); + Insert(ATo, ATemp); + end; +end; + +procedure TQPagedList.Notify(Ptr: Pointer; Action: TListNotification); +begin + +end; + +function TQPagedList.Remove(Item: Pointer): Integer; +begin + Result := RemoveItem(Item, FromBeginning); +end; + +procedure TQPagedList.Remove(AIndex: Integer); +var + APage: Integer; +begin + APage := FindPage(AIndex); + if APage >= 0 then + begin + Dec(AIndex, FPages[APage].FStartIndex); + System.Move(FPages[APage].FItems[AIndex + 1], FPages[APage].FItems[AIndex], + SizeOf(Pointer) * (FPages[APage].FUsedCount - AIndex - 1)); + Dec(FPages[APage].FUsedCount); + CheckLastPage; + Assert(FPages[APage].FUsedCount >= 0); + Dirty(APage + 1); + end; +end; +{$IFDEF UNICODE} + +procedure TQPagedList.Sort(AOnCompare: TQPagedListSortCompareA); +begin + TQPagedListSortCompareA(TMethod(FOnCompare).Code) := AOnCompare; + TMethod(FOnCompare).Data := Pointer(-1); + Sort; +end; +{$ENDIF} + +procedure TQPagedList.SetCapacity(const Value: Integer); +begin + // Ϊݱʵʲκ +end; + +procedure TQPagedList.SetItems(AIndex: Integer; const Value: Pointer); +var + p: Integer; +begin + p := FindPage(AIndex); + if p <> -1 then + begin + Dec(AIndex, FPages[p].FStartIndex); + FPages[p].FItems[AIndex] := Value; + end + else + raise Exception.Create('Խ:' + IntToStr(AIndex)); +end; + +procedure TQPagedList.SetOnCompare(const Value: TQPagedListSortCompare); +begin + if (TMethod(FOnCompare).Code <> TMethod(Value).Code) or + (TMethod(FOnCompare).Data <> TMethod(Value).Data) then + begin + FOnCompare := Value; + if Assigned(Value) then + Sort; + end; +end; + +procedure TQPagedList.Sort(AOnCompare: TQPagedListSortCompareG); +begin + TMethod(FOnCompare).Code := @AOnCompare; + TMethod(FOnCompare).Data := nil; + Sort; +end; + +procedure TQPagedList.Sort; + procedure QuickSort(l, R: Integer); + var + I, J, p: Integer; + begin + repeat + I := l; + J := R; + p := (l + R) shr 1; + repeat + while DoCompare(Items[I], Items[p]) < 0 do + Inc(I); + while DoCompare(Items[J], Items[p]) > 0 do + Dec(J); + if I <= J then + begin + if I <> J then + Exchange(I, J); + if p = I then + p := J + else if p = J then + p := I; + Inc(I); + Dec(J); + end; + until I > J; + if l < J then + QuickSort(l, J); + l := I; + until I >= R; + end; + +begin + if not Assigned(FOnCompare) then + raise Exception.Create('δָ'); + if count > 0 then + QuickSort(0, count - 1); +end; + +function TQPagedList.RemoveItem(Item: Pointer; Direction: TDirection): Integer; +begin + Result := IndexOfItem(Item, Direction); + if Result > 0 then + Remove(Result); +end; + +{ TQListPage } + +constructor TQListPage.Create(APageSize: Integer); +begin + SetLength(FItems, APageSize); +end; + +{ TQPagedListEnumerator } + +constructor TQPagedListEnumerator.Create(AList: TQPagedList); +begin + inherited Create; + FList := AList; + FIndex := -1; +end; + +function TQPagedListEnumerator.GetCurrent: Pointer; +begin + Result := FList[FIndex]; +end; + +function TQPagedListEnumerator.MoveNext: Boolean; +begin + Result := FIndex < FList.count - 1; + if Result then + Inc(FIndex); +end; + +{ TQPagedStream } +constructor TQPagedStream.Create; +begin + Create(8192); +end; + +function TQPagedStream.ActiveOffset: Integer; +begin + Result := FPosition mod FPageSize; +end; + +function TQPagedStream.ActivePage: Integer; +begin + Result := FPosition div FPageSize; +end; + +procedure TQPagedStream.Clear; +var + I: Integer; +begin + for I := 0 to High(FPages) do + FreeMem(FPages[I]); + SetLength(FPages, 0); + FSize := 0; + FPosition := 0; +end; + +constructor TQPagedStream.Create(APageSize: Integer); +begin + inherited Create; + if APageSize <= 0 then + APageSize := 8192; + FPageSize := APageSize; +end; + +destructor TQPagedStream.Destroy; +begin + Clear; + inherited; +end; + +function TQPagedStream.GetAsBytes: TBytes; +begin + if Size > 0 then + begin + SetLength(Result, FSize); + FPosition := 0; + Read(Result[0], FSize); + end + else + FSize := 0; +end; + +function TQPagedStream.GetBytes(AIndex: Int64): Byte; +begin + if AIndex + 1 > FSize then + Result := 0 + else + Result := PByte(IntPtr(FPages[AIndex div FPageSize]) + + (AIndex mod FPageSize))^; +end; + +function TQPagedStream.GetSize: Int64; +begin + Result := FSize; +end; + +procedure TQPagedStream.LoadFromFile(const FileName: string); +var + AStream: TStream; +begin + AStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(AStream); + finally + FreeAndNil(AStream); + end; +end; + +procedure TQPagedStream.LoadFromStream(Stream: TStream); +var + ACount: Int64; +begin + ACount := Stream.Size - Stream.Position; + Capacity := ACount; + CopyFrom(Stream, ACount); +end; + +procedure TQPagedStream.PageNeeded(APageIndex: Integer); +begin + if High(FPages) < APageIndex then + Capacity := (APageIndex + 1) * FPageSize - 1; +end; + +function TQPagedStream.Read(var Buffer; count: Longint): Longint; +var + ACanRead: Int64; + pBuf: PByte; + APage, APageSpace, APageOffset, AToRead: Integer; +begin + ACanRead := FSize - FPosition; + Result := 0; + if ACanRead >= count then + begin + if ACanRead < count then + count := ACanRead; + pBuf := @Buffer; + while count > 0 do + begin + APage := ActivePage; + APageOffset := ActiveOffset; + APageSpace := FPageSize - ActiveOffset; + if count > APageSpace then + AToRead := APageSpace + else + AToRead := count; + Dec(count, AToRead); + Move(PByte(IntPtr(FPages[APage]) + APageOffset)^, pBuf^, AToRead); + Inc(pBuf, AToRead); + Inc(Result, AToRead); + Inc(FPosition, AToRead); + end; + end; +end; + +function TQPagedStream.Read(Buffer: TBytes; Offset, count: Longint): Longint; +begin + if count > 0 then + Result := Read(Buffer[Offset], count) + else + Result := 0; +end; + +procedure TQPagedStream.SaveToFile(const FileName: string); +var + AStream: TFileStream; +begin + AStream := TFileStream.Create(FileName, fmCreate); + try + SaveToStream(AStream); + finally + FreeAndNil(AStream); + end; +end; + +procedure TQPagedStream.SaveToStream(Stream: TStream); +begin + Stream.CopyFrom(Self, 0); +end; + +function TQPagedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; +begin + case Origin of + soBeginning: + Result := Offset; + soCurrent: + Result := FPosition + Offset; + soEnd: + Result := FSize - Offset + else + Result := 0; + end; + if Result > FSize then + Result := FSize + else if Result < 0 then + Result := 0; + FPosition := Result; +end; + +procedure TQPagedStream.SetSize(const NewSize: Int64); +begin + Capacity := NewSize; +end; + +procedure TQPagedStream.SetAsBytes(const Value: TBytes); +begin + Size := Length(Value); + if Size > 0 then + WriteBuffer(Value[0], Size); +end; + +procedure TQPagedStream.SetBytes(AIndex: Int64; const Value: Byte); +begin + if FSize < AIndex + 1 then + Size := AIndex + 1; + PByte(IntPtr(FPages[AIndex div FPageSize]) + (AIndex mod FPageSize))^ + := Value; +end; + +procedure TQPagedStream.SetCapacity(Value: Int64); +var + APageNum: Int64; + I: Integer; +begin + if Value < 0 then + Value := 0; + APageNum := (Value div FPageSize); + if (Value mod FPageSize) <> 0 then + Inc(APageNum); + if FCapacity <> APageNum * FPageSize then + begin + FCapacity := APageNum * FPageSize; + if Length(FPages) > APageNum then + begin + I := High(FPages); + while I >= APageNum do + begin + FreeMem(FPages[I]); + Dec(I); + end; + SetLength(FPages, APageNum); + end + else + begin + I := Length(FPages); + SetLength(FPages, APageNum); + while I < APageNum do + begin + GetMem(FPages[I], FPageSize); + Inc(I); + end; + end; + end; +end; + +procedure TQPagedStream.SetSize(NewSize: Longint); +begin + Capacity := NewSize; +end; + +function TQPagedStream.Write(const Buffer: TBytes; + Offset, count: Longint): Longint; +begin + if count > 0 then + Result := Write(Buffer[Offset], count) + else + Result := 0; +end; + +function TQPagedStream.Write(const Buffer; count: Longint): Longint; +var + ADest: PByte; + APageIndex, APageOffset, APageSpace: Integer; + AOffset: Int64; + pBuf: PByte; +begin + Result := 0; + if count > 0 then + begin + AOffset := FPosition + count; + PageNeeded(AOffset div FPageSize); + APageIndex := ActivePage; + APageOffset := ActiveOffset; + APageSpace := FPageSize - APageOffset; + pBuf := @Buffer; + while count > 0 do + begin + ADest := PByte(IntPtr(FPages[APageIndex]) + APageOffset); + if APageSpace < count then + begin + Move(pBuf^, ADest^, APageSpace); + Inc(APageIndex); + Dec(count, APageSpace); + Inc(Result, APageSpace); + Inc(pBuf, APageSpace); + APageOffset := 0; + APageSpace := FPageSize; + end + else + begin + Move(pBuf^, ADest^, count); + Inc(Result, count); + Break; + end; + end; + Inc(FPosition, Result); + if FSize < FPosition then + FSize := FPosition; + end; +end; + +const + PR_ORDERED = 9; // ˳ʱ12345ʱÿظַСȨֵ + PR_REPEAT = 5; // ظʱaaaaʱÿظٵȨֵ + PR_CHARTYPE = 20; // ÿһͬ͵ַʱӵȨֵ + PR_LENGTH = 10; // ÿһַʱӵȨֵ + PR_CHART = 50; // ֺĸĿַʱӵȨֵ + PR_UNICODE = 70; // UnicodeַʱӵȨֵ + +function PasswordScale(const S: QStringW): Integer; +var + p: PQCharW; + ARules: TPasswordRules; + AMaxOrder, AMaxRepeat, ACharTypes: Integer; + function RepeatCount: Integer; + var + t: PQCharW; + begin + t := p; + Inc(t); + Result := 0; + while t^ = p^ do + begin + Inc(Result); + Inc(t); + end; + if Result > AMaxRepeat then + AMaxRepeat := Result; + end; + + function OrderCount: Integer; + var + t, tl: PQCharW; + AStep: Integer; + begin + t := p; + tl := p; + Inc(t); + AStep := Ord(t^) - Ord(p^); + Result := 0; + while Ord(t^) - Ord(tl^) = AStep do + begin + Inc(Result); + tl := t; + Inc(t); + end; + if Result > AMaxOrder then + AMaxOrder := Result; + end; + +begin + if LowerCase(S) = 'password' then + Result := 0 + else + begin + Result := Length(S) * PR_LENGTH; + p := PQCharW(S); + ARules := []; + AMaxOrder := 0; + AMaxRepeat := 0; + while p^ <> #0 do + begin + if (p^ >= '0') and (p^ <= '9') then + ARules := ARules + [prIncNumber] + else if (p^ >= 'a') and (p^ <= 'z') then + ARules := ARules + [prIncLowerCase] + else if (p^ >= 'A') and (p^ <= 'Z') then + ARules := ARules + [prIncUpperCase] + else if p^ > #$7F then + ARules := ARules + [prIncUnicode] + else + ARules := ARules + [prIncChart]; + if RepeatCount > 2 then + ARules := ARules + [prRepeat]; + if OrderCount > 2 then + ARules := ARules + [prSimpleOrder]; + Inc(p); + end; + if prSimpleOrder in ARules then + Result := Result - AMaxOrder * PR_ORDERED; + if prRepeat in ARules then + Result := Result - AMaxRepeat * PR_REPEAT; + ACharTypes := 0; + if prIncNumber in ARules then + Inc(ACharTypes); + if prIncLowerCase in ARules then + Inc(ACharTypes); + if prIncUpperCase in ARules then + Inc(ACharTypes); + if prIncChart in ARules then + begin + Inc(ACharTypes); + Result := Result + PR_CHART; + end; + if prIncUnicode in ARules then + begin + Inc(ACharTypes); + Result := Result + PR_UNICODE; + end; + Result := Result + (ACharTypes - 1) * PR_CHARTYPE; + // ǿȵȡֵΧ<0 + if Result < 0 then + Result := 0; + end; +end; + +function CheckPassword(const AScale: Integer): TPasswordStrongLevel; overload; +begin + if AScale < 60 then + Result := pslLowest + else if AScale < 100 then + Result := pslLower + else if AScale < 200 then + Result := pslNormal + else if AScale < 300 then + Result := pslHigher + else + Result := pslHighest; +end; + +function CheckPassword(const S: QStringW): TPasswordStrongLevel; overload; +begin + Result := CheckPassword(PasswordScale(S)); +end; + +{ TQBits } + +function TQBits.GetIsSet(AIndex: Integer): Boolean; +begin + if (AIndex < 0) or (AIndex >= Size) then + Result := False + else + Result := (FBits[AIndex shr 3] and ($80 shr (AIndex and $7))) <> 0; +end; + +function TQBits.GetSize: Integer; +begin + Result := Length(FBits) shl 3; +end; + +procedure TQBits.SetIsSet(AIndex: Integer; const Value: Boolean); +var + AByteIdx: Integer; +begin + if (AIndex < 0) or (AIndex >= Size) then + raise QException.CreateFmt(SOutOfIndex, [AIndex, 0, Size - 1]); + AByteIdx := AIndex shr 3; + if Value then + FBits[AByteIdx] := FBits[AByteIdx] or ($80 shr (AIndex and $7)) + else + FBits[AByteIdx] := FBits[AByteIdx] and (not($80 shr (AIndex and $7))); +end; + +procedure TQBits.SetSize(const Value: Integer); +begin + if (Value and $7) <> 0 then + SetLength(FBits, (Value shr 3) + 1) + else + SetLength(FBits, Value shr 3); +end; + +initialization + +{$IFDEF MSWINDOWS} + hMsvcrtl := LoadLibrary('msvcrt.dll'); +if hMsvcrtl <> 0 then +begin + VCStrStr := TMSVCStrStr(GetProcAddress(hMsvcrtl, 'strstr')); + VCStrStrW := TMSVCStrStrW(GetProcAddress(hMsvcrtl, 'wcsstr')); + VCMemCmp := TMSVCMemCmp(GetProcAddress(hMsvcrtl, 'memcmp')); +end +else +begin + VCStrStr := nil; + VCStrStrW := nil; + VCMemCmp := nil; +end; +{$ENDIF} +IsFMXApp := GetClass('TFmxObject') <> nil; + +finalization + +{$IFDEF MSWINDOWS} +if hMsvcrtl <> 0 then + FreeLibrary(hMsvcrtl); +{$ENDIF} + +end. diff --git a/qdac/qdac.inc b/qdac/qdac.inc new file mode 100644 index 0000000..9bbdfad --- /dev/null +++ b/qdac/qdac.inc @@ -0,0 +1,9 @@ +{$DEFINE QDAC} + +{$IF RTLVersion<18} +{$MESSAGE Error '!!!QDAC Only test in 2007 and XE6,No support in other version!!!'} +{$IFEND =24} +{$LEGACYIFEND ON} +{$IFEND} + diff --git a/qdac/qjson.pas b/qdac/qjson.pas new file mode 100644 index 0000000..4b4b3aa --- /dev/null +++ b/qdac/qjson.pas @@ -0,0 +1,5889 @@ +unit qjson; +{$I 'qdac.inc'} + +interface + +{ + ԴQDACĿȨswish(QQ:109867294)С + (1)ʹɼ + ɸơַ޸ıԴ룬޸Ӧ÷ߣڱҪʱ + ϲĿԹʹãϲԴͬѭQDACȨơ + IJƷĹУӦµİ汾: + ƷʹõJSONQDACĿеQJSONȨС + (2)֧ + м⣬ԼQDACٷQQȺ250530692ͬ̽֡ + (3) + ʹñԴҪ֧κηáñԴа + Ŀǿƣʹ߲ΪȣиľΪָõƷ + ʽ + ֧ guansonghuan@sina.com + У + + ˺ţ4367 4209 4324 0179 731 + Угŷ索 +} + +{ ޶־ + 2015.7.20 + ========== + * ToRtti ʱԲĬֵԵĿƷ + * ToRtti FromRtti TDateTime Ե⣨лһƽ + 2015.6.23 + ========= + + TQJsonStreamHelper ֱд JSON ʽݣŵʡ + TQJsonĴάٶȸ죬ȱҪпƸIJ裨Ͷıպϣ + 2015.5.22 + ========= + + ӶƱΪBase64Ĭú EncodeJsonBinaryAsBase64ҪָĬϣ + EncodeJsonBinaryAsHex + + 2015.4.21 + ========= + + IgnoreCase ڿJsonִСдԣĬϼ̳ȫֵJsonCaseSensitiveֵֺ뽨飩 + + Root ڷظ + * HashName TQHashedJson Ƶ TQJson + 2015.2.6 + ========= + * AsInteger/AsFloat ʱ֧ʮƵ + + 2015.2.1 + ========= + * ޸˽ͱΪΪַJSON루л Synopse + 2015.1.26 + ========== + + ޲Deleteɾ + + 2015.1.19 + ========== + * ˱ʱһЩضַΪԱֶԼƱĽӿɶԣľ飩 + * ظ FromRtti ʱûĩֵ⣨ľ棩 + + 2015.1.13 + ========== + * TQHashedJson IndexOf δȷСд + * TQHashedJson ڽɺδȷ¼ϣֵ⣨ľ棩 + * ˽ֵʱ Parse ʱδȷƳƿո + + 2015.1.6 + ======== + * SetAsVariantԷDZ׼ı͵֧⣨С㱨棩 + + 2015.1.5 + ========= + * IsChildOfһжϴɿܷAV쳣 + + 2015.1.4 + ========= + * ޸ ItemByName IJִ룬ûȷ JsonCaseSensitive ǵ,ɺԴСдЧ(ľ + * ItemByName ±괦߼ + * ItemByPath ֶ֧ά + 2015.1.3 + ========= + * SaveToStream/SaveToFile һADoFormatԱǷʽֺ롢ľ飩 + 2014.12.24 + ========== + * ˽Jsonаעʱȫ⣨kylix2008棩 + + 2014.12.13 + ========== + + HasChildжָ·ӽǷڣľ飩 + + 2014.11.25 + ========== + * ޸ItemByPathĴ룬ְ֧˳jdtObject͵ӳԱ + + 2014.11.24 + ========== + * ToRtti.FoArrayΪδҵʱʾ쳣 + + 2014.11.20 + ========== + + AsBytesԣֶ֧ͣĬʵֱʹõʮַ + ϲOnQJsonEncodeBytesOnQJsonDecodeBytes¼滻ΪԼʵ֣ + ZLib+Base64 + + ValueFromStream/ValueFromFile/StreamFromValue/StreamFromFile + + 2014.11.14 + ========== + * GetAsVariantʱûдjdtNull͵ + + 2014.11.13 + ========== + + IsBoolжϵǰֵǷתΪֵʣ¹_???-飩 + 2014.11.10 + ========== + * FromRtti/ToRttiڴTCollectionʱڵ(ľ) + * FromRttiToObjectӺһ(hq200306) + + 2014.11.6 + ========== + * FromRttiʱԪӽʱANameдNameɽδ⣨ľ棩 + + IntByPath,IntByNameBoolByPath,BoolByName,FloatByPath,FloatByName,DateTimeByPath, + DateTimeByNameԼжϱ(FreeSpace8) + + 2014.10.30 + ========== + + DetachAttachToMoveToRemove + * JsonԱMoveToAttachToʱԪδ + + 2014.9.11 + ========= + * ˴ļмؿհJSONͶʱ⣨ֺ뱨棩 + * ޸ֱӽǶֵ浽еIJԣٷʱ棩 + 1JSONѾָ򱣴ΪһӶ + 2δָƣΪδ֪ΪjdtNull򲻱κ + 2014.9.3 + ========= + * ˽ֵַʱܶʧַ(ľ) + + 2014.8.27 + ========= + * ˽ǰעʱ(ľ) + 2014.8.15 + ========= + * AddԶʱضʽ11,23ʱ(Tuesday) + 2014.7.31 + ========= + * ˽ʱйϵͳ쳣޷ʾ(Сױ) + * ˽ʱѭ⣨Сױ棩 + * ˳쳣ʱ쳣ʾظ + * ForcePathʱ'array[].subobjectname'δȷ·(Сױ) + 2014.7.28 + ========= + * ToRttiԴʱͣJSONΪnullʱ(ֺ걨) + * ޸ToRecordΪvarconst(ֺ걨) + 2014.7.16 + ========= + * GetPathʱδʼַPathԿܳ(Сױ) + 2014.7.6 + ========= + + ToRttiԾ̬͵֧ + + 2014.7.3 + ========= + * Assignʱ˵ǰƵ + + 2014.7.1 + ========= + * AsString޸jdtNull/jdtUnknownʱΪؿַ + 2014.6.28 + ========= + * ForcePath('Items[]')Ĭ˿ӽ(pony,) + + JsonRttiEnumAsIntȫѡöֵͼֵǷ񱣴ַĬΪTrue(ֺ뽨) + 2014.6.27 + ========= + + TryParseֺ뽨飩 + * ޸EncodeʱԼҲӵ˽ַе⣨ֺ뱨棩 + * FromRTTIʱڷ¼ûнй˵ + * ToRtti.ToArrayʱڶ̬óʱʹ󣨻ֺ뱨棩 + 2014.6.26 + ========== + * ToRtti.ToRecordӺʱĴ(лȺѻֺRTTIͲ) + * HPPEMITĬӱԪ(ٷ ) + 2014.6.23 + ========== + + FromRecordֶ֧̬ͨ + 2014.6.21 + ========== + * ƳԭAddObject/AddRecord/ToObject/ToRecord֧ + + FromRtti/ToRtti/FromRecord/ToRecord/ToRttiValue֧֣滻ԭRTTI + + Invokeֱ֧ͨJsonöӦĺοDemo + 2014.6.17 + ========= + * AsFloatֵʱNanInfiniteNegInfiniteЧֵļ + * AsVariantֵʱvarNull,varEmpty,varUnknown,varUInt64͵֧ + 2014.5.27 + ========== + + TQHashedJson ֧֣һѯŻİ汾ʹùϣӿItemByNameIJѯٶȣ + ӦдʹItemByNameItemByPathȲѯʹTQJsonӦֱ + ʹTQJson + + 2014.5.14 + ========= + + CopyIf/DeleteIf/FindIf + + for..in﷨֧ + * EncodeForcePathܴڵ + + 2014.5.6 + ======== + + ParseBlock֧ʽͷֶν + * ˽\uxxxxʱʶ + * ޸ParseΪӽ + + 2014.5.4 + ======== + + JavaScript.netʱ/DATE(MillSeconds+TimeZone)/ʽ֧ + * Jsonּ֧VCLTDateTime֧֣ɵJSONĬJsonDateFormat + JsonTimeFormat,JsonDateTimeFormatƣStrictJsonΪTrue + /DATE(MillSeconds+TimeZone)/ʽ + ע + ʱͽʱJSONʵΪַַٴδʱ + ʧϢԿֱAsDateTimeдʱʹ + JavaScript.netʽҰʱϢʱ佫תΪʱ䡣 + + 2014.5.1 + ======== + + AddRecordֱ֧ӱ¼ݣ͵ijԱᱻ + (Class)(Method)ӿ(Interface)(ClassRef),ָ(Pointer)(Procedure) + ܸʵҪǷ֧ + + ToRecordJsonֱӵ¼͵ת + + CopyڴǰһʵעĿǰ汾¡ڲCopyܸĵ + * Assignһ +} +// ԻΪDelphi 2007XE6汾Ŀ޸ +uses classes, sysutils, math, qstring, typinfo, qrbtree, + EncdDecd{$IF RTLVersion>27}, + System.NetEncoding{$IFEND} +{$IFDEF MSWINDOWS}, windows{$ENDIF} +{$IFDEF UNICODE}, Generics.Collections{$ENDIF}{$IF RTLVersion>=21}, + Rtti{$IFEND >=XE10} +{$IF RTLVersion<22}// 2007-2010 + , PerlRegEx, pcre +{$ELSE} + , RegularExpressionsCore +{$IFEND} + ; +{$M+} +{$HPPEMIT '#pragma link "qjson"'} +{$HPPEMIT '#pragma comment(lib,"soaprtl")'} +// ҪʹʾʽTForm1.FormCreate,Ķ壬򷽷ΪForm1.FormCreate +{ .$DEFINE TYPENAMEASMETHODPREF } +type + /// ԪQDACɲ֣QDACȨƣQDACվ˽ + /// + /// JSONԪڿٽάJSONṹ.ȫֱStrictJsonΪFalseʱ֧ + /// עͺƲ'"' + /// + /// TQJsonDataTypeڼ¼JSONԪͣȡֵ + /// + /// + /// jdtUnknownδֻ֪ͣ¹δֵʱŻǸ + /// + /// + /// jdtNullNULL + /// + /// + /// jdtStringַ + /// + /// + /// jdtInteger(Int64,ֵڲʹ64λ) + /// + /// + /// jdtFloat˫ȸ(Double) + /// + /// + /// jdtBoolean + /// + /// + /// jdtDateTimeʱ + /// + /// + /// jdtArray + /// + /// + /// jdtObject + /// + /// + TQJsonDataType = (jdtUnknown, jdtNull, jdtString, jdtInteger, jdtFloat, + jdtBoolean, jdtDateTime, jdtArray, jdtObject); + TQJson = class; +{$IF RTLVersion>=21} + /// + /// RTTIϢ˻صXE6֧XEǰİ汾¼ص + /// + /// ¼TQJson + /// (AddObject)ֶ(AddRecord) + /// ԻֶεϢ + /// Ƿ¼Իֶ + /// ûԶĸݳԱ + TQJsonRttiFilterEventA = reference to procedure(ASender: TQJson; + AObject: Pointer; AName: QStringW; AType: PTypeInfo; var Accept: Boolean; + ATag: Pointer); + /// + /// ˴XE6֧ + /// + /// ¼TQJson + /// Ҫ˵Ķ + /// ǷҪö + /// ûӵ + TQJsonFilterEventA = reference to procedure(ASender, AItem: TQJson; + var Accept: Boolean; ATag: Pointer); +{$IFEND >=2010} + /// + /// RTTIϢ˻صXE6֧XEǰİ汾¼ص + /// + /// ¼TQJson + /// (AddObject)ֶ(AddRecord) + /// ԻֶεϢ + /// Ƿ¼Իֶ + /// ûԶĸݳԱ + TQJsonRttiFilterEvent = procedure(ASender: TQJson; AObject: Pointer; + AName: QStringW; AType: PTypeInfo; var Accept: Boolean; ATag: Pointer) + of object; + /// + /// ˴XE6֧ + /// + /// ¼TQJson + /// Ҫ˵Ķ + /// ǷҪö + /// ûӵ + TQJsonFilterEvent = procedure(ASender, AItem: TQJson; var Accept: Boolean; + ATag: Pointer) of object; + PQJson = ^TQJson; +{$IFDEF UNICODE} + TQJsonItemList = TList; +{$ELSE} + TQJsonItemList = TList; +{$ENDIF} + /// + /// TQJsonTagTypeڲAddObjectAddRecordڲʹ + /// + /// + /// + /// ttAnonEventص + /// ttNameFilterԻԱƹ + /// + TQJsonTagType = (ttAnonEvent, ttNameFilter); + PQJsonInternalTagData = ^TQJsonInternalTagData; + + /// + /// TQJsonInternalTagDataAddRecordAddObjectҪڲRTTIϢʱʹ + /// + TQJsonInternalTagData = record + /// Tagݵ + TagType: TQJsonTagType; +{$IF RTLVersion>=21} + /// ʹõ + OnEvent: TQJsonRttiFilterEventA; +{$IFEND >=2010} + /// ܵ(AddObject)¼ֶ(AddRecord)ƣͬʱIgnoreNames֣IgnoreNamesϢ + AcceptNames: QStringW; + /// Ե(AddObject)¼ֶ(AddRecord)ƣͬʱAcceptNamedsAcceptNames + IgnoreNames: QStringW; + /// ԭʼݸAddObjectAddRecordĸݳԱݸOnEventTagԹûʹ + Tag: Pointer; + end; + + TQJsonEnumerator = class; + /// ⲿֶ֧صĺһµQJSONעӳдĶ + /// ´QJSON + TQJsonCreateEvent = function: TQJson; + /// ⲿ󻺴棬Աö + /// ҪͷŵJson + TQJsonFreeEvent = procedure(AJson: TQJson); + + TQJsonEncodeBytesEvent = procedure(const ABytes: TBytes; + var AResult: QStringW); + TQJsonDecodeBytesEvent = procedure(const S: QStringW; var AResult: TBytes); + + EJsonError = class(Exception) + + end; + + /// + /// TQJsonڽάJSONʽĶͣҪʹǰҪڶдӦʵ + /// TQJsonTQXMLھӿϱһ£JsonϢXMLû + /// ϢʼΪַٲֽӿڻвͬ. + /// ʵֲͬQJSONеͶͬһʵ֣DataTypeIJͬʹ + /// ͬijԱʡΪjdtArrayjdtObjectʱӽ. + /// + TQJson = class + private + function GetRoot: TQJson; + protected + FName: QStringW; + FNameHash: Cardinal; + FDataType: TQJsonDataType; + FValue: QStringW; + FParent: TQJson; + FData: Pointer; + FItems: TQJsonItemList; + FIgnoreCase: Boolean; + function GetValue: QStringW; + procedure SetValue(const Value: QStringW); + procedure SetDataType(const Value: TQJsonDataType); + function GetAsBoolean: Boolean; + function GetAsFloat: Extended; + function GetAsInt64: Int64; + function GetAsInteger: Integer; + function GetAsString: QStringW; + procedure SetAsBoolean(const Value: Boolean); + procedure SetAsFloat(const Value: Extended); + procedure SetAsInt64(const Value: Int64); + procedure SetAsInteger(const Value: Integer); + procedure SetAsString(const Value: QStringW); + function GetAsObject: QStringW; + procedure SetAsObject(const Value: QStringW); + function GetAsDateTime: TDateTime; + procedure SetAsDateTime(const Value: TDateTime); + function GetCount: Integer; + function GetItems(AIndex: Integer): TQJson; + class function CharUnescape(var p: PQCharW): QCharW; + class function CharEscape(c: QCharW; pd: PQCharW): Integer; + procedure ArrayNeeded(ANewType: TQJsonDataType); + procedure ValidArray; + procedure ParseObject(var p: PQCharW); + function ParseJsonPair(ABuilder: TQStringCatHelperW; + var p: PQCharW): Integer; + function ParseName(ABuilder: TQStringCatHelperW; var p: PQCharW): Integer; + procedure ParseValue(ABuilder: TQStringCatHelperW; var p: PQCharW); + function FormatParseError(ACode: Integer; AMsg: QStringW; ps, p: PQCharW) + : QStringW; + procedure RaiseParseException(ACode: Integer; ps, p: PQCharW); + function TryParseValue(ABuilder: TQStringCatHelperW; + var p: PQCharW): Integer; + function BooleanToStr(const b: Boolean): QStringW; + function GetIsNull: Boolean; + function GetIsNumeric: Boolean; + function GetIsArray: Boolean; + function GetIsObject: Boolean; + function GetIsString: Boolean; + function GetIsDateTime: Boolean; + function GetAsArray: QStringW; + procedure SetAsArray(const Value: QStringW); + function GetPath: QStringW; + function GetAsVariant: Variant; + procedure SetAsVariant(const Value: Variant); + function GetAsJson: QStringW; + procedure SetAsJson(const Value: QStringW); + function GetItemIndex: Integer; + function ParseJsonTime(p: PQCharW; var ATime: TDateTime): Boolean; + function CreateJson: TQJson; virtual; + procedure FreeJson(AJson: TQJson); inline; + procedure CopyValue(ASource: TQJson); inline; + procedure Replace(AIndex: Integer; ANewItem: TQJson); virtual; + procedure InternalRttiFilter(ASender: TQJson; AObject: Pointer; + APropName: QStringW; APropType: PTypeInfo; var Accept: Boolean; + ATag: Pointer); + function InternalEncode(ABuilder: TQStringCatHelperW; ADoFormat: Boolean; + ADoEscape: Boolean; ANullConvert: Boolean; const AIndent: QStringW) + : TQStringCatHelperW; + function ArrayItemTypeName(ATypeName: QStringW): QStringW; + function ArrayItemType(ArrType: PTypeInfo): PTypeInfo; + procedure DoJsonNameChanged(AJson: TQJson); virtual; + procedure SetName(const Value: QStringW); + function GetIsBool: Boolean; + function GetAsBytes: TBytes; + procedure SetAsBytes(const Value: TBytes); + class function SkipSpaceAndComment(var p: PQCharW): Integer; + procedure DoParsed; virtual; + procedure SetIgnoreCase(const Value: Boolean); + function HashName(const S: QStringW): TQHashType; + public + /// + constructor Create; overload; + constructor Create(const AName, AValue: QStringW; + ADataType: TQJsonDataType = jdtUnknown); overload; + /// + destructor Destroy; override; + { + ҪӵĽ + ӵĽ + } + function Add(ANode: TQJson): Integer; overload; + /// һδJSONӽ + /// ӵĽʵ + /// + /// һ£ͣӦδʵ + /// + function Add: TQJson; overload; + /// һ + /// ҪӵĶĽ + /// Ҫӵݵֵʽַ + /// ʽͣΪjdtUnknownԶ + /// شĽ + function Add(AName, AValue: QStringW; + ADataType: TQJsonDataType = jdtUnknown): Integer; overload; + /// һ + /// ҪӵĶĽ + /// Ҫӵ + /// شĽʵ + function Add(const AName: QStringW; AItems: array of const) + : TQJson; overload; + { һӽ + ҪӵĽ + ҪӵĽͣʡԣԶֵݼ + ӵ¶ + + 1.ǰͲjdtObjectjdtArrayԶתΪjdtObject + 2.ϲӦԼ + + } + function Add(AName: QStringW; ADataType: TQJsonDataType): TQJson; overload; + + /// һӽ + /// ҪӵĽǰΪ飬ʱԸֵ + /// ҪӵĽֵ + /// ӵ¶ + function Add(AName: QStringW; AValue: Extended): TQJson; overload; + /// һӽ + /// ҪӵĽǰΪ飬ʱԸֵ + /// ҪӵĽֵ + /// ӵ¶ + function Add(AName: QStringW; AValue: Int64): TQJson; overload; + /// һӽ + /// ҪӵĽǰΪ飬ʱԸֵ + /// ҪӵĽֵ + /// ӵ¶ + function Add(AName: QStringW; AValue: Boolean): TQJson; overload; + /// һӽ + /// ҪӵĽǰΪ飬ʱԸֵ + /// Ҫӵӽ + /// ӵ¶λ + /// ӵĽͷŹ㸺ⲿӦͷ + function Add(AName: QStringW; AChild: TQJson): Integer; overload; + /// һӽ + /// ҪӵĽǰΪ飬ʱԸֵ + /// ӵ¶ + function AddArray(AName: QStringW): TQJson; overload; + /// һӽ + /// ҪӵĽǰΪ飬ʱԸֵ + /// ҪӵĽֵ + /// ӵ¶ + function AddDateTime(AName: QStringW; AValue: TDateTime): TQJson; overload; + /// һӽ + /// ҪӵĽǰΪ飬ʱԸֵ + /// ҪӵĽֵ + /// ӵ¶ + function AddVariant(AName: QStringW; AValue: Variant): TQJson; overload; + /// һӽ(Null) + /// ҪӵĽǰΪ飬ʱԸֵ + /// ӵ¶ + function Add(AName: QStringW): TQJson; overload; virtual; + + /// ǿһ·,,δҪĽ(jdtObjectjdtArray) + /// ҪӵĽ· + /// ·ӦĶ + /// + /// ·ȫڣForcePathᰴ¹ִ: + /// 1APathа[]ΪӦ·Ϊ飬ʾ£ + /// (1)'a.b[].name' + /// a -> jdtObject + /// b -> jdtArray + /// b[0].name -> jdtNull(bδָԶΪb[0] + /// (2)'a.c[2].name' + /// a -> jdtObject + /// c -> jdtArray + /// c[2].name -> jdtNull + /// ,c[0],c[1]ԶֵΪjdtNullִɺcΪԪص + /// (3)'a[0]' + /// a -> jdtArray + /// a[0] -> jdtNull + /// 2·ָ./\ǵȼ۵ģҽвӦַ֮һ, + /// a.b.ca\b\ca/b/cȫͬ· + /// 3APathָĶͲƥ䣬׳쳣aΪ󣬵ʹa[0].bʱ + /// + function ForcePath(APath: QStringW): TQJson; + /// ָJSONַ + /// Ҫַ + /// ַȣ<=0Ϊ\0(#0)βCԱ׼ַ + /// l>=0p[l]ǷΪ\0Ϊ\0ᴴʵʵ + procedure Parse(p: PWideChar; l: Integer = -1); overload; + /// ָJSONַ + /// ҪJSONַ + procedure Parse(const S: QStringW); overload; + function TryParse(p: PWideChar; l: Integer = -1): Boolean; overload; + /// ָJSONַ + /// ҪJSONַ + function TryParse(const S: QStringW): Boolean; overload; + /// н׸JSONݿ + /// + /// ݵı뷽ʽ + /// ParseBlockʺϽֶʽJSONӵǰλÿʼǰΪֹ. + /// Ժܺõ㽥ʽҪ + procedure ParseBlock(AStream: TStream; AEncoding: TTextEncoding); + /// һµʵ + /// µĿʵ + /// Ϊǿ¾ɶ֮ݱûκιϵһ + /// 󣬲һӰ졣 + /// + function Copy: TQJson; +{$IF RTLVersion>=21} + /// һµʵ + /// ûӵıǩ + /// û¼ڿҪ + /// µĿʵ + /// Ϊǿ¾ɶ֮ݱûκιϵһ + /// 󣬲һӰ졣 + /// + function CopyIf(const ATag: Pointer; AFilter: TQJsonFilterEventA) + : TQJson; overload; +{$IFEND >=2010} + /// һµʵ + /// ûӵıǩ + /// û¼ڿҪ + /// µĿʵ + /// Ϊǿ¾ɶ֮ݱûκιϵһ + /// 󣬲һӰ졣 + /// + function CopyIf(const ATag: Pointer; AFilter: TQJsonFilterEvent) + : TQJson; overload; + /// ¡һµʵ + /// µĿʵ + /// Ϊʵִеǿ¾ɶ֮ݱûκιϵ + /// һ󣬲һӰ죬Ϊ֤ + /// ΪãԱ໥Ӱ졣 + /// + function Clone: TQJson; + /// Ϊַ + /// Ƿʽַӿɶ + /// Ƿתĸַ + /// ADoFormatΪTrueʱݣĬΪո + /// رַ + /// AsJsonȼEncode(True,' ') + function Encode(ADoFormat: Boolean; ADoEscape: Boolean = False; + AIndent: QStringW = ' '): QStringW; + /// ȡָƻȡֵַʾ + /// + /// Ĭֵ + /// Ӧֵ + function ValueByName(AName, ADefVal: QStringW): QStringW; + /// ȡָƻȡֵIJֵʾ + /// + /// Ĭֵ + /// Ӧֵ + function BoolByName(AName: QStringW; ADefVal: Boolean): Boolean; + /// ȡָƻȡֵֵʾ + /// + /// Ĭֵ + /// Ӧֵ + function IntByName(AName: QStringW; ADefVal: Int64): Int64; + /// ȡָƻȡֵĸֵʾ + /// + /// Ĭֵ + /// Ӧֵ + function FloatByName(AName: QStringW; ADefVal: Extended): Extended; + /// ȡָƻȡֵʱֵʾ + /// + /// Ĭֵ + /// Ӧֵ + function DateTimeByName(AName: QStringW; ADefVal: TDateTime): TDateTime; + /// ȡָ·ֵַʾ + /// + /// Ĭֵ + /// ڣĬֵ򣬷ԭʼֵ + function ValueByPath(APath, ADefVal: QStringW): QStringW; + /// ȡָ·ֵIJֵʾ + /// + /// Ĭֵ + /// ڣĬֵ򣬷ԭʼֵ + function BoolByPath(APath: QStringW; ADefVal: Boolean): Boolean; + /// ȡָ·ֵʾ + /// + /// Ĭֵ + /// ڣĬֵ򣬷ԭʼֵ + function IntByPath(APath: QStringW; ADefVal: Int64): Int64; + /// ȡָ·ֵĸʾ + /// + /// Ĭֵ + /// ڣĬֵ򣬷ԭʼֵ + function FloatByPath(APath: QStringW; ADefVal: Extended): Extended; + /// ȡָ·ֵʱʾ + /// + /// Ĭֵ + /// ڣĬֵ򣬷ԭʼֵ + function DateTimeByPath(APath: QStringW; ADefVal: TDateTime): TDateTime; + /// ȡָ·ĶƱʾ + /// + /// Ĭֵ + /// ڣĬֵ򣬷ԭʼֵ + function BytesByPath(APath: QStringW; ADefVal: TBytes): TBytes; + /// ȡָƵĵһ + /// + /// ҵĽ㣬δҵؿ(NULL/nil) + /// עQJsonˣĽ㣬ֻ᷵صһ + function ItemByName(AName: QStringW): TQJson; overload; + /// ȡָƵĽ㵽б + /// + /// ڱб + /// Ƿݹӽ + /// ҵĽδҵ0 + /// ˺ְ֧±귽ʽ + function ItemByName(const AName: QStringW; AList: TQJsonItemList; + ANest: Boolean = False): Integer; overload; + /// ȡָƹĽ㵽б + /// ʽ + /// ڱб + /// Ƿݹӽ + /// ҵĽδҵ0 + function ItemByRegex(const ARegex: QStringW; AList: TQJsonItemList; + ANest: Boolean = False): Integer; overload; + /// ȡָ·JSON + /// ·".""/""\"ָ + /// ҵӽ㣬δҵNULL(nil) + /// ӽ㣬ֱʹ±ʣͶӽ㣬ʹ[][]ʡ + function ItemByPath(APath: QStringW): TQJson; + /// ԴJSON + /// ҪƵԴ + /// עⲻҪӽԼѭҪӽ㣬ȸ + /// һӽʵٴʵ + /// + procedure Assign(ANode: TQJson); virtual; + /// ɾָӽ + /// ҪɾĽ + /// + /// ָĽ㲻ڣ׳EOutRange쳣 + /// + procedure Delete(AIndex: Integer); overload; virtual; + /// ɾָƵӽ + /// ҪɾĽ + /// + /// ҪĽ㣬ֻɾһ + procedure Delete(AName: QStringW); overload; + /// Ӹɾûи㣬ͷԼ + procedure Delete; overload; +{$IF RTLVersion>=21} + /// + /// ɾӽ + /// + /// ûԼӵĶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnilȼClear + procedure DeleteIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEventA); overload; +{$IFEND >=2010} + /// + /// ɾӽ + /// + /// ûԼӵĶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnilȼClear + procedure DeleteIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEvent); overload; + /// ָƵĽ + /// ҪҵĽ + /// ֵδҵ-1 + function IndexOf(const AName: QStringW): Integer; virtual; +{$IF RTLVersion>=21} + /// ҷĽ + /// ûԶĸӶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnil򷵻nil + function FindIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEventA): TQJson; overload; +{$IFEND >=2010} + /// ҷĽ + /// ûԶĸӶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnil򷵻nil + function FindIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEvent): TQJson; overload; + /// еĽ + procedure Clear; virtual; + /// 浱ǰݵ + /// Ŀ + /// ʽ + /// ǷдBOM + /// ǷʽJson + /// ע⵱ǰƲᱻд + procedure SaveToStream(AStream: TStream; AEncoding: TTextEncoding = teUtf8; + AWriteBOM: Boolean = True; ADoFormat: Boolean = True); + /// ĵǰλÿʼJSON + /// Դ + /// Դļ룬ΪteUnknownԶж + /// ĵǰλõijȱ2ֽڣ + procedure LoadFromStream(AStream: TStream; + AEncoding: TTextEncoding = teUnknown); + /// 浱ǰݵļ + /// ļ + /// ʽ + /// ǷдUTF-8BOM + /// ǷʽJson + /// ע⵱ǰƲᱻд + procedure SaveToFile(AFileName: String; AEncoding: TTextEncoding = teUtf8; + AWriteBOM: Boolean = True; ADoFormat: Boolean = True); + /// ָļмصǰ + /// Ҫصļ + /// Դļ룬ΪteUnknownԶж + procedure LoadFromFile(AFileName: String; + AEncoding: TTextEncoding = teUnknown); + /// / ֵΪNullȼֱDataTypeΪjdtNull + procedure ResetNull; + function Escape(const S: QStringW): QStringW; + /// TObject.ToString + function ToString: string; {$IFDEF UNICODE}override; {$ELSE}virtual; +{$ENDIF} + /// ȡfor..inҪGetEnumerator֧ + function GetEnumerator: TQJsonEnumerator; + /// жԼǷָӶ + /// ܵĸ + /// AParentԼĸ󣬷True򷵻false + function IsChildOf(AParent: TQJson): Boolean; + /// жԼǷָĸ + /// ܵӶ + /// AChildԼӶ󣬷True򷵻false + function IsParentOf(AChild: TQJson): Boolean; +{$IF RTLVersion>=21} + /// ʹõǰJsonָӦ + /// Ķʵ + /// غõĽ + /// ΪǰƣIJӽҪһ + function Invoke(AInstance: TValue): TValue; + /// ǰֵתΪTValue͵ֵ + /// صǰתTValueֵ + function ToRttiValue: TValue; + /// ָRTTIʵJSON + /// RTTIֵ + /// עⲻȫRTTIͶ֧֣ӿɶ + procedure FromRtti(AInstance: TValue); overload; + /// ָԴַָJSON + /// ṹַ + /// ṹϢ + procedure FromRtti(ASource: Pointer; AType: PTypeInfo); overload; + /// ָļ¼ʵJSON + /// ¼ʵ + procedure FromRecord(const ARecord: T); + /// ӵǰJSONлԭָĶʵ + /// ʵַ + /// ʵϲֶֻ֧󣬼¼Ŀǰ޷ֱתΪTValueû + /// 壬Ϊֵʵʾ㸳ֵҲزˣ + procedure ToRtti(AInstance: TValue); overload; + /// ӵǰJSONаָϢԭָĵַ + /// Ŀĵַ + /// ṹϢ + /// ADestӦӦǼ¼Ͳ֧ + procedure ToRtti(ADest: Pointer; AType: PTypeInfo); overload; + /// ӵǰJSONлԭָļ¼ʵ + /// Ŀļ¼ʵ + procedure ToRecord(var ARecord: T); +{$IFEND} + /// ָӽƳ + /// ҪƳӽ + /// رƳӽ㣬ָڣnil + /// ƳӽҪûԼֹͷ + function Remove(AItemIndex: Integer): TQJson; overload; virtual; + /// ָӽƳ + /// ҪƳӽ + /// ƳӽҪûԼֹͷ + procedure Remove(AJson: TQJson); overload; + /// ӵǰз뵱ǰ + /// ĽҪͷ + procedure Detach; + /// ǰ㸽ӵµĸ + /// ҪӵĿ + /// ӺĽɸ㸺ͷ + procedure AttachTo(ANewParent: TQJson); + /// ǰƶµĸָλ + /// µĸ + /// λ + /// λСڵ0뵽ʼλãڸн뵽 + /// ĩβӵָλ + procedure MoveTo(ANewParent: TQJson; AIndex: Integer); + + /// мض + /// Դ + /// ȣΪ0ȫ + procedure ValueFromStream(AStream: TStream; ACount: Cardinal); + /// д뵽 + /// Ŀ + procedure StreamFromValue(AStream: TStream); + + /// мض + /// Դļ + procedure ValueFromFile(AFileName: QStringW); + /// д뵽 + /// Ŀļ + procedure FileFromValue(AFileName: QStringW); + /// жǷзָ·Ҫӽ㣬ڣͨAChildʵַTrue򷵻False + /// ·ָܡ./\ + /// ڷӽָ + /// ɹTrueAChildֵΪӽָ룬ʧܣFalse + function HasChild(ANamePath: QStringW; var AChild: TQJson): Boolean; inline; + // תһJsonֵΪַ + class function BuildJsonString(ABuilder: TQStringCatHelperW; var p: PQCharW) + : Boolean; overload; + class function BuildJsonString(S: QStringW): QStringW; overload; + class function BuildJsonString(ABuilder: TQStringCatHelperW; S: QStringW) + : Boolean; overload; + class procedure JsonCat(ABuilder: TQStringCatHelperW; const S: QStringW; + ADoEscape: Boolean); overload; + class function JsonCat(const S: QStringW; ADoEscape: Boolean) + : QStringW; overload; + class function JsonEscape(const S: QStringW; ADoEscape: Boolean) + : QStringW; overload; + class function JsonUnescape(const S: QStringW): QStringW; + class function EncodeDateTime(const AValue: TDateTime): QStringW; + /// + property Parent: TQJson read FParent; + /// + /// TQJsonDataType + property DataType: TQJsonDataType read FDataType write SetDataType; + /// + property Name: QStringW read FName write SetName; + /// ӽsummary> + property Count: Integer read GetCount; + /// ӽ + property Items[AIndex: Integer]: TQJson read GetItems; default; + /// ӽֵ + property Value: QStringW read GetValue write SetValue; + /// жǷDz + property IsBool: Boolean read GetIsBool; + /// жǷNULL + property IsNull: Boolean read GetIsNull; + /// жǷ + property IsNumeric: Boolean read GetIsNumeric; + /// жǷʱ + property IsDateTime: Boolean read GetIsDateTime; + /// жǷַ + property IsString: Boolean read GetIsString; + /// жǷǶ + property IsObject: Boolean read GetIsObject; + /// жǷ + property IsArray: Boolean read GetIsArray; + /// ǰ㵱ͷ + property AsBoolean: Boolean read GetAsBoolean write SetAsBoolean; + /// ǰ㵱 + property AsInteger: Integer read GetAsInteger write SetAsInteger; + /// ǰ㵱64λ + property AsInt64: Int64 read GetAsInt64 write SetAsInt64; + /// ǰ㵱 + property AsFloat: Extended read GetAsFloat write SetAsFloat; + /// ǰ㵱ʱ + property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime; + /// ǰ㵱ַͷ + property AsString: QStringW read GetAsString write SetAsString; + /// ǰ㵱һַ + property AsObject: QStringW read GetAsObject write SetAsObject; + /// ǰ㵱һַ + property AsArray: QStringW read GetAsArray write SetAsArray; + /// ԼVariant + property AsVariant: Variant read GetAsVariant write SetAsVariant; + /// ԼJson + property AsJson: QStringW read GetAsJson write SetAsJson; + /// Լݷ + property AsBytes: TBytes read GetAsBytes write SetAsBytes; + // ĸݳԱû + property Data: Pointer read FData write FData; + /// ··м"\"ָ + property Path: QStringW read GetPath; + /// ڸе˳򣬴0ʼ-1ԼǸ + property ItemIndex: Integer read GetItemIndex; + /// ƹϣֵ + property NameHash: Cardinal read FNameHash; + property IgnoreCase: Boolean read FIgnoreCase write SetIgnoreCase; + property Root: TQJson read GetRoot; + end; + + TQJsonEnumerator = class + private + FIndex: Integer; + FList: TQJson; + public + constructor Create(AList: TQJson); + function GetCurrent: TQJson; inline; + function MoveNext: Boolean; + property Current: TQJson read GetCurrent; + end; + + TQHashedJson = class(TQJson) + protected + FHashTable: TQHashTable; + function CreateJson: TQJson; override; + procedure Replace(AIndex: Integer; ANewItem: TQJson); override; + procedure DoJsonNameChanged(AJson: TQJson); override; + procedure DoParsed; override; + public + constructor Create; overload; + destructor Destroy; override; + procedure Assign(ANode: TQJson); override; + function IndexOf(const AName: QStringW): Integer; override; + function Remove(AIndex: Integer): TQJson; override; + procedure Clear; override; + end; + + TQJsonStreamHelper = record + private + FEncoding: TTextEncoding; + FStream: TStream; + FDoEscape: Boolean; + FIsEmpty: Boolean; + procedure InternalWriteString(S: QStringW; ADoAppend: Boolean = True); + public + procedure BeginWrite(AStream: TStream; AEncoding: TTextEncoding; + ADoEscape: Boolean = False); + procedure EndWrite; + procedure BeginObject; overload; + procedure BeginObject(const AName: QStringW); overload; + procedure EndObject; + procedure BeginArray; overload; + procedure BeginArray(const AName: QStringW); overload; + procedure EndArray; + procedure WriteName(const S: QStringW); + procedure Write(const S: QStringW); overload; + procedure Write(const I: Int64); overload; + procedure Write(const D: Double); overload; + procedure WriteDateTime(const V: TDateTime); overload; + procedure Write(const c: Currency); overload; + procedure Write(const ABytes: TBytes); overload; + procedure Write(const p: PByte; l: Integer); overload; + procedure WriteNull; overload; + procedure Write(const b: Boolean); overload; + procedure Write(const AName, AValue: QStringW); overload; + procedure Write(const AName: QStringW; AValue: Int64); overload; + procedure Write(const AName: QStringW; AValue: Double); overload; + procedure Write(const AName: QStringW; AValue: TBytes); overload; + procedure Write(const AName: QStringW; AValue: Boolean); overload; + procedure WriteDateTime(const AName: QStringW; AValue: TDateTime); overload; + procedure Write(const AName: QStringW; const p: PByte; + const l: Integer); overload; + procedure WriteNull(const AName: QStringW); overload; + property DoEscape: Boolean read FDoEscape write FDoEscape; + property IsEmpty: Boolean read FIsEmpty; + end; + +var + /// Ƿϸģʽϸģʽ£ + /// 1.ƻַʹ˫Ű,ΪFalseƿûŻʹõš + /// 2.עͲ֧֣ΪFalse֧//עͺ/**/Ŀע + /// + StrictJson: Boolean; + /// ָδRTTIеöٺͼ + JsonRttiEnumAsInt: Boolean; + /// תΪJsonʱתַθʽ + JsonDateFormat: QStringW; + /// ʱתΪJsonʱתַθʽ + JsonTimeFormat: QStringW; + /// ʱתΪJsonʱתַθʽ + JsonDateTimeFormat: QStringW; + /// ItemByName/ItemByPath/ValueByName/ValueByPathȺжУǷƴСд + JsonCaseSensitive: Boolean; + /// Ҫ½һTQJsonʱ + OnQJsonCreate: TQJsonCreateEvent; + /// ҪͷһTQJsonʱ + OnQJsonFree: TQJsonFreeEvent; + /// ַֽ֮ת¼ + OnQJsonEncodeBytes: TQJsonEncodeBytesEvent; + OnQJsonDecodeBytes: TQJsonDecodeBytesEvent; + // ֵַʼ + CharStringStart: QStringW = '"'; + // ֵַĽ + CharStringEnd: QStringW = '",'; + // JSONƿʼ + CharNameStart: QStringW = '"'; + // JSONƽ + CharNameEnd: QStringW = '":'; + // JSON 鿪ʼ + CharArrayStart: QStringW = '['; + // JSON + CharArrayEnd: QStringW = '],'; + // JSON ʼ + CharObjectStart: QStringW = '{'; + // JSON + CharObjectEnd: QStringW = '},'; + // JSON NULL ֵ + CharNull: QStringW = 'null'; + // JSON ٵֵ + CharFalse: QStringW = 'false'; + // JSON ֵ + CharTrue: QStringW = 'true'; + // JSON ֵָ + CharComma: QStringW = ','; +procedure EncodeJsonBinaryAsBase64; +procedure EncodeJsonBinaryAsHex; + +implementation + +uses variants, varutils, dateutils; + +resourcestring + SBadJson = 'ǰݲЧJSONַ'; + SNotArrayOrObject = '%s һJSON'; + SVarNotArray = '%s Ͳ'; + SBadConvert = '%s һЧ %s ͵ֵ'; + SCharNeeded = 'ǰλӦ "%s" "%s"'; + SEndCharNeeded = 'ǰλҪJsonַ",]}"'; + SBadNumeric = '"%s"Чֵ'; + SBadJsonTime = '"%s"һЧʱֵ'; + SBadNameStart = 'JsonӦ''"''ַʼ'; + SBadNameEnd = 'Jsonδȷ'; + SNameNotFound = 'Ŀδҵ'; + SCommentNotSupport = 'ϸģʽ²֧עͣҪע͵JSONݣ뽫StrictJsonΪFalse'; + SUnsupportArrayItem = 'ӵĶ̬%dԪͲ֧֡'; + SBadStringStart = 'ϸJSONַ"ʼ'; + SUnknownToken = '޷ʶעͷעͱ///**/'; + SNotSupport = ' [%s] ڵǰ²֧֡'; + SBadJsonArray = '%s һЧJSON鶨塣'; + SBadJsonObject = '%s һЧJSON塣'; + SBadJsonEncoding = 'Чı룬ֻUTF-8ANSIUnicode 16 LEUnicode 16 BE'; + SJsonParseError = '%dе%d:%s '#13#10':%s'; + SBadJsonName = '%s һЧJSONơ'; + SObjectChildNeedName = ' %s ĵ %d ӽδֵǰ踳ֵ'; + SReplaceTypeNeed = '滻Ҫ %s ࡣ'; + SSupportFloat = 'NaN/+/-޲JSON淶֧֡'; + SParamMissed = ' %s ͬĽδҵ'; + SMethodMissed = 'ָĺ %s ڡ'; + SMissRttiTypeDefine = + '޷ҵ %s RTTIϢԽӦ͵(array[0..1] of ByteΪTByteArr=array[0..1]ȻTByteArr)'; + SUnsupportPropertyType = 'ֵ֧͡'; + SArrayTypeMissed = 'δ֪Ԫ͡'; + SUnknownError = 'δ֪Ĵ'; + SCantAttachToSelf = 'ԼΪԼӽ㡣'; + SCanAttachToNoneContainer = 'ܽ㸽ӵͶ͵Ľ¡'; + SCantAttachNoNameNodeToObject = 'ܽδĽΪ͵ӽ㡣'; + SNodeNameExists = 'ָĸѾΪ %s ӽ㡣'; + SCantMoveToChild = 'ܽԼƶԼӽ'; + SConvertError = '޷ %s תΪ %s '; + SUnsupportVarType = 'ֵ֧ı %d '; + +const + JsonTypeName: array [TQJsonDataType] of QStringW = ('Unknown', 'Null', + 'String', 'Integer', 'Float', 'Boolean', 'DateTime', 'Array', 'Object'); + EParse_Unknown = -1; + EParse_BadStringStart = 1; + EParse_BadJson = 2; + EParse_CommentNotSupport = 3; + EParse_UnknownToken = 4; + EParse_EndCharNeeded = 5; + EParse_BadNameStart = 6; + EParse_BadNameEnd = 7; + EParse_NameNotFound = 8; + { TQJson } + +function TQJson.Add(AName: QStringW; AValue: Int64): TQJson; +begin + Result := Add(AName, jdtInteger); + PInt64(PQCharW(Result.FValue))^ := AValue; +end; + +function TQJson.Add(AName: QStringW; AValue: Extended): TQJson; +begin + Result := Add(AName, jdtFloat); + PExtended(PQCharW(Result.FValue))^ := AValue; +end; + +function TQJson.Add(AName: QStringW; AValue: Boolean): TQJson; +begin + Result := Add(AName, jdtBoolean); + PBoolean(PQCharW(Result.FValue))^ := AValue; +end; + +function TQJson.Add(AName: QStringW): TQJson; +begin + Result := Add; + Result.FName := AName; + DoJsonNameChanged(Result); +end; + +function TQJson.Add(AName: QStringW; AChild: TQJson): Integer; +begin + AChild.FName := AName; + Result := Add(AChild); +end; + +function TQJson.AddArray(AName: QStringW): TQJson; +begin + Result := Add(AName, jdtArray); +end; + +function TQJson.AddDateTime(AName: QStringW; AValue: TDateTime): TQJson; +begin + Result := Add; + Result.FName := AName; + Result.DataType := jdtString; + Result.AsDateTime := AValue; +end; + +function TQJson.AddVariant(AName: QStringW; AValue: Variant): TQJson; +begin + Result := Add(AName); + Result.AsVariant := AValue; +end; + +function TQJson.Add: TQJson; +begin + Result := CreateJson; + Add(Result); +end; + +function TQJson.Add(ANode: TQJson): Integer; +begin + ArrayNeeded(jdtObject); + Result := FItems.Add(ANode); + ANode.FParent := Self; + ANode.FIgnoreCase := FIgnoreCase; +end; + +function TQJson.Add(AName, AValue: QStringW; ADataType: TQJsonDataType) + : Integer; +var + ANode: TQJson; + p: PQCharW; + ABuilder: TQStringCatHelperW; + procedure AddAsDateTime; + var + ATime: TDateTime; + begin + if ParseDateTime(PQCharW(AValue), ATime) then + ANode.AsDateTime := ATime + else if ParseJsonTime(PQCharW(AValue), ATime) then + ANode.AsDateTime := ATime + else + raise Exception.Create(SBadJsonTime); + end; + +begin + ANode := CreateJson; + ANode.FName := AName; + Result := Add(ANode); + p := PQCharW(AValue); + if ADataType = jdtUnknown then + begin + ABuilder := TQStringCatHelperW.Create; + try + if ANode.TryParseValue(ABuilder, p) <> 0 then + ANode.AsString := AValue + else if p^ <> #0 then + ANode.AsString := AValue; + finally + FreeObject(ABuilder); + end; + end + else + begin + case ADataType of + jdtString: + ANode.AsString := AValue; + jdtInteger: + ANode.AsInteger := StrToInt(AValue); + jdtFloat: + ANode.AsFloat := StrToFloat(AValue); + jdtBoolean: + ANode.AsBoolean := StrToBool(AValue); + jdtDateTime: + AddAsDateTime; + jdtArray: + begin + if p^ <> '[' then + raise Exception.CreateFmt(SBadJsonArray, [Value]); + ANode.ParseObject(p); + end; + jdtObject: + begin + if p^ <> '{' then + raise Exception.CreateFmt(SBadJsonObject, [Value]); + ANode.ParseObject(p); + end; + end; + + end; +end; + +function TQJson.Add(AName: QStringW; ADataType: TQJsonDataType): TQJson; +begin + Result := Add(AName); + Result.DataType := ADataType; +end; + +function TQJson.Add(const AName: QStringW; AItems: array of const): TQJson; +var + I: Integer; +begin + Result := Add(AName); + Result.DataType := jdtArray; + for I := Low(AItems) to High(AItems) do + begin + case AItems[I].VType of + vtInteger: + Result.Add.AsInteger := AItems[I].VInteger; + vtBoolean: + Result.Add.AsBoolean := AItems[I].VBoolean; +{$IFNDEF NEXTGEN} + vtChar: + Result.Add.AsString := QStringW(AItems[I].VChar); +{$ENDIF !NEXTGEN} + vtExtended: + Result.Add.AsFloat := AItems[I].VExtended^; +{$IFNDEF NEXTGEN} + vtPChar: + Result.Add.AsString := QStringW(AItems[I].VPChar); + vtString: + Result.Add.AsString := QStringW(AItems[I].VString^); + vtAnsiString: + Result.Add.AsString := QStringW( +{$IFDEF UNICODE} + PAnsiString(AItems[I].VAnsiString)^ +{$ELSE} + AItems[I].VPChar +{$ENDIF UNICODE} + ); + vtWideString: + Result.Add.AsString := PWideString(AItems[I].VWideString)^; +{$ENDIF !NEXTGEN} + vtPointer: + Result.Add.AsInt64 := IntPtr(AItems[I].VPointer); + vtWideChar: + Result.Add.AsString := AItems[I].VWideChar; + vtPWideChar: + Result.Add.AsString := AItems[I].VPWideChar; + vtCurrency: + Result.Add.AsFloat := AItems[I].VCurrency^; + vtInt64: + Result.Add.AsInt64 := AItems[I].VInt64^; +{$IFDEF UNICODE} // variants + vtUnicodeString: + Result.Add.AsString := AItems[I].VPWideChar; +{$ENDIF UNICODE} + vtVariant: + Result.Add.AsVariant := AItems[I].VVariant^; + vtObject: + begin + if TObject(AItems[I].VObject) is TQJson then + Result.Add(TObject(AItems[I].VObject) as TQJson) + else + raise Exception.Create(Format(SUnsupportArrayItem, [I])); + end + else + raise Exception.Create(Format(SUnsupportArrayItem, [I])); + end; // End case + end; // End for +end; + +function TQJson.ArrayItemType(ArrType: PTypeInfo): PTypeInfo; +var + ATypeData: PTypeData; +begin + Result := nil; + if (ArrType <> nil) and (ArrType.Kind in [tkArray, tkDynArray]) then + begin + ATypeData := GetTypeData(ArrType); + if (ATypeData <> nil) then + Result := ATypeData.elType2^; + if Result = nil then + begin + if ATypeData.BaseType^ = TypeInfo(Byte) then + Result := TypeInfo(Byte); + end; + end; +end; + +function TQJson.ArrayItemTypeName(ATypeName: QStringW): QStringW; +var + p, ps: PQCharW; + ACount: Integer; +begin + p := PQCharW(ATypeName); + if StartWithW(p, 'TArray<', True) then + begin + Inc(p, 7); + ps := p; + ACount := 1; + while ACount > 0 do + begin + if p^ = '>' then + Dec(ACount) + else if p^ = '<' then + Inc(ACount); + Inc(p); + end; + Result := StrDupX(ps, p - ps - 1); + end + else + Result := ''; +end; + +procedure TQJson.ArrayNeeded(ANewType: TQJsonDataType); +begin + if not(DataType in [jdtArray, jdtObject]) then + begin + FDataType := ANewType; + ValidArray; + end; +end; + +procedure TQJson.Assign(ANode: TQJson); +var + I: Integer; + AItem, ACopy: TQJson; +begin + if ANode.FDataType in [jdtArray, jdtObject] then + begin + DataType := ANode.FDataType; + Clear; + for I := 0 to ANode.Count - 1 do + begin + AItem := ANode[I]; + if Length(AItem.FName) > 0 then + begin + ACopy := Add(AItem.FName); + ACopy.FNameHash := AItem.FNameHash; + end + else + ACopy := Add; + ACopy.Assign(AItem); + end; + end + else + CopyValue(ANode); +end; + +procedure TQJson.AttachTo(ANewParent: TQJson); +begin + MoveTo(ANewParent, MaxInt); +end; + +function TQJson.BoolByName(AName: QStringW; ADefVal: Boolean): Boolean; +var + AChild: TQJson; +begin + AChild := ItemByName(AName); + if Assigned(AChild) then + begin + try + Result := AChild.AsBoolean; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +function TQJson.BoolByPath(APath: QStringW; ADefVal: Boolean): Boolean; +var + AItem: TQJson; +begin + AItem := ItemByPath(APath); + if Assigned(AItem) then + begin + try + Result := AItem.AsBoolean + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +function TQJson.BooleanToStr(const b: Boolean): QStringW; +begin + if b then + Result := CharTrue + else + Result := CharFalse; +end; + +class function TQJson.BuildJsonString(ABuilder: TQStringCatHelperW; + S: QStringW): Boolean; +var + p: PQCharW; +begin + p := PQCharW(S); + Result := BuildJsonString(ABuilder, p); +end; + +class function TQJson.BuildJsonString(S: QStringW): QStringW; +var + AHelper: TQStringCatHelperW; + p: PQCharW; +begin + AHelper := TQStringCatHelperW.Create; + try + p := PQCharW(S); + BuildJsonString(AHelper, p); + finally + FreeAndNil(AHelper); + end; +end; + +class function TQJson.BuildJsonString(ABuilder: TQStringCatHelperW; + var p: PQCharW): Boolean; +var + AQuoter: QCharW; + ps: PQCharW; +begin + ABuilder.Position := 0; + if (p^ = '"') or (p^ = '''') then + begin + AQuoter := p^; + Inc(p); + ps := p; + Result := False; + while p^ <> #0 do + begin + if (p^ = AQuoter) then + begin + if ps <> p then + ABuilder.Cat(ps, p - ps); + if p[1] = AQuoter then + begin + ABuilder.Cat(AQuoter); + Inc(p, 2); + ps := p; + end + else + begin + Inc(p); + SkipSpaceAndComment(p); + ps := p; + Result := True; + Break; + end; + end + else if p^ = '\' then + begin + if ps <> p then + ABuilder.Cat(ps, p - ps); + ABuilder.Cat(CharUnescape(p)); + ps := p; + end + else + Inc(p); + end; + if Result then + begin + if (ps <> p) then + ABuilder.Cat(ps, p - ps) + end + else + begin + ABuilder.Position := 0; + ABuilder.Cat(ps - 1, p - ps + 1); + end; + end + else + begin + Result := True; + while p^ <> #0 do + begin + if (p^ = ':') or (p^ = ']') or (p^ = ',') or (p^ = '}') then + Break + else + ABuilder.Cat(p, 1); + Inc(p); + end + end; +end; + +function TQJson.BytesByPath(APath: QStringW; ADefVal: TBytes): TBytes; +var + AItem: TQJson; +begin + AItem := ItemByPath(APath); + if Assigned(AItem) then + begin + try + Result := AItem.AsBytes; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +class procedure TQJson.JsonCat(ABuilder: TQStringCatHelperW; const S: QStringW; + ADoEscape: Boolean); +var + ps: PQCharW; +const + CharNum1: PWideChar = '1'; + CharNum0: PWideChar = '0'; + Char7: PWideChar = '\b'; + Char9: PWideChar = '\t'; + Char10: PWideChar = '\n'; + Char12: PWideChar = '\f'; + Char13: PWideChar = '\r'; + CharQuoter: PWideChar = '\"'; + CharBackslash: PWideChar = '\\'; + CharCode: PWideChar = '\u00'; + CharEscape: PWideChar = '\u'; +begin + ps := PQCharW(S); + while ps^ <> #0 do + begin + case ps^ of + #7: + ABuilder.Cat(Char7, 2); + #9: + ABuilder.Cat(Char9, 2); + #10: + ABuilder.Cat(Char10, 2); + #12: + ABuilder.Cat(Char12, 2); + #13: + ABuilder.Cat(Char13, 2); + '\': + ABuilder.Cat(CharBackslash, 2); + '"': + ABuilder.Cat(CharQuoter, 2); + else + begin + if ps^ < #$1F then + begin + ABuilder.Cat(CharCode, 4); + if ps^ > #$F then + ABuilder.Cat(CharNum1, 1) + else + ABuilder.Cat(CharNum0, 1); + ABuilder.Cat(HexChar(Ord(ps^) and $0F)); + end + else if (ps^ <= #$7E) or (not ADoEscape) then // Ӣַ + ABuilder.Cat(ps, 1) + else + ABuilder.Cat(CharEscape, 2).Cat(HexChar((PWord(ps)^ shr 12) and $0F)) + .Cat(HexChar((PWord(ps)^ shr 8) and $0F)) + .Cat(HexChar((PWord(ps)^ shr 4) and $0F)) + .Cat(HexChar(PWord(ps)^ and $0F)); + end; + end; + Inc(ps); + end; +end; + +class function TQJson.CharEscape(c: QCharW; pd: PQCharW): Integer; +begin + case c of + #7: + begin + pd[0] := '\'; + pd[1] := 'b'; + Result := 2; + end; + #9: + begin + pd[0] := '\'; + pd[1] := 't'; + Result := 2; + end; + #10: + begin + pd[0] := '\'; + pd[1] := 'n'; + Result := 2; + end; + #12: + begin + pd[0] := '\'; + pd[1] := 'f'; + Result := 2; + end; + #13: + begin + pd[0] := '\'; + pd[1] := 'r'; + Result := 2; + end; + '\': + begin + pd[0] := '\'; + pd[1] := '\'; + Result := 2; + end; + '''': + begin + pd[0] := '\'; + pd[1] := ''''; + Result := 2; + end; + '"': + begin + pd[0] := '\'; + pd[1] := '"'; + Result := 2; + end; + '/': + begin + pd[0] := '\'; + pd[1] := '/'; + Result := 2; + end + else + begin + pd[0] := c; + Result := 1; + end; + end; +end; + +class function TQJson.CharUnescape(var p: PQCharW): QCharW; + function DecodeOrd: Integer; + var + c: Integer; + begin + Result := 0; + c := 0; + while (p^ <> #0) and (c < 4) do + begin + if IsHexChar(p^) then + Result := (Result shl 4) + HexValue(p^) + else + Break; + Inc(p); + Inc(c); + end + end; + +begin + if p^ = #0 then + begin + Result := #0; + Exit; + end; + if p^ <> '\' then + begin + Result := p^; + Inc(p); + Exit; + end; + Inc(p); + case p^ of + 'b': + begin + Result := #7; + Inc(p); + end; + 't': + begin + Result := #9; + Inc(p); + end; + 'n': + begin + Result := #10; + Inc(p); + end; + 'f': + begin + Result := #12; + Inc(p); + end; + 'r': + begin + Result := #13; + Inc(p); + end; + '\': + begin + Result := '\'; + Inc(p); + end; + '''': + begin + Result := ''''; + Inc(p); + end; + '"': + begin + Result := '"'; + Inc(p); + end; + 'u': + begin + // \uXXXX + if IsHexChar(p[1]) and IsHexChar(p[2]) and IsHexChar(p[3]) and + IsHexChar(p[4]) then + begin + Result := WideChar((HexValue(p[1]) shl 12) or (HexValue(p[2]) shl 8) + or (HexValue(p[3]) shl 4) or HexValue(p[4])); + Inc(p, 5); + end + else + raise Exception.CreateFmt(SCharNeeded, + ['0-9A-Fa-f', StrDupW(p, 0, 4)]); + end; + '/': + begin + Result := '/'; + Inc(p); + end + else + begin + if StrictJson then + raise Exception.CreateFmt(SCharNeeded, ['btfrn"u''/', StrDupW(p, 0, 4)]) + else + begin + Result := p^; + Inc(p); + end; + end; + end; +end; + +procedure TQJson.Clear; +var + I: Integer; +begin + if FDataType in [jdtArray, jdtObject] then + begin + for I := 0 to Count - 1 do + FreeJson(FItems[I]); + FItems.Clear; + end; +end; + +function TQJson.Clone: TQJson; +begin + Result := Copy; +end; + +function TQJson.Copy: TQJson; +begin + Result := CreateJson; + Result.Assign(Self); +end; +{$IF RTLVersion>=21} + +function TQJson.CopyIf(const ATag: Pointer; + AFilter: TQJsonFilterEventA): TQJson; + procedure NestCopy(AParentSource, AParentDest: TQJson); + var + I: Integer; + Accept: Boolean; + AChildSource, AChildDest: TQJson; + begin + for I := 0 to AParentSource.Count - 1 do + begin + Accept := True; + AChildSource := AParentSource[I]; + AFilter(Self, AChildSource, Accept, ATag); + if Accept then + begin + AChildDest := AParentDest.Add(AChildSource.FName, + AChildSource.DataType); + if AChildSource.DataType in [jdtArray, jdtObject] then + begin + AChildDest.DataType := AChildSource.DataType; + NestCopy(AChildSource, AChildDest) + end + else + AChildDest.CopyValue(AChildSource); + end; + end; + end; + +begin + if Assigned(AFilter) then + begin + Result := CreateJson; + Result.FName := Name; + if DataType in [jdtObject, jdtArray] then + begin + NestCopy(Self, Result); + end + else + Result.CopyValue(Self); + end + else + Result := Copy; +end; +{$IFEND >=2010} + +function TQJson.CopyIf(const ATag: Pointer; AFilter: TQJsonFilterEvent): TQJson; + procedure NestCopy(AParentSource, AParentDest: TQJson); + var + I: Integer; + Accept: Boolean; + AChildSource, AChildDest: TQJson; + begin + for I := 0 to AParentSource.Count - 1 do + begin + Accept := True; + AChildSource := AParentSource[I]; + AFilter(Self, AChildSource, Accept, ATag); + if Accept then + begin + AChildDest := AParentDest.Add(AChildSource.FName, + AChildSource.DataType); + if AChildSource.DataType in [jdtArray, jdtObject] then + NestCopy(AChildSource, AChildDest) + else + AChildDest.CopyValue(AChildSource); + end; + end; + end; + +begin + if Assigned(AFilter) then + begin + Result := CreateJson; + Result.FName := Name; + if DataType in [jdtObject, jdtArray] then + begin + NestCopy(Self, Result); + end + else + Result.CopyValue(Self); + end + else + Result := Copy; +end; + +procedure TQJson.CopyValue(ASource: TQJson); +var + l: Integer; +begin + l := Length(ASource.FValue); + DataType := ASource.DataType; + SetLength(FValue, l); + if l > 0 then + Move(PQCharW(ASource.FValue)^, PQCharW(FValue)^, l shl 1); +end; + +constructor TQJson.Create(const AName, AValue: QStringW; + ADataType: TQJsonDataType); +begin + inherited Create; + FName := AName; + FIgnoreCase := not JsonCaseSensitive; + if ADataType <> jdtUnknown then + DataType := ADataType; + Value := AValue; +end; + +function TQJson.CreateJson: TQJson; +begin + if Assigned(OnQJsonCreate) then + Result := OnQJsonCreate + else + Result := TQJson.Create; +end; + +constructor TQJson.Create; +begin + inherited; + FIgnoreCase := not JsonCaseSensitive; +end; + +function TQJson.DateTimeByName(AName: QStringW; ADefVal: TDateTime): TDateTime; +var + AChild: TQJson; +begin + AChild := ItemByName(AName); + if Assigned(AChild) then + begin + try + Result := AChild.AsDateTime; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +function TQJson.DateTimeByPath(APath: QStringW; ADefVal: TDateTime): TDateTime; +var + AItem: TQJson; +begin + AItem := ItemByPath(APath); + if Assigned(AItem) then + begin + try + Result := AItem.AsDateTime; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +procedure TQJson.Delete(AName: QStringW); +var + I: Integer; +begin + I := IndexOf(AName); + if I <> -1 then + Delete(I); +end; +{$IF RTLVersion>=21} + +procedure TQJson.DeleteIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEventA); + procedure DeleteChildren(AParent: TQJson); + var + I: Integer; + Accept: Boolean; + AChild: TQJson; + begin + I := 0; + while I < AParent.Count do + begin + Accept := True; + AChild := AParent.Items[I]; + if ANest then + DeleteChildren(AChild); + AFilter(Self, AChild, Accept, ATag); + if Accept then + AParent.Delete(I) + else + Inc(I); + end; + end; + +begin + if Assigned(AFilter) then + DeleteChildren(Self) + else + Clear; +end; +{$IFEND >=2010} + +procedure TQJson.DeleteIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEvent); + procedure DeleteChildren(AParent: TQJson); + var + I: Integer; + Accept: Boolean; + AChild: TQJson; + begin + I := 0; + while I < AParent.Count do + begin + Accept := True; + AChild := AParent.Items[I]; + if ANest then + DeleteChildren(AChild); + AFilter(Self, AChild, Accept, ATag); + if Accept then + AParent.Delete(I) + else + Inc(I); + end; + end; + +begin + if Assigned(AFilter) then + DeleteChildren(Self) + else + Clear; +end; + +procedure TQJson.Delete(AIndex: Integer); +var + AJson: TQJson; +begin + AJson := Remove(AIndex); + if Assigned(AJson) then + FreeJson(AJson); +end; + +destructor TQJson.Destroy; +begin + if DataType in [jdtArray, jdtObject] then + begin + Clear; + FreeObject(FItems); + end; + inherited; +end; + +procedure TQJson.Detach; +begin + if Assigned(FParent) then + FParent.Remove(Self); +end; + +procedure TQJson.DoJsonNameChanged(AJson: TQJson); +begin + +end; + +procedure TQJson.DoParsed; +begin + +end; + +function TQJson.Encode(ADoFormat: Boolean; ADoEscape: Boolean; + AIndent: QStringW): QStringW; +var + ABuilder: TQStringCatHelperW; +begin + ABuilder := TQStringCatHelperW.Create; + try + InternalEncode(ABuilder, ADoFormat, ADoEscape, False, AIndent); + ABuilder.Back(1); // ɾһ + Result := ABuilder.Value; + finally + FreeObject(ABuilder); + end; +end; + +class function TQJson.EncodeDateTime(const AValue: TDateTime): QStringW; +var + ADate: Integer; +begin + ADate := Trunc(AValue); + if SameValue(ADate, 0) then // DateΪ0ʱ + begin + if SameValue(AValue, 0) then + Result := FormatDateTime(JsonDateFormat, AValue) + else + Result := FormatDateTime(JsonTimeFormat, AValue); + end + else + begin + if SameValue(AValue - ADate, 0) then + Result := FormatDateTime(JsonDateFormat, AValue) + else + Result := FormatDateTime(JsonDateTimeFormat, AValue); + end; +end; + +function TQJson.Escape(const S: QStringW): QStringW; +var + ABuilder: TQStringCatHelperW; +begin + ABuilder := TQStringCatHelperW.Create; + try + JsonCat(ABuilder, S, True); + Result := ABuilder.Value; + finally + FreeObject(ABuilder); + end; +end; + +{$IF RTLVersion>=21} + +function TQJson.FindIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEventA): TQJson; + function DoFind(AParent: TQJson): TQJson; + var + I: Integer; + AChild: TQJson; + Accept: Boolean; + begin + Result := nil; + for I := 0 to AParent.Count - 1 do + begin + AChild := AParent[I]; + Accept := True; + AFilter(Self, AChild, Accept, ATag); + if Accept then + Result := AChild + else if ANest then + Result := DoFind(AChild); + if Result <> nil then + Break; + end; + end; + +begin + if Assigned(AFilter) then + Result := DoFind(Self) + else + Result := nil; +end; +{$IFEND >=2010} + +procedure TQJson.FileFromValue(AFileName: QStringW); +var + AStream: TFileStream; +begin + AStream := TFileStream.Create(AFileName, fmCreate); + try + StreamFromValue(AStream); + finally + FreeObject(AStream); + end; +end; + +function TQJson.FindIf(const ATag: Pointer; ANest: Boolean; + AFilter: TQJsonFilterEvent): TQJson; + function DoFind(AParent: TQJson): TQJson; + var + I: Integer; + AChild: TQJson; + Accept: Boolean; + begin + Result := nil; + for I := 0 to AParent.Count - 1 do + begin + AChild := AParent[I]; + Accept := True; + AFilter(Self, AChild, Accept, ATag); + if Accept then + Result := AChild + else if ANest then + Result := DoFind(AChild); + if Result <> nil then + Break; + end; + end; + +begin + if Assigned(AFilter) then + Result := DoFind(Self) + else + Result := nil; +end; + +function TQJson.FloatByName(AName: QStringW; ADefVal: Extended): Extended; +var + AChild: TQJson; +begin + AChild := ItemByName(AName); + if Assigned(AChild) then + begin + try + Result := AChild.AsFloat; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +function TQJson.FloatByPath(APath: QStringW; ADefVal: Extended): Extended; +var + AItem: TQJson; +begin + AItem := ItemByPath(APath); + if Assigned(AItem) then + begin + try + Result := AItem.AsFloat; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +function TQJson.ForcePath(APath: QStringW): TQJson; +var + AName: QStringW; + p, pn, ws: PQCharW; + AParent: TQJson; + l: Integer; + AIndex: Int64; +const + PathDelimiters: PWideChar = './\'; +begin + p := PQCharW(APath); + AParent := Self; + Result := Self; + while p^ <> #0 do + begin + AName := DecodeTokenW(p, PathDelimiters, WideChar(0), True); + if not(AParent.DataType in [jdtObject, jdtArray]) then + AParent.DataType := jdtObject; + Result := AParent.ItemByName(AName); + if not Assigned(Result) then + begin + pn := PQCharW(AName); + l := Length(AName); + AIndex := -1; + if (pn[l - 1] = ']') then + begin + repeat + if pn[l] = '[' then + begin + ws := pn + l + 1; + if ParseInt(ws, AIndex) = 0 then + AIndex := -1; + Break; + end + else + Dec(l); + until l = 0; + if l > 0 then + begin + AName := StrDupX(pn, l); + Result := AParent.ItemByName(AName); + if Result = nil then + Result := AParent.Add(AName, jdtArray) + else if Result.DataType <> jdtArray then + raise Exception.CreateFmt(SBadJsonArray, [AName]); + if AIndex >= 0 then + begin + while Result.Count <= AIndex do + Result.Add; + Result := Result[AIndex]; + end; + end + else + raise Exception.CreateFmt(SBadJsonName, [AName]); + end + else + begin + if AParent.IsArray then + Result := AParent.Add.Add(AName) + else + Result := AParent.Add(AName); + end; + end; + AParent := Result; + end; +end; + +function TQJson.FormatParseError(ACode: Integer; AMsg: QStringW; ps, p: PQCharW) + : QStringW; +var + ACol, ARow: Integer; + ALine: QStringW; +begin + if ACode <> 0 then + begin + p := StrPosW(ps, p, ACol, ARow); + ALine := DecodeLineW(p, False); + if Length(ALine) > 1024 then // һ1024ַ + begin + SetLength(ALine, 1024); + PQCharW(ALine)[1023] := '.'; + PQCharW(ALine)[1022] := '.'; + PQCharW(ALine)[1021] := '.'; + end; + Result := Format(SJsonParseError, [ARow, ACol, AMsg, ALine]); + end + else + SetLength(Result, 0); +end; + +procedure TQJson.FreeJson(AJson: TQJson); +begin + if Assigned(OnQJsonFree) then + OnQJsonFree(AJson) + else + FreeObject(AJson); +end; +{$IF RTLVersion>=21} + +procedure TQJson.FromRecord(const ARecord: T); +begin + FromRtti(@ARecord, TypeInfo(T)); +end; + +procedure TQJson.FromRtti(ASource: Pointer; AType: PTypeInfo); +var + AValue: TValue; + procedure AddCollection(AParent: TQJson; ACollection: TCollection); + var + J: Integer; + begin + for J := 0 to ACollection.Count - 1 do + AParent.Add.FromRtti(ACollection.Items[J]); + end; +// XE6System.rttiTValuetkSetʹBug + function SetAsOrd(AValue: TValue): Int64; + var + ATemp: Integer; + begin + AValue.ExtractRawData(@ATemp); + case GetTypeData(AValue.TypeInfo).OrdType of + otSByte: + Result := PShortint(@ATemp)^; + otUByte: + Result := PByte(@ATemp)^; + otSWord: + Result := PSmallint(@ATemp)^; + otUWord: + Result := PWord(@ATemp)^; + otSLong: + Result := PInteger(@ATemp)^; + otULong: + Result := PCardinal(@ATemp)^ + else + Result := 0; + end; + end; + procedure AddRecord; + var + AContext: TRttiContext; + AFields: TArray; + ARttiType: TRttiType; + I, J: Integer; + AObj: TObject; + begin + AContext := TRttiContext.Create; + ARttiType := AContext.GetType(AType); + AFields := ARttiType.GetFields; + for J := Low(AFields) to High(AFields) do + begin + if AFields[J].FieldType <> nil then + begin + // Ǵӽṹ壬¼ԱǶֻ¼乫ԣ⴦TStringsTCollection + case AFields[J].FieldType.TypeKind of + tkInteger: + Add(AFields[J].Name).AsInteger := AFields[J].GetValue(ASource) + .AsInteger; +{$IFNDEF NEXTGEN}tkString, tkLString, tkWString, +{$ENDIF !NEXTGEN}tkUString: + Add(AFields[J].Name).AsString := + AFields[J].GetValue(ASource).AsString; + tkEnumeration: + begin + if GetTypeData(AFields[J].FieldType.Handle) + .BaseType^ = TypeInfo(Boolean) then + Add(AFields[J].Name).AsBoolean := AFields[J].GetValue(ASource) + .AsBoolean + else if JsonRttiEnumAsInt then + Add(AFields[J].Name).AsInteger := AFields[J].GetValue(ASource) + .AsOrdinal + else + Add(AFields[J].Name).AsString := + AFields[J].GetValue(ASource).ToString; + end; + tkSet: + begin + if JsonRttiEnumAsInt then + Add(AFields[J].Name).AsInt64 := + SetAsOrd(AFields[J].GetValue(ASource)) + else + Add(AFields[J].Name).AsString := + AFields[J].GetValue(ASource).ToString; + end; + tkChar, tkWChar: + Add(AFields[J].Name).AsString := + AFields[J].GetValue(ASource).ToString; + tkFloat: + begin + if (AFields[J].FieldType.Handle = TypeInfo(TDateTime)) or + (AFields[J].FieldType.Handle = TypeInfo(TTime)) or + (AFields[J].FieldType.Handle = TypeInfo(TDate)) then + begin + // жһֵǷһЧֵ + + Add(AFields[J].Name).AsDateTime := AFields[J].GetValue(ASource) + .AsExtended + end + else + Add(AFields[J].Name).AsFloat := AFields[J].GetValue(ASource) + .AsExtended; + end; + tkInt64: + Add(AFields[J].Name).AsInt64 := + AFields[J].GetValue(ASource).AsInt64; + tkVariant: + Add(AFields[J].Name).AsVariant := AFields[J].GetValue(ASource) + .AsVariant; + tkArray, tkDynArray: + begin + with Add(AFields[J].Name, jdtArray) do + begin + AValue := AFields[J].GetValue(ASource); + for I := 0 to AValue.GetArrayLength - 1 do + Add.FromRtti(AValue.GetArrayElement(I)); + end; + end; + tkClass: + begin + AValue := AFields[J].GetValue(ASource); + AObj := AValue.AsObject; + if (AObj is TStrings) then + Add(AFields[J].Name).AsString := TStrings(AObj).Text + else if AObj is TCollection then + AddCollection(AddArray(AFields[J].Name), AObj as TCollection) + else // ͵Ķ󲻱 + Add(AFields[J].Name, jdtObject) + .FromRtti(AObj, AFields[J].FieldType.Handle); + end; + tkRecord: + begin + DataType := jdtObject; + AValue := AFields[J].GetValue(ASource); + Add(AFields[J].Name) + .FromRtti(Pointer(IntPtr(ASource) + AFields[J].Offset), + AFields[J].FieldType.Handle); + end; + end; + end + else + raise Exception.CreateFmt(SMissRttiTypeDefine, [AFields[J].Name]); + end; + end; + + procedure AddObject; + var + APropList: PPropList; + ACount: Integer; + J: Integer; + AObj, AChildObj: TObject; + AName: String; + begin + AObj := ASource; + if AObj is TStrings then + AsString := (AObj as TStrings).Text + else if AObj is TCollection then + begin + DataType := jdtArray; + AddCollection(Self, AObj as TCollection) + end + else + begin + ACount := GetPropList(AType, APropList); + try + for J := 0 to ACount - 1 do + begin + if not(APropList[J].PropType^.Kind in [tkMethod, tkInterface, + tkClassRef, tkPointer, tkProcedure]) then + begin +{$IF RTLVersion>25} + AName := APropList[J].NameFld.ToString; +{$ELSE} + AName := String(APropList[J].Name); +{$IFEND} + case APropList[J].PropType^.Kind of + tkClass: + begin + AChildObj := Pointer(GetOrdProp(AObj, APropList[J])); + if AChildObj is TStrings then + Add(AName).AsString := (AChildObj as TStrings).Text + else if AChildObj is TCollection then + AddCollection(AddArray(AName), AChildObj as TCollection) + else + Add(AName, jdtObject).FromRtti(AChildObj); + end; + tkRecord, tkArray, tkDynArray: // ¼顢̬ϵͳҲ棬Ҳûṩ̫õĽӿ + raise Exception.Create(SUnsupportPropertyType); + tkInteger: + Add(AName).AsInt64 := GetOrdProp(AObj, APropList[J]); + tkFloat: + begin + if (APropList[J].PropType^ = TypeInfo(TDateTime)) or + (APropList[J].PropType^ = TypeInfo(TTime)) or + (APropList[J].PropType^ = TypeInfo(TDate)) then + begin + // жһֵǷһЧֵ + Add(AName).AsDateTime := GetFloatProp(AObj, APropList[J]); + end + else + Add(AName).AsFloat := GetFloatProp(AObj, APropList[J]); + end; + tkChar, tkString, tkWChar, tkLString, tkWString, tkUString: + Add(AName).AsString := GetStrProp(AObj, APropList[J]); + tkEnumeration: + begin + if GetTypeData(APropList[J]^.PropType^) + ^.BaseType^ = TypeInfo(Boolean) then + Add(AName).AsBoolean := GetOrdProp(AObj, APropList[J]) <> 0 + else if JsonRttiEnumAsInt then + Add(AName).AsInteger := GetOrdProp(AObj, APropList[J]) + else + Add(AName).AsString := GetEnumProp(AObj, APropList[J]); + end; + tkSet: + begin + if JsonRttiEnumAsInt then + Add(AName).AsInteger := GetOrdProp(AObj, APropList[J]) + else + Add(AName).AsString := GetSetProp(AObj, APropList[J], True); + end; + tkVariant: + Add(AName).AsVariant := GetPropValue(AObj, APropList[J]); + tkInt64: + Add(AName).AsInt64 := GetInt64Prop(AObj, APropList[J]); + end; + end; + end; + finally + FreeMem(APropList); + end; + end; + end; + + procedure AddArray; + var + I, c: Integer; + begin + DataType := jdtArray; + Clear; + TValue.Make(ASource, AType, AValue); + c := AValue.GetArrayLength; + for I := 0 to c - 1 do + Add.FromRtti(AValue.GetArrayElement(I)); + end; + +begin + if ASource = nil then + Exit; + Clear; + case AType.Kind of + tkRecord: + AddRecord; + tkClass: + AddObject; + tkDynArray: + AddArray; + end; +end; + +procedure TQJson.FromRtti(AInstance: TValue); +var + I, c: Integer; +begin + case AInstance.Kind of + tkClass: + FromRtti(AInstance.AsObject, AInstance.TypeInfo); + tkRecord: + FromRtti(AInstance.GetReferenceToRawData, AInstance.TypeInfo); + tkArray, tkDynArray: + begin + DataType := jdtArray; + Clear; + c := AInstance.GetArrayLength; + for I := 0 to c - 1 do + Add.FromRtti(AInstance.GetArrayElement(I)); + end; + tkInteger: + AsInt64 := AInstance.AsInt64; + tkChar, tkString, tkWChar, tkLString, tkWString, tkUString: + AsString := AInstance.ToString; + tkEnumeration: + begin + if GetTypeData(AInstance.TypeInfo)^.BaseType^ = TypeInfo(Boolean) then + AsBoolean := AInstance.AsBoolean + else if JsonRttiEnumAsInt then + AsInteger := AInstance.AsOrdinal + else + AsString := AInstance.ToString; + end; + tkSet: + AsString := AInstance.ToString; + tkVariant: + AsVariant := AInstance.AsVariant; + tkInt64: + AsInt64 := AInstance.AsInt64; + end; +end; +{$IFEND >=2010} + +function TQJson.GetAsArray: QStringW; +begin + if DataType = jdtArray then + Result := Value + else + raise Exception.Create(Format(SBadConvert, [AsString, 'Array'])); +end; + +function TQJson.GetAsBoolean: Boolean; +begin + if DataType = jdtBoolean then + Result := PBoolean(FValue)^ + else if DataType = jdtString then + begin + if not TryStrToBool(FValue, Result) then + raise Exception.Create(Format(SBadConvert, [FValue, 'Boolean'])); + end + else if DataType in [jdtFloat, jdtDateTime] then + Result := not SameValue(AsFloat, 0, 5E-324) + else if DataType = jdtInteger then + Result := AsInt64 <> 0 + else + raise Exception.Create(Format(SBadConvert, [JsonTypeName[DataType], + 'Boolean'])); +end; + +function TQJson.GetAsBytes: TBytes; +var + I: Integer; + AItem: TQJson; + procedure StrToBin; + var + S: QStringW; + begin + S := AsString; + Result := HexToBin(S); + if Length(Result) = 0 then + raise Exception.CreateFmt(SConvertError, ['jdtString', 'Bytes']); + end; + +begin + if DataType = jdtString then // ַ + begin + if Assigned(OnQJsonDecodeBytes) then + OnQJsonDecodeBytes(AsString, Result) + else + Result := HexToBin(AsString); + end + else if DataType = jdtArray then + begin + SetLength(Result, Count); + for I := 0 to Count - 1 do + begin + AItem := Items[I]; + if (AItem.DataType = jdtInteger) and (AItem.AsInteger >= 0) and + (AItem.AsInteger <= 255) then + Result[I] := AItem.AsInteger + else + raise Exception.CreateFmt(SConvertError, ['jdtArray', 'Bytes']); + end; + end + else + raise Exception.CreateFmt(SConvertError, ['jdtArray', 'Bytes']); +end; + +function TQJson.GetAsDateTime: TDateTime; +begin + if DataType in [jdtDateTime, jdtFloat] then + Result := PExtended(FValue)^ + else if DataType = jdtString then + begin + if not(ParseDateTime(PWideChar(FValue), Result) or + ParseJsonTime(PWideChar(FValue), Result) or ParseWebTime(PQCharW(FValue), + Result)) then + raise Exception.Create(Format(SBadConvert, ['String', 'DateTime'])) + end + else if DataType = jdtInteger then + Result := AsInt64 + else if DataType in [jdtNull, jdtUnknown] then + Result := 0 + else + raise Exception.Create(Format(SBadConvert, [JsonTypeName[DataType], + 'DateTime'])); +end; + +function TQJson.GetAsFloat: Extended; + procedure StrAsFloat; + var + p: PQCharW; + begin + p := PQCharW(FValue); + if (not ParseNumeric(p, Result)) or (p^ <> #0) then + raise Exception.Create(Format(SBadConvert, [FValue, 'Numeric'])); + end; + +begin + if DataType in [jdtFloat, jdtDateTime] then + Result := PExtended(FValue)^ + else if DataType = jdtBoolean then + Result := Integer(AsBoolean) + else if DataType = jdtString then + StrAsFloat + else if DataType = jdtInteger then + Result := AsInt64 + else if DataType = jdtNull then + Result := 0 + else + raise Exception.Create(Format(SBadConvert, [JsonTypeName[DataType], + 'Numeric'])) +end; + +function TQJson.GetAsInt64: Int64; +begin + if DataType = jdtInteger then + Result := PInt64(FValue)^ + else if DataType in [jdtFloat, jdtDateTime] then + Result := Trunc(PExtended(FValue)^) + else if DataType = jdtBoolean then + Result := Integer(AsBoolean) + else if DataType = jdtString then + Result := Trunc(AsFloat) + else if DataType = jdtNull then + Result := 0 + else + raise Exception.Create(Format(SBadConvert, [JsonTypeName[DataType], + 'Numeric'])) +end; + +function TQJson.GetAsInteger: Integer; +begin + Result := GetAsInt64; +end; + +function TQJson.GetAsJson: QStringW; +begin + Result := Encode(True, False, ' '); +end; + +function TQJson.GetAsObject: QStringW; +begin + if DataType = jdtObject then + Result := Value + else + raise Exception.Create(Format(SBadConvert, [AsString, 'Object'])); +end; + +function TQJson.GetAsString: QStringW; +begin + if DataType in [jdtNull, jdtUnknown] then + SetLength(Result, 0) + else + Result := Value; +end; + +function TQJson.GetAsVariant: Variant; +var + I: Integer; +begin + case DataType of + jdtNull: + Result := Null; + jdtString: + begin + if IsDateTime then + Result := AsDateTime + else + Result := AsString; + end; + jdtInteger: + Result := AsInt64; + jdtFloat: + Result := AsFloat; + jdtDateTime: + Result := AsDateTime; + jdtBoolean: + Result := AsBoolean; + jdtArray, jdtObject: + begin + Result := VarArrayCreate([0, Count - 1], varVariant); + for I := 0 to Count - 1 do + Result[I] := Items[I].AsVariant; + end + else + VarClear(Result); + end; +end; + +function TQJson.GetCount: Integer; +begin + if DataType in [jdtObject, jdtArray] then + Result := FItems.Count + else + Result := 0; +end; + +function TQJson.GetEnumerator: TQJsonEnumerator; +begin + Result := TQJsonEnumerator.Create(Self); +end; + +function TQJson.GetIsArray: Boolean; +begin + Result := (DataType = jdtArray); +end; + +function TQJson.GetIsBool: Boolean; +begin + if DataType = jdtBoolean then + Result := True + else if DataType = jdtString then + Result := TryStrToBool(FValue, Result) + else + Result := DataType in [jdtInteger, jdtFloat, jdtDateTime]; +end; + +function TQJson.GetIsDateTime: Boolean; +var + ATime: TDateTime; +begin + Result := (DataType = jdtDateTime); + if not Result then + begin + if DataType = jdtString then + Result := ParseDateTime(PQCharW(FValue), ATime) or + ParseJsonTime(PQCharW(FValue), ATime) or + ParseWebTime(PQCharW(FValue), ATime); + end; +end; + +function TQJson.GetIsNull: Boolean; +begin + Result := (DataType = jdtNull); +end; + +function TQJson.GetIsNumeric: Boolean; +var + V: Extended; +begin + if DataType in [jdtInteger, jdtFloat] then + Result := True + else if (DataType = jdtString) then + Result := TryStrToFloat(AsString, V) + else + Result := False; +end; + +function TQJson.GetIsObject: Boolean; +begin + Result := (DataType = jdtObject); +end; + +function TQJson.GetIsString: Boolean; +begin + Result := (DataType = jdtString); +end; + +function TQJson.GetItemIndex: Integer; +var + I: Integer; +begin + Result := -1; + if Assigned(Parent) then + begin + for I := 0 to Parent.Count - 1 do + begin + if Parent.Items[I] = Self then + begin + Result := I; + Break; + end; + end; + end; +end; + +function TQJson.GetItems(AIndex: Integer): TQJson; +begin + Result := FItems[AIndex]; +end; + +function TQJson.GetPath: QStringW; +var + AParent, AItem: TQJson; +begin + AParent := FParent; + AItem := Self; + SetLength(Result, 0); + repeat + if Assigned(AParent) and AParent.IsArray then + Result := '[' + IntToStr(AItem.ItemIndex) + ']' + Result + else if AItem.IsArray then + Result := '\' + AItem.FName + Result + else + Result := '\' + AItem.FName + Result; + if AParent <> nil then + begin + AItem := AParent; + AParent := AItem.Parent; + end; + until AParent = nil; + if Length(Result) > 0 then + Result := StrDupX(PQCharW(Result) + 1, Length(Result) - 1); +end; + +function TQJson.GetRoot: TQJson; +begin + Result := Self; + while Result.FParent <> nil do + Result := Result.FParent; +end; + +function TQJson.GetValue: QStringW; + procedure ValueAsDateTime; + var + ADate: Integer; + AValue: Extended; + begin + AValue := PExtended(FValue)^; + ADate := Trunc(AValue); + if SameValue(ADate, 0) then // DateΪ0ʱ + begin + if SameValue(AValue, 0) then + Result := FormatDateTime(JsonDateFormat, AValue) + else + Result := FormatDateTime(JsonTimeFormat, AValue); + end + else + begin + if SameValue(AValue - ADate, 0) then + Result := FormatDateTime(JsonDateFormat, AValue) + else + Result := FormatDateTime(JsonDateTimeFormat, AValue); + end; + end; + +begin + case DataType of + jdtNull, jdtUnknown: + Result := CharNull; + jdtString: + Result := FValue; + jdtInteger: + Result := IntToStr(PInt64(FValue)^); + jdtFloat: + Result := FloatToStr(PExtended(FValue)^); + jdtDateTime: + ValueAsDateTime; + jdtBoolean: + Result := BooleanToStr(PBoolean(FValue)^); + jdtArray, jdtObject: + Result := Encode(True); + end; +end; + +function TQJson.HasChild(ANamePath: QStringW; var AChild: TQJson): Boolean; +begin + AChild := ItemByPath(ANamePath); + Result := AChild <> nil; +end; + +function TQJson.HashName(const S: QStringW): TQHashType; +var + ATemp: QStringW; +begin + if IgnoreCase then + begin + ATemp := UpperCase(S); + Result := HashOf(PQCharW(S), Length(S) shl 1); + end + else + Result := HashOf(PQCharW(S), Length(S) shl 1) +end; + +function TQJson.IndexOf(const AName: QStringW): Integer; +var + I, l: Integer; + AItem: TQJson; + AHash: Cardinal; +begin + Result := -1; + l := Length(AName); + if l > 0 then + AHash := HashName(AName) + else + begin + Exit; + end; + for I := 0 to Count - 1 do + begin + AItem := Items[I]; + if Length(AItem.FName) = l then + begin + if not IgnoreCase then + begin + if AItem.FNameHash = 0 then + AItem.FNameHash := HashName(AItem.FName); + if AItem.FNameHash = AHash then + begin + if AItem.FName = AName then + begin + Result := I; + Break; + end; + end; + end + else if StartWithW(PQCharW(AItem.FName), PQCharW(AName), True) then + begin + Result := I; + Break; + end; + end; + end; +end; + +function TQJson.IntByName(AName: QStringW; ADefVal: Int64): Int64; +var + AChild: TQJson; +begin + AChild := ItemByName(AName); + if Assigned(AChild) then + begin + try + Result := AChild.AsInt64; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +function TQJson.IntByPath(APath: QStringW; ADefVal: Int64): Int64; +var + AItem: TQJson; +begin + AItem := ItemByPath(APath); + if Assigned(AItem) then + begin + try + Result := AItem.AsInt64; + except + Result := ADefVal; + end; + end + else + Result := ADefVal; +end; + +function TQJson.InternalEncode(ABuilder: TQStringCatHelperW; ADoFormat: Boolean; + ADoEscape: Boolean; ANullConvert: Boolean; const AIndent: QStringW) + : TQStringCatHelperW; + procedure StrictJsonTime(ATime: TDateTime); + var + MS: Int64; // ʱϢ + const + JsonTimeStart: PWideChar = '"/DATE('; + JsonTimeEnd: PWideChar = ')/"'; + begin + MS := Trunc(ATime * 86400000); + ABuilder.Cat(JsonTimeStart, 7); + ABuilder.Cat(IntToStr(MS)); + ABuilder.Cat(JsonTimeEnd, 3); + end; + + procedure DoEncode(ANode: TQJson; ALevel: Integer); + var + I: Integer; + ArrayWraped: Boolean; + AChild: TQJson; + begin + if (ANode.Parent <> nil) and (ANode.Parent.DataType <> jdtArray) and + (ANode <> Self) then + begin + if ADoFormat then + ABuilder.Replicate(AIndent, ALevel); + ABuilder.Cat(CharNameStart); + JsonCat(ABuilder, ANode.FName, ADoEscape); + ABuilder.Cat(CharNameEnd); + end; + case ANode.DataType of + jdtArray: + begin + ABuilder.Cat(CharArrayStart); + if ANode.Count > 0 then + begin + ArrayWraped := False; + for I := 0 to ANode.Count - 1 do + begin + AChild := ANode.Items[I]; + if AChild.DataType in [jdtArray, jdtObject] then + begin + if ADoFormat then + begin + ABuilder.Cat(SLineBreak); // ڶ飬 + ABuilder.Replicate(AIndent, ALevel + 1); + ArrayWraped := True; + end; + end; + DoEncode(AChild, ALevel + 1); + end; + ABuilder.Back(1); + if ArrayWraped then + begin + ABuilder.Cat(SLineBreak); + ABuilder.Replicate(AIndent, ALevel); + end; + end; + ABuilder.Cat(CharArrayEnd); + end; + jdtObject: + begin + if ADoFormat then + begin + ABuilder.Cat(CharObjectStart); + ABuilder.Cat(SLineBreak); + end + else + ABuilder.Cat(CharObjectStart); + if ANode.Count > 0 then + begin + for I := 0 to ANode.Count - 1 do + begin + AChild := ANode.Items[I]; + // if Length(AChild.Name) = 0 then + // raise Exception.CreateFmt(SObjectChildNeedName, [ANode.Name, I]); + DoEncode(AChild, ALevel + 1); + if ADoFormat then + ABuilder.Cat(SLineBreak); + end; + if ADoFormat then + ABuilder.Back(Length(SLineBreak) + 1) + else + ABuilder.Back(1); + end; + if ADoFormat then + begin + ABuilder.Cat(SLineBreak); + ABuilder.Replicate(AIndent, ALevel); + end; + ABuilder.Cat(CharObjectEnd); + end; + jdtNull, jdtUnknown: + begin + if ANullConvert then + ABuilder.Cat(CharStringStart).Cat(CharStringEnd) + else + begin + ABuilder.Cat(CharNull); + ABuilder.Cat(CharComma); + end; + end; + jdtString: + begin + ABuilder.Cat(CharStringStart); + JsonCat(ABuilder, ANode.FValue, ADoEscape); + ABuilder.Cat(CharStringEnd); + end; + jdtInteger, jdtFloat, jdtBoolean: + begin + ABuilder.Cat(ANode.Value); + ABuilder.Cat(CharComma); + end; + jdtDateTime: + begin + ABuilder.Cat(CharStringStart); + if StrictJson then + StrictJsonTime(ANode.AsDateTime) + else + ABuilder.Cat(ANode.Value); + ABuilder.Cat(CharStringEnd); + end; + end; + end; + +begin + Result := ABuilder; + DoEncode(Self, 0); +end; + +procedure TQJson.InternalRttiFilter(ASender: TQJson; AObject: Pointer; + APropName: QStringW; APropType: PTypeInfo; var Accept: Boolean; + ATag: Pointer); +var + ATagData: PQJsonInternalTagData; + procedure DoNameFilter; + var + ps: PQCharW; + begin + if Length(ATagData.AcceptNames) > 0 then + begin + Accept := False; + ps := StrIStrW(PQCharW(ATagData.AcceptNames), PQCharW(APropName)); + if (ps <> nil) and ((ps = PQCharW(ATagData.AcceptNames)) or (ps[-1] = ',') + or (ps[-1] = ';')) then + begin + ps := ps + Length(APropName); + Accept := (ps^ = ',') or (ps^ = ';') or (ps^ = #0); + end; + end + else if Length(ATagData.IgnoreNames) > 0 then + begin + ps := StrIStrW(PQCharW(ATagData.IgnoreNames), PQCharW(APropName)); + Accept := True; + if (ps <> nil) and ((ps = PQCharW(ATagData.IgnoreNames)) or (ps[-1] = ',') + or (ps[-1] = ';')) then + begin + ps := ps + Length(APropName); + Accept := not((ps^ = ',') or (ps^ = ';') or (ps^ = #0)); + end; + end; + end; + +begin + ATagData := PQJsonInternalTagData(ATag); + if ATagData.TagType = ttNameFilter then + begin + DoNameFilter; + Exit; + end; +{$IF RTLVersion>=21} + if ATagData.TagType = ttAnonEvent then + begin + ATagData.OnEvent(ASender, AObject, APropName, APropType, Accept, + ATagData.Tag); + end; +{$IFEND >=2010} +end; + +function TQJson.IsChildOf(AParent: TQJson): Boolean; +begin + if Assigned(FParent) then + begin + if AParent = FParent then + Result := True + else + Result := FParent.IsChildOf(AParent); + end + else + Result := False; +end; + +function TQJson.IsParentOf(AChild: TQJson): Boolean; +begin + if Assigned(AChild) then + Result := AChild.IsChildOf(Self) + else + Result := False; +end; + +function TQJson.ItemByName(AName: QStringW): TQJson; +var + I: Integer; + p: PQCharW; + AIndex: Int64; +begin + Result := nil; + p := PQCharW(AName); + if (p^ = '[') and (DataType in [jdtObject, jdtArray]) then + begin + Inc(p); + SkipSpaceW(p); + if ParseInt(p, AIndex) <> 0 then + begin + SkipSpaceW(p); + if p^ = ']' then + begin + Inc(p); + if p^ <> #0 then + Exit; + end + else + Exit; + end + else + Exit; + if (AIndex >= 0) and (AIndex < Count) then + Result := Items[AIndex]; + end + else if DataType = jdtObject then + begin + I := IndexOf(AName); + if I <> -1 then + Result := Items[I]; + end; +end; + +function TQJson.ItemByName(const AName: QStringW; AList: TQJsonItemList; + ANest: Boolean): Integer; +var + AHash: Cardinal; + l: Integer; + function InternalFind(AParent: TQJson): Integer; + var + I: Integer; + AItem: TQJson; + begin + Result := -1; + for I := 0 to Count - 1 do + begin + AItem := Items[I]; + if Length(AItem.FName) = l then + begin + if not AItem.IgnoreCase then + begin + if AItem.FNameHash = 0 then + AItem.FNameHash := HashName(AItem.FName); + if AItem.FNameHash = AHash then + begin + if AItem.FName = AName then + AList.Add(AItem); + end; + end + else if StartWithW(PQCharW(AItem.FName), PQCharW(AName), True) then + AList.Add(AItem); + end; + if ANest then + InternalFind(AItem); + end; + end; + +begin + l := Length(AName); + if l > 0 then + begin + AHash := HashName(AName); + Result := InternalFind(Self); + end + else + begin + AHash := 0; + Result := -1; + Exit; + end; +end; + +function TQJson.ItemByPath(APath: QStringW): TQJson; +var + AParent: TQJson; + AName: QStringW; + p, pn, ws: PQCharW; + l: Integer; + AIndex: Int64; +const + PathDelimiters: PWideChar = './\'; + ArrayStart: PWideChar = '['; +begin + AParent := Self; + p := PQCharW(APath); + Result := nil; + while Assigned(AParent) and (p^ <> #0) do + begin + AName := DecodeTokenW(p, PathDelimiters, WideChar(0), False); + if Length(AName) > 0 then + begin + // ҵ飿 + l := Length(AName); + AIndex := -1; + pn := PQCharW(AName); + if (pn[l - 1] = ']') then + begin + ws := pn; + SkipUntilW(ws, ArrayStart); + Result := AParent.ItemByName + (StrDupX(pn, (IntPtr(ws) - IntPtr(pn)) shr 1)); + if Result <> nil then + begin + if Result.DataType in [jdtArray, jdtObject] then + begin + repeat + Inc(ws); + SkipSpaceW(ws); + if ParseInt(ws, AIndex) <> 0 then + begin + if (AIndex >= 0) and (AIndex < Result.Count) then + begin + Result := Result[AIndex]; + SkipSpaceW(ws); + if ws^ = ']' then + begin + Inc(ws); + SkipSpaceW(ws); + if ws^ = '[' then + Continue + else if ws^ = #0 then + Break + else + Result := nil; + end + else + Result := nil; + end + else + Result := nil; + end; + until Result = nil; + end + end; + end + else + Result := AParent.ItemByName(AName); + if Assigned(Result) then + AParent := Result + else + begin + Exit; + end; + end; + if CharInW(p, PathDelimiters) then + Inc(p); + // ..//\\· + end; + if p^ <> #0 then + Result := nil; +end; + +function TQJson.ItemByRegex(const ARegex: QStringW; AList: TQJsonItemList; + ANest: Boolean): Integer; +var + ANode: TQJson; + APcre: TPerlRegEx; + function RegexStr(const S: QStringW): +{$IF RTLVersion<=24}UTF8String{$ELSE}UnicodeString{$IFEND}; + begin +{$IF RTLVersion<19} + Result := System.UTF8Encode(S); +{$ELSE} +{$IF RTLVersion<=24} + Result := UTF8String(S); +{$ELSE} + Result := S; +{$IFEND} +{$IFEND} + end; + function InternalFind(AParent: TQJson): Integer; + var + I: Integer; + begin + Result := 0; + for I := 0 to AParent.Count - 1 do + begin + ANode := AParent.Items[I]; + APcre.Subject := RegexStr(ANode.Name); + if APcre.Match then + begin + AList.Add(ANode); + Inc(Result); + end; + if ANest then + Inc(Result, InternalFind(ANode)); + end; + end; + +begin + APcre := TPerlRegEx.Create; + try + APcre.RegEx := RegexStr(ARegex); + APcre.Compile; + Result := InternalFind(Self); + finally + FreeObject(APcre); + end; +end; + +class function TQJson.JsonCat(const S: QStringW; ADoEscape: Boolean): QStringW; +var + ABuilder: TQStringCatHelperW; +begin + ABuilder := TQStringCatHelperW.Create; + try + JsonCat(ABuilder, S, ADoEscape); + Result := ABuilder.Value; + finally + FreeObject(ABuilder); + end; +end; + +class function TQJson.JsonEscape(const S: QStringW; ADoEscape: Boolean) + : QStringW; +begin + Result := JsonCat(S, ADoEscape); +end; + +class function TQJson.JsonUnescape(const S: QStringW): QStringW; +begin + Result := BuildJsonString(S); +end; + +procedure TQJson.LoadFromFile(AFileName: String; AEncoding: TTextEncoding); +var + AStream: TFileStream; +begin + AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(AStream, AEncoding); + finally + FreeObject(AStream); + end; +end; + +procedure TQJson.LoadFromStream(AStream: TStream; AEncoding: TTextEncoding); +var + S: QStringW; +begin + S := LoadTextW(AStream, AEncoding); + case Length(S) of + 0: + DataType := jdtNull; + 1: + raise Exception.Create(SBadJson) + else + Parse(PQCharW(S), Length(S)); + end; +end; + +procedure TQJson.MoveTo(ANewParent: TQJson; AIndex: Integer); +begin + if ANewParent = Self then + raise Exception.Create(SCantAttachToSelf) + else + begin + if Parent = ANewParent then + Exit; + if IsParentOf(ANewParent) then + raise Exception.Create(SCantMoveToChild); + if ANewParent.DataType in [jdtArray, jdtObject] then + begin + if ANewParent.DataType = jdtObject then + begin + if Length(Name) = 0 then + raise Exception.Create(SCantAttachNoNameNodeToObject) + else if ANewParent.IndexOf(Name) <> -1 then + raise Exception.CreateFmt(SNodeNameExists, [Name]);; + end; + if Assigned(FParent) then + FParent.Remove(Self); + FParent := ANewParent; + if AIndex >= ANewParent.Count then + ANewParent.FItems.Add(Self) + else if AIndex <= 0 then + ANewParent.FItems.Insert(0, Self) + else + ANewParent.FItems.Insert(AIndex, Self); + DoJsonNameChanged(Self); + end + else + raise Exception.Create(SCanAttachToNoneContainer); + end; +end; + +procedure TQJson.Parse(p: PWideChar; l: Integer); + procedure ParseCopy; + var + S: QStringW; + begin + S := StrDupW(p, 0, l); + p := PQCharW(S); + ParseObject(p); + end; + +begin + if DataType in [jdtObject, jdtArray] then + Clear; + if (l > 0) and (p[l] <> #0) then + ParseCopy + else + ParseObject(p); +end; + +procedure TQJson.Parse(const S: QStringW); +begin + Parse(PQCharW(S), Length(S)); +end; + +procedure TQJson.ParseBlock(AStream: TStream; AEncoding: TTextEncoding); +var + AMS: TMemoryStream; + procedure ParseUCS2; + var + c: QCharW; + ABlockCount: Integer; + begin + ABlockCount := 0; + repeat + if ABlockCount = 0 then + begin + repeat + AStream.ReadBuffer(c, SizeOf(QCharW)); + AMS.WriteBuffer(c, SizeOf(QCharW)); + until c = '{'; + Inc(ABlockCount); + end; + AStream.ReadBuffer(c, SizeOf(QCharW)); + if c = '{' then + Inc(ABlockCount) + else if c = '}' then + Dec(ABlockCount); + AMS.WriteBuffer(c, SizeOf(QCharW)); + until ABlockCount = 0; + c := #0; + AMS.Write(c, SizeOf(QCharW)); + Parse(AMS.Memory, AMS.Size - 1); + end; + + procedure ParseUCS2BE; + var + c: Word; + ABlockCount: Integer; + p: PQCharW; + begin + ABlockCount := 0; + repeat + if ABlockCount = 0 then + begin + repeat + AStream.ReadBuffer(c, SizeOf(Word)); + c := (c shr 8) or ((c shl 8) and $FF00); + AMS.WriteBuffer(c, SizeOf(Word)); + until c = $7B; // #$7B={ + Inc(ABlockCount); + end; + AStream.ReadBuffer(c, SizeOf(Word)); + c := (c shr 8) or ((c shl 8) and $FF00); + if c = $7B then + Inc(ABlockCount) + else if c = $7D then // #$7D=} + Dec(ABlockCount); + AMS.WriteBuffer(c, SizeOf(QCharW)); + until ABlockCount = 0; + c := 0; + AMS.Write(c, SizeOf(QCharW)); + p := AMS.Memory; + ParseObject(p); + end; + + procedure ParseByByte; + var + c: Byte; + ABlockCount: Integer; + begin + ABlockCount := 0; + repeat + if ABlockCount = 0 then + begin + repeat + AStream.ReadBuffer(c, SizeOf(Byte)); + AMS.WriteBuffer(c, SizeOf(Byte)); + until c = $7B; // #$7B={ + Inc(ABlockCount); + end; + AStream.ReadBuffer(c, SizeOf(Byte)); + if c = $7B then + Inc(ABlockCount) + else if c = $7D then // #$7D=} + Dec(ABlockCount); + AMS.WriteBuffer(c, SizeOf(Byte)); + until ABlockCount = 0; + end; + + procedure ParseUtf8; + var + S: QStringW; + p: PQCharW; + begin + ParseByByte; + S := qstring.Utf8Decode(AMS.Memory, AMS.Size); + p := PQCharW(S); + ParseObject(p); + end; + + procedure ParseAnsi; + var + S: QStringW; + begin + ParseByByte; + S := qstring.AnsiDecode(AMS.Memory, AMS.Size); + Parse(PQCharW(S)); + end; + +begin + AMS := TMemoryStream.Create; + try + if AEncoding = teAnsi then + ParseAnsi + else if AEncoding = teUtf8 then + ParseUtf8 + else if AEncoding = teUnicode16LE then + ParseUCS2 + else if AEncoding = teUnicode16BE then + ParseUCS2BE + else + raise Exception.Create(SBadJsonEncoding); + finally + AMS.Free; + end; +end; + +function TQJson.ParseJsonPair(ABuilder: TQStringCatHelperW; + var p: PQCharW): Integer; +const + SpaceWithSemicolon: PWideChar = ': '#9#10#13#$3000; + CommaWithSpace: PWideChar = ', '#9#10#13#$3000; + JsonEndChars: PWideChar = ',}]'; + JsonComplexEnd: PWideChar = '}]'; +var + AChild: TQJson; + AObjEnd: QCharW; +begin + Result := SkipSpaceAndComment(p); + if Result <> 0 then + Exit; + // ֵ + if (p^ = '{') or (p^ = '[') then // + begin + try + if p^ = '{' then + begin + DataType := jdtObject; + AObjEnd := '}'; + end + else + begin + DataType := jdtArray; + AObjEnd := ']'; + end; + Inc(p); + Result := SkipSpaceAndComment(p); + while (p^ <> #0) and (p^ <> AObjEnd) do + begin + if (p^ <> AObjEnd) then + begin + AChild := Add; + Result := AChild.ParseJsonPair(ABuilder, p); + if Result <> 0 then + Exit; + if p^ = ',' then + begin + Inc(p); + Result := SkipSpaceAndComment(p); + if Result <> 0 then + Exit; + end; + end + else + Exit; + end; + Result := SkipSpaceAndComment(p); + if Result <> 0 then + Exit; + if p^ <> AObjEnd then + begin + Result := EParse_BadJson; + Exit; + end + else + begin + Inc(p); + SkipSpaceAndComment(p); + end; + except + Clear; + raise; + end; + end + else if Parent <> nil then + begin + if (Parent.DataType = jdtObject) and (Length(FName) = 0) then + begin + Result := ParseName(ABuilder, p); + if Result <> 0 then + Exit; + end; + Result := TryParseValue(ABuilder, p); + if Result = 0 then + begin + if not CharInW(p, JsonEndChars) then + begin + Result := EParse_EndCharNeeded; + end; + end; + end + else + Result := EParse_BadJson; +end; + +function TQJson.ParseJsonTime(p: PQCharW; var ATime: TDateTime): Boolean; +var + MS, TimeZone: Int64; +begin + // JavascriptڸʽΪ/DATE(1970.1.1ڵĺ+ʱ)/ + Result := False; + if not StartWithW(p, '/DATE', False) then + Exit; + Inc(p, 5); + SkipSpaceW(p); + if p^ <> '(' then + Exit; + Inc(p); + SkipSpaceW(p); + if ParseInt(p, MS) = 0 then + Exit; + SkipSpaceW(p); + if (p^ = '+') or (p^ = '-') then + begin + if ParseInt(p, TimeZone) = 0 then + Exit; + SkipSpaceW(p); + end + else + TimeZone := 0; + if p^ = ')' then + begin + ATime := (MS div 86400000) + ((MS mod 86400000) / 86400000.0); + if TimeZone <> 0 then + ATime := IncHour(ATime, -TimeZone); + Inc(p); + SkipSpaceW(p); + Result := True + end; +end; + +function TQJson.ParseName(ABuilder: TQStringCatHelperW; var p: PQCharW) + : Integer; +begin + if StrictJson and (p^ <> '"') then + begin + Result := EParse_BadNameStart; + Exit; + end; + if not BuildJsonString(ABuilder, p) then + begin + Result := EParse_NameNotFound; + Exit; + end; + SkipSpaceAndComment(p); + if p^ <> ':' then + begin + Result := EParse_BadNameEnd; + Exit; + end; + ABuilder.TrimRight; + FName := ABuilder.Value; + + // + Inc(p); + SkipSpaceAndComment(p); + Result := 0; +end; + +procedure TQJson.ParseObject(var p: PQCharW); +var + ABuilder: TQStringCatHelperW; + ps: PQCharW; + AErrorCode: Integer; +begin + ABuilder := TQStringCatHelperW.Create; + try + ps := p; + try + SkipSpaceAndComment(p); + AErrorCode := ParseJsonPair(ABuilder, p); + if AErrorCode <> 0 then + RaiseParseException(AErrorCode, ps, p); + except + on E: Exception do + begin + if E is EJsonError then + raise + else + raise Exception.Create(Self.FormatParseError(EParse_Unknown, + E.Message, ps, p)); + end; + end; + finally + FreeObject(ABuilder); + DoParsed; + end; +end; + +procedure TQJson.ParseValue(ABuilder: TQStringCatHelperW; var p: PQCharW); +var + ps: PQCharW; +begin + ps := p; + RaiseParseException(TryParseValue(ABuilder, p), ps, p); +end; + +procedure TQJson.RaiseParseException(ACode: Integer; ps, p: PQCharW); +begin + if ACode <> 0 then + begin + case ACode of + EParse_BadStringStart: + raise EJsonError.Create(FormatParseError(ACode, + SBadStringStart, ps, p)); + EParse_BadJson: + raise EJsonError.Create(FormatParseError(ACode, SBadJson, ps, p)); + EParse_CommentNotSupport: + raise EJsonError.Create(FormatParseError(ACode, + SCommentNotSupport, ps, p)); + EParse_UnknownToken: + raise EJsonError.Create(FormatParseError(ACode, + SCommentNotSupport, ps, p)); + EParse_EndCharNeeded: + raise EJsonError.Create(FormatParseError(ACode, SEndCharNeeded, ps, p)); + EParse_BadNameStart: + raise EJsonError.Create(FormatParseError(ACode, SBadNameStart, ps, p)); + EParse_BadNameEnd: + raise EJsonError.Create(FormatParseError(ACode, SBadNameEnd, ps, p)); + EParse_NameNotFound: + raise EJsonError.Create(FormatParseError(ACode, SNameNotFound, ps, p)) + else + raise EJsonError.Create(FormatParseError(ACode, SUnknownError, ps, p)); + end; + end; +end; + +function TQJson.Remove(AItemIndex: Integer): TQJson; +begin + if FDataType in [jdtArray, jdtObject] then + begin + if (AItemIndex >= 0) and (AItemIndex < Count) then + begin + Result := Items[AItemIndex]; + FItems.Delete(AItemIndex); + Result.FParent := nil; + end + else + Result := nil; + end + else + Result := nil; +end; + +procedure TQJson.Remove(AJson: TQJson); +begin + Remove(AJson.ItemIndex); +end; + +procedure TQJson.Replace(AIndex: Integer; ANewItem: TQJson); +begin + FreeObject(Items[AIndex]); + FItems[AIndex] := ANewItem; +end; + +procedure TQJson.ResetNull; +begin + DataType := jdtNull; +end; + +procedure TQJson.SaveToFile(AFileName: String; AEncoding: TTextEncoding; + AWriteBOM, ADoFormat: Boolean); +var + AStream: TMemoryStream; +begin + AStream := TMemoryStream.Create; + try + SaveToStream(AStream, AEncoding, AWriteBOM, ADoFormat); + AStream.SaveToFile(AFileName); + finally + FreeObject(AStream); + end; +end; + +procedure TQJson.SaveToStream(AStream: TStream; AEncoding: TTextEncoding; + AWriteBOM, ADoFormat: Boolean); +var + S: QStringW; +begin + if DataType in [jdtArray, jdtObject] then + S := Encode(ADoFormat) + else + begin + if DataType in [jdtUnknown, jdtNull] then + begin + if Length(FName) = 0 then + S := '' + else + S := '{"' + Escape(FName) + '":' + Value + '}'; + end + else + begin + if Length(FName) > 0 then + S := '{"' + Escape(FName) + '":' + Encode(True) + '}' + else + raise Exception.Create(SNameNotFound); + end; + end; + if AEncoding = teUtf8 then + SaveTextU(AStream, qstring.UTF8Encode(S), AWriteBOM) + else if AEncoding = teAnsi then + SaveTextA(AStream, qstring.AnsiEncode(S)) + else if AEncoding = teUnicode16LE then + SaveTextW(AStream, S, AWriteBOM) + else + SaveTextWBE(AStream, S, AWriteBOM); +end; + +procedure TQJson.SetAsArray(const Value: QStringW); +var + p: PQCharW; +begin + DataType := jdtArray; + Clear; + p := PQCharW(Value); + ParseObject(p); +end; + +procedure TQJson.SetAsBoolean(const Value: Boolean); +begin + DataType := jdtBoolean; + PBoolean(FValue)^ := Value; +end; + +procedure TQJson.SetAsBytes(const Value: TBytes); +var + S: QStringW; +begin + if Assigned(OnQJsonEncodeBytes) then + OnQJsonEncodeBytes(Value, S) + else + S := BinToHex(Value, False); + AsString := S; +end; + +procedure TQJson.SetAsDateTime(const Value: TDateTime); +begin + DataType := jdtDateTime; + PExtended(FValue)^ := Value; +end; + +procedure TQJson.SetAsFloat(const Value: Extended); +begin + if IsNan(Value) or IsInfinite(Value) then + raise Exception.Create(SSupportFloat); + DataType := jdtFloat; + PExtended(FValue)^ := Value; +end; + +procedure TQJson.SetAsInt64(const Value: Int64); +begin + DataType := jdtInteger; + PInt64(FValue)^ := Value; +end; + +procedure TQJson.SetAsInteger(const Value: Integer); +begin + SetAsInt64(Value); +end; + +procedure TQJson.SetAsJson(const Value: QStringW); +var + ABuilder: TQStringCatHelperW; + p: PQCharW; +begin + ABuilder := TQStringCatHelperW.Create; + try + try + if DataType in [jdtArray, jdtObject] then + Clear; + p := PQCharW(Value); + ParseValue(ABuilder, p); + except + AsString := Value; + end; + finally + FreeObject(ABuilder); + end; +end; + +procedure TQJson.SetAsObject(const Value: QStringW); +begin + Parse(PQCharW(Value), Length(Value)); +end; + +procedure TQJson.SetAsString(const Value: QStringW); +begin + DataType := jdtString; + FValue := Value; +end; + +procedure TQJson.SetAsVariant(const Value: Variant); +var + I: Integer; + AType: TVarType; + procedure CastFromCustomVarType; + var + ATypeInfo: TCustomVariantType; + AData: TVarData; + begin + if FindCustomVariantType(AType, ATypeInfo) then + begin + VariantInit(AData); + // ȳת˫ֵУ͵ַ + try + try + ATypeInfo.CastTo(AData, FindVarData(Value)^, varDouble); + AsFloat := AData.VDouble; + except + AsString := Value; + end; + finally + VariantClear(AData); + end; + end + else + raise Exception.CreateFmt(SUnsupportVarType, [AType]); + end; + +begin + if VarIsArray(Value) then + begin + ArrayNeeded(jdtArray); + Clear; + for I := VarArrayLowBound(Value, VarArrayDimCount(Value)) + to VarArrayHighBound(Value, VarArrayDimCount(Value)) do + Add.AsVariant := Value[I]; + end + else + begin + AType := VarType(Value); + case AType of + varEmpty, varNull, varUnknown: + ResetNull; + varSmallInt, varInteger, varByte, varShortInt, varWord, + varLongWord, varInt64: + AsInt64 := Value; + varSingle, varDouble, varCurrency: + AsFloat := Value; + varDate: + AsDateTime := Value; + varOleStr, varString{$IFDEF UNICODE}, varUString{$ENDIF}: + AsString := Value; +{$IF RtlVersion>=26} + varUInt64: + AsInt64 := Value; + varRecord: + FromRtti(PVarRecord(@Value).RecInfo, PVarRecord(@Value).PRecord); +{$IFEND >=XE5} + varBoolean: + AsBoolean := Value + else + CastFromCustomVarType; + end; + end; +end; + +procedure TQJson.SetDataType(const Value: TQJsonDataType); +begin + if FDataType <> Value then + begin + if DataType in [jdtArray, jdtObject] then + begin + Clear; + if not(Value in [jdtArray, jdtObject]) then + begin + FreeObject(FItems); + end; + end; + case Value of + jdtNull, jdtUnknown, jdtString: + SetLength(FValue, 0); + jdtInteger: + begin + SetLength(FValue, SizeOf(Int64) shr 1); + PInt64(FValue)^ := 0; + end; + jdtFloat, jdtDateTime: + begin + SetLength(FValue, SizeOf(Extended) shr 1); + PExtended(FValue)^ := 0; + end; + jdtBoolean: + begin + SetLength(FValue, 1); + PBoolean(FValue)^ := False; + end; + jdtArray, jdtObject: + if not(FDataType in [jdtArray, jdtObject]) then + ArrayNeeded(Value); + end; + FDataType := Value; + end; +end; + +procedure TQJson.SetIgnoreCase(const Value: Boolean); + procedure InternalSetIgnoreCase(AParent: TQJson); + var + I: Integer; + begin + AParent.FIgnoreCase := Value; + if AParent.FNameHash <> 0 then + AParent.FNameHash := AParent.HashName(AParent.FName); + if AParent.DataType in [jdtArray, jdtObject] then + begin + for I := 0 to AParent.Count - 1 do + InternalSetIgnoreCase(AParent[I]); + end; + end; + +begin + if FIgnoreCase <> Value then + begin + InternalSetIgnoreCase(Root); + end; +end; + +procedure TQJson.SetName(const Value: QStringW); +begin + if FName <> Value then + begin + if Assigned(FParent) then + begin + if FParent.IndexOf(Value) <> -1 then + raise Exception.CreateFmt(SNodeNameExists, [Value]); + end; + FName := Value; + DoJsonNameChanged(Self); + end; +end; + +procedure TQJson.SetValue(const Value: QStringW); +var + p: PQCharW; + procedure ParseNum; + var + ANum: Extended; + begin + if ParseNumeric(p, ANum) then + begin + if SameValue(ANum, Trunc(ANum), 5E-324) then + AsInt64 := Trunc(ANum) + else + AsFloat := ANum; + end + else + raise Exception.Create(Format(SBadNumeric, [Value])); + end; + procedure SetDateTime; + var + ATime: TDateTime; + begin + if ParseDateTime(PQCharW(Value), ATime) then + AsDateTime := ATime + else if ParseJsonTime(PQCharW(Value), ATime) then + AsDateTime := ATime + else + raise Exception.Create(SBadJsonTime); + end; + procedure DetectValue; + var + ABuilder: TQStringCatHelperW; + p: PQCharW; + begin + ABuilder := TQStringCatHelperW.Create; + try + p := PQCharW(Value); + ParseValue(ABuilder, p); + except + AsString := Value; + end; + FreeObject(ABuilder); + end; + +begin + if DataType = jdtString then + FValue := Value + else if DataType = jdtBoolean then + AsBoolean := StrToBool(Value) + else + begin + p := PQCharW(Value); + if DataType in [jdtInteger, jdtFloat] then + ParseNum + else if DataType = jdtDateTime then + SetDateTime + else if DataType in [jdtArray, jdtObject] then + begin + Clear; + ParseObject(p); + end + else // jdtUnknown + DetectValue; + end; +end; + +class function TQJson.SkipSpaceAndComment(var p: PQCharW): Integer; +begin + SkipSpaceW(p); + Result := 0; + if not StrictJson then + begin + while p^ = '/' do + begin + if StrictJson then + begin + Result := EParse_CommentNotSupport; + Exit; + end; + if p[1] = '/' then + begin + SkipUntilW(p, [WideChar(10)]); + SkipSpaceW(p); + end + else if p[1] = '*' then + begin + Inc(p, 2); + while p^ <> #0 do + begin + if (p[0] = '*') and (p[1] = '/') then + begin + Inc(p, 2); + SkipSpaceW(p); + Break; + end + else + Inc(p); + end; + end + else + begin + Result := EParse_UnknownToken; + Exit; + end; + end; + end; +end; + +procedure TQJson.StreamFromValue(AStream: TStream); +var + ABytes: TBytes; +begin + ABytes := AsBytes; + AStream.WriteBuffer(ABytes[0], Length(ABytes)); +end; + +{$IF RTLVersion>=21} + +function TQJson.Invoke(AInstance: TValue): TValue; +var + AMethods: TArray; + AParams: TArray; + AMethod: TRttiMethod; + AType: TRttiType; + AContext: TRttiContext; + AParamValues: array of TValue; + I, c: Integer; + AParamItem: TQJson; +begin + AContext := TRttiContext.Create; + Result := TValue.Empty; + if AInstance.IsObject then + AType := AContext.GetType(AInstance.AsObject.ClassInfo) + else if AInstance.IsClass then + AType := AContext.GetType(AInstance.AsClass) + else if AInstance.Kind = tkRecord then + AType := AContext.GetType(AInstance.TypeInfo) + else + AType := AContext.GetType(AInstance.TypeInfo); + AMethods := AType.GetMethods(Name); + c := Count; + for AMethod in AMethods do + begin + AParams := AMethod.GetParameters; + if Length(AParams) = c then + begin + SetLength(AParamValues, c); + for I := 0 to c - 1 do + begin + AParamItem := ItemByName(AParams[I].Name); + if AParamItem <> nil then + AParamValues[I] := AParamItem.ToRttiValue + else + raise Exception.CreateFmt(SParamMissed, [AParams[I].Name]); + end; + Result := AMethod.Invoke(AInstance, AParamValues); + Exit; + end; + end; + raise Exception.CreateFmt(SMethodMissed, [Name]); +end; + +procedure TQJson.ToRecord(var ARecord: T); +begin + ToRtti(@ARecord, TypeInfo(T)); +end; + +procedure TQJson.ToRtti(AInstance: TValue); +begin + if AInstance.IsEmpty then + Exit; + if AInstance.Kind = tkRecord then + ToRtti(AInstance.GetReferenceToRawData, AInstance.TypeInfo) + else if AInstance.Kind = tkClass then + ToRtti(AInstance.AsObject, AInstance.TypeInfo) +end; + +procedure TQJson.ToRtti(ADest: Pointer; AType: PTypeInfo); + + procedure LoadCollection(AJson: TQJson; ACollection: TCollection); + var + I: Integer; + begin + for I := 0 to AJson.Count - 1 do + AJson[I].ToRtti(ACollection.Add); + end; + procedure ToRecord; + var + AContext: TRttiContext; + AFields: TArray; + ARttiType: TRttiType; + ABaseAddr: Pointer; + J: Integer; + AChild: TQJson; + AObj: TObject; + begin + AContext := TRttiContext.Create; + ARttiType := AContext.GetType(AType); + ABaseAddr := ADest; + AFields := ARttiType.GetFields; + for J := Low(AFields) to High(AFields) do + begin + if AFields[J].FieldType <> nil then + begin + AChild := ItemByName(AFields[J].Name); + if AChild <> nil then + begin + case AFields[J].FieldType.TypeKind of + tkInteger: + AFields[J].SetValue(ABaseAddr, AChild.AsInteger); +{$IFNDEF NEXTGEN} + tkString: + PShortString(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + ShortString(AChild.AsString); +{$ENDIF !NEXTGEN} + tkUString{$IFNDEF NEXTGEN}, tkLString, tkWString{$ENDIF !NEXTGEN}: + AFields[J].SetValue(ABaseAddr, AChild.AsString); + tkEnumeration: + begin + if GetTypeData(AFields[J].FieldType.Handle) + ^.BaseType^ = TypeInfo(Boolean) then + AFields[J].SetValue(ABaseAddr, AChild.AsBoolean) + else + begin + case GetTypeData(AFields[J].FieldType.Handle).OrdType of + otSByte: + begin + if AChild.DataType = jdtInteger then + PShortint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PShortint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + GetEnumValue(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otUByte: + begin + if AChild.DataType = jdtInteger then + PByte(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PByte(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + GetEnumValue(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otSWord: + begin + if AChild.DataType = jdtInteger then + PSmallint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PSmallint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + GetEnumValue(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otUWord: + begin + if AChild.DataType = jdtInteger then + PWord(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PWord(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + GetEnumValue(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otSLong: + begin + if AChild.DataType = jdtInteger then + PInteger(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PInteger(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + GetEnumValue(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otULong: + begin + if AChild.DataType = jdtInteger then + PCardinal(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PCardinal(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + GetEnumValue(AFields[J].FieldType.Handle, + AChild.AsString); + end; + end; + end; + end; + tkSet: + begin + case GetTypeData(AFields[J].FieldType.Handle).OrdType of + otSByte: + begin + if AChild.DataType = jdtInteger then + PShortint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PShortint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + StringToSet(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otUByte: + begin + if AChild.DataType = jdtInteger then + PByte(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PByte(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + StringToSet(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otSWord: + begin + if AChild.DataType = jdtInteger then + PSmallint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PSmallint(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + StringToSet(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otUWord: + begin + if AChild.DataType = jdtInteger then + PWord(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PWord(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + StringToSet(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otSLong: + begin + if AChild.DataType = jdtInteger then + PInteger(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PInteger(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + StringToSet(AFields[J].FieldType.Handle, + AChild.AsString); + end; + otULong: + begin + if AChild.DataType = jdtInteger then + PCardinal(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsInteger + else + PCardinal(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + StringToSet(AFields[J].FieldType.Handle, + AChild.AsString); + end; + end; + end; + tkChar, tkWChar: + AFields[J].SetValue(ABaseAddr, AChild.AsString); + tkFloat: + if (AFields[J].FieldType.Handle = TypeInfo(TDateTime)) or + (AFields[J].FieldType.Handle = TypeInfo(TTime)) or + (AFields[J].FieldType.Handle = TypeInfo(TDate)) then + begin + if AChild.IsDateTime then + AFields[J].SetValue(ABaseAddr, AChild.AsDateTime) + else if AChild.DataType in [jdtNull, jdtUnknown] then + AFields[J].SetValue(ABaseAddr, 0) + else + raise Exception.CreateFmt(SBadConvert, + [AChild.AsString, JsonTypeName[AChild.DataType]]); + end + else + AFields[J].SetValue(ABaseAddr, AChild.AsFloat); + tkInt64: + AFields[J].SetValue(ABaseAddr, AChild.AsInt64); + tkVariant: + PVariant(IntPtr(ABaseAddr) + AFields[J].Offset)^ := + AChild.AsVariant; + tkArray, tkDynArray: + AChild.ToRtti(Pointer(IntPtr(ABaseAddr) + AFields[J].Offset), + AFields[J].FieldType.Handle); + tkClass: + begin + AObj := AFields[J].GetValue(ABaseAddr).AsObject; + if AObj is TStrings then + (AObj as TStrings).Text := AChild.AsString + else if AObj is TCollection then + LoadCollection(AChild, AObj as TCollection) + else + AChild.ToRtti(AObj); + end; + tkRecord: + AChild.ToRtti(Pointer(IntPtr(ABaseAddr) + AFields[J].Offset), + AFields[J].FieldType.Handle); + end; + end; + end; + end; + end; + + procedure ToObject; + var + AProp: PPropInfo; + ACount: Integer; + J: Integer; + AObj, AChildObj: TObject; + AChild: TQJson; + begin + AObj := ADest; + ACount := Count; + if AObj is TStrings then + (AObj as TStrings).Text := AsString + else if AObj is TCollection then + LoadCollection(Self, AObj as TCollection) + else + begin + for J := 0 to ACount - 1 do + begin + AChild := Items[J]; + AProp := GetPropInfo(AObj, AChild.Name); + if AProp <> nil then + begin + case AProp.PropType^.Kind of + tkClass: + begin + AChildObj := Pointer(GetOrdProp(AObj, AProp)); + if AChildObj is TStrings then + (AChildObj as TStrings).Text := AChild.AsString + else if AChildObj is TCollection then + LoadCollection(AChild, AChildObj as TCollection) + else + AChild.ToRtti(AChildObj); + end; + tkRecord, tkArray, tkDynArray: + // tkArray,tkDynArray͵û,tkRecord + begin + AChild.ToRtti(Pointer(GetOrdProp(AObj, AProp)), + AProp.PropType^); + end; + tkInteger: + SetOrdProp(AObj, AProp, AChild.AsInteger); + tkFloat: + begin + if (AProp.PropType^ = TypeInfo(TDateTime)) or + (AProp.PropType^ = TypeInfo(TTime)) or + (AProp.PropType^ = TypeInfo(TDate)) then + SetFloatProp(AObj, AProp, AChild.AsDateTime) + else + SetFloatProp(AObj, AProp, AChild.AsFloat); + end; + tkChar, tkString, tkWChar, tkLString, tkWString, tkUString: + SetStrProp(AObj, AProp, AChild.AsString); + tkEnumeration: + begin + if GetTypeData(AProp.PropType^)^.BaseType^ = TypeInfo(Boolean) + then + SetOrdProp(AObj, AProp, Integer(AChild.AsBoolean)) + else if AChild.DataType = jdtInteger then + SetOrdProp(AObj, AProp, AChild.AsInteger) + else + SetEnumProp(AObj, AProp, AChild.AsString); + end; + tkSet: + begin + if AChild.DataType = jdtInteger then + SetOrdProp(AObj, AProp, AChild.AsInteger) + else + SetSetProp(AObj, AProp, AChild.AsString); + end; + tkVariant: + SetVariantProp(AObj, AProp, AChild.AsVariant); + tkInt64: + SetInt64Prop(AObj, AProp, AChild.AsInt64); + end; + end; + end; + end; + end; + + procedure SetDynArrayLen(arr: Pointer; AType: PTypeInfo; ALen: NativeInt); + var + pmem: Pointer; + begin + pmem := PPointer(arr)^; + DynArraySetLength(pmem, AType, 1, @ALen); + PPointer(arr)^ := pmem; + end; + + procedure ToArray; + var + AContext: TRttiContext; + ASubType: TRttiType; + I, l, AOffset: Integer; + S: QStringW; + pd, pi: PByte; + AChildObj: TObject; + ASubTypeInfo: PTypeInfo; + AChild: TQJson; + begin + AContext := TRttiContext.Create; +{$IF RTLVersion>25} + S := ArrayItemTypeName(AType.NameFld.ToString); +{$ELSE} + S := ArrayItemTypeName(String(AType.Name)); +{$IFEND} + if Length(S) > 0 then + ASubType := AContext.FindType(S) + else + ASubType := nil; + if ASubType <> nil then + begin + ASubTypeInfo := ASubType.Handle; + l := Count; + SetDynArrayLen(ADest, AType, l); + pd := PPointer(ADest)^; + for I := 0 to l - 1 do + begin + AOffset := I * GetTypeData(AType).elSize; + pi := Pointer(IntPtr(pd) + AOffset); + AChild := Items[I]; + case ASubType.TypeKind of + tkInteger: + begin + case GetTypeData(ASubTypeInfo).OrdType of + otSByte: + PShortint(pi)^ := AChild.AsInteger; + otUByte: + pi^ := Items[I].AsInteger; + otSWord: + PSmallint(pi)^ := AChild.AsInteger; + otUWord: + PWord(pi)^ := AChild.AsInteger; + otSLong: + PInteger(pi)^ := AChild.AsInteger; + otULong: + PCardinal(pi)^ := AChild.AsInteger; + end; + end; +{$IFNDEF NEXTGEN} + tkChar: + pi^ := Ord(PAnsiChar(AnsiString(AChild.AsString))[0]); +{$ENDIF !NEXTGEN} + tkEnumeration: + begin + if GetTypeData(ASubTypeInfo)^.BaseType^ = TypeInfo(Boolean) then + PBoolean(pi)^ := AChild.AsBoolean + else + begin + case GetTypeData(ASubTypeInfo)^.OrdType of + otSByte: + begin + if AChild.DataType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := GetEnumValue(ASubTypeInfo, + AChild.AsString); + end; + otUByte: + begin + if AChild.DataType = jdtInteger then + pi^ := AChild.AsInteger + else + pi^ := GetEnumValue(ASubTypeInfo, AChild.AsString); + end; + otSWord: + begin + if AChild.DataType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := GetEnumValue(ASubTypeInfo, + AChild.AsString); + end; + otUWord: + begin + if AChild.DataType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := GetEnumValue(ASubTypeInfo, + AChild.AsString); + end; + otSLong: + begin + if AChild.DataType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := GetEnumValue(ASubTypeInfo, + AChild.AsString); + end; + otULong: + begin + if AChild.DataType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := GetEnumValue(ASubTypeInfo, + Items[I].AsString); + end; + end; + end; + end; + tkFloat: + case GetTypeData(ASubTypeInfo)^.FloatType of + ftSingle: + PSingle(pi)^ := Items[I].AsFloat; + ftDouble: + PDouble(pi)^ := Items[I].AsFloat; + ftExtended: + PExtended(pi)^ := Items[I].AsFloat; + ftComp: + PComp(pi)^ := Items[I].AsFloat; + ftCurr: + PCurrency(pi)^ := Items[I].AsFloat; + end; +{$IFNDEF NEXTGEN} + tkString: + PShortString(pi)^ := ShortString(Items[I].AsString); +{$ENDIF !NEXTGEN} + tkSet: + begin + case GetTypeData(ASubTypeInfo)^.OrdType of + otSByte: + begin + if AChild.DataType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := StringToSet(ASubTypeInfo, + AChild.AsString); + end; + otUByte: + begin + if AChild.DataType = jdtInteger then + pi^ := AChild.AsInteger + else + pi^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + otSWord: + begin + if AChild.DataType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := StringToSet(ASubTypeInfo, + AChild.AsString); + end; + otUWord: + begin + if AChild.DataType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + otSLong: + begin + if AChild.DataType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := StringToSet(ASubTypeInfo, + AChild.AsString); + end; + otULong: + begin + if AChild.DataType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := StringToSet(ASubTypeInfo, + Items[I].AsString); + end; + end; + end; + tkClass: + begin + if PPointer(pi)^ <> nil then + begin + AChildObj := PPointer(pi)^; + if AChildObj is TStrings then + (AChildObj as TStrings).Text := Items[I].AsString + else if AChildObj is TCollection then + LoadCollection(Items[I], AChildObj as TCollection) + else + Items[I].ToRtti(AChildObj); + end; + end; + tkWChar: + PWideChar(pi)^ := PWideChar(Items[I].AsString)[0]; +{$IFNDEF NEXTGEN} + tkLString: + PAnsiString(pi)^ := AnsiString(Items[I].AsString); + tkWString: + PWideString(pi)^ := Items[I].AsString; +{$ENDIF} + tkVariant: + PVariant(pi)^ := Items[I].AsVariant; + tkArray, tkDynArray: + Items[I].ToRtti(pi, ASubTypeInfo); + tkRecord: + Items[I].ToRtti(pi, ASubTypeInfo); + tkInt64: + PInt64(pi)^ := Items[I].AsInt64; + tkUString: + PUnicodeString(pi)^ := Items[I].AsString; + end; + end; + end + else + raise Exception.CreateFmt(SMissRttiTypeDefine, [AType.Name]); + end; + function GetFixedArrayItemType: PTypeInfo; + var + pType: PPTypeInfo; + begin + pType := GetTypeData(AType)^.ArrayData.elType; + if pType = nil then + Result := nil + else + Result := pType^; + end; + procedure ToFixedArray; + var + I, c, ASize: Integer; + ASubType: PTypeInfo; + AChild: TQJson; + AChildObj: TObject; + pi: Pointer; + begin + c := GetTypeData(AType).ArrayData.ElCount; + ASubType := GetFixedArrayItemType; + if ASubType = nil then + Exit; + ASize := GetTypeData(ASubType).elSize; + for I := 0 to c - 1 do + begin + pi := Pointer(IntPtr(ADest) + ASize * I); + AChild := Items[I]; + case ASubType.Kind of + tkInteger: + begin + case GetTypeData(ASubType).OrdType of + otSByte: + PShortint(pi)^ := AChild.AsInteger; + otUByte: + PByte(pi)^ := AChild.AsInteger; + otSWord: + PSmallint(pi)^ := AChild.AsInteger; + otUWord: + PWord(pi)^ := AChild.AsInteger; + otSLong: + PInteger(pi)^ := AChild.AsInteger; + otULong: + PCardinal(pi)^ := AChild.AsInteger; + end; + end; +{$IFNDEF NEXTGEN} + tkChar: + PByte(pi)^ := Ord(PAnsiChar(AnsiString(AChild.AsString))[0]); +{$ENDIF !NEXTGEN} + tkEnumeration: + begin + if GetTypeData(ASubType)^.BaseType^ = TypeInfo(Boolean) then + PBoolean(pi)^ := AChild.AsBoolean + else + begin + case GetTypeData(ASubType)^.OrdType of + otSByte: + begin + if AChild.DataType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otUByte: + begin + if AChild.DataType = jdtInteger then + PByte(pi)^ := AChild.AsInteger + else + PByte(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otSWord: + begin + if AChild.DataType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otUWord: + begin + if AChild.DataType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otSLong: + begin + if AChild.DataType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otULong: + begin + if AChild.DataType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := + GetEnumValue(ASubType, Items[I].AsString); + end; + end; + end; + end; + tkFloat: + case GetTypeData(ASubType)^.FloatType of + ftSingle: + PSingle(pi)^ := Items[I].AsFloat; + ftDouble: + PDouble(pi)^ := Items[I].AsFloat; + ftExtended: + PExtended(pi)^ := Items[I].AsFloat; + ftComp: + PComp(pi)^ := Items[I].AsFloat; + ftCurr: + PCurrency(pi)^ := Items[I].AsFloat; + end; +{$IFNDEF NEXTGEN} + tkString: + PShortString(pi)^ := ShortString(Items[I].AsString); +{$ENDIF !NEXTGEN} + tkSet: + begin + case GetTypeData(ASubType)^.OrdType of + otSByte: + begin + if AChild.DataType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otUByte: + begin + if AChild.DataType = jdtInteger then + PByte(pi)^ := AChild.AsInteger + else + PByte(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otSWord: + begin + if AChild.DataType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otUWord: + begin + if AChild.DataType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otSLong: + begin + if AChild.DataType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otULong: + begin + if AChild.DataType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := StringToSet(ASubType, Items[I].AsString); + end; + end; + end; + tkClass: + begin + if PPointer(pi)^ <> nil then + begin + AChildObj := PPointer(pi)^; + if AChildObj is TStrings then + (AChildObj as TStrings).Text := Items[I].AsString + else if AChildObj is TCollection then + LoadCollection(Items[I], AChildObj as TCollection) + else + Items[I].ToRtti(AChildObj); + end; + end; + tkWChar: + PWideChar(pi)^ := PWideChar(Items[I].AsString)[0]; +{$IFNDEF NEXTGEN} + tkLString: + PAnsiString(pi)^ := AnsiString(Items[I].AsString); + tkWString: + PWideString(pi)^ := Items[I].AsString; +{$ENDIF} + tkVariant: + PVariant(pi)^ := Items[I].AsVariant; + tkArray, tkDynArray: + Items[I].ToRtti(pi, ASubType); + tkRecord: + Items[I].ToRtti(pi, ASubType); + tkInt64: + PInt64(pi)^ := Items[I].AsInt64; + tkUString: + PUnicodeString(pi)^ := Items[I].AsString; + end; + end; + end; + +begin + if ADest <> nil then + begin + if AType.Kind = tkRecord then + ToRecord + else if AType.Kind = tkClass then + ToObject + else if AType.Kind = tkDynArray then + ToArray + else if AType.Kind = tkArray then + ToFixedArray + else + raise Exception.Create(SUnsupportPropertyType); + end; +end; + +function TQJson.ToRttiValue: TValue; + procedure AsDynValueArray; + var + AValues: array of TValue; + I: Integer; + begin + SetLength(AValues, Count); + for I := 0 to Count - 1 do + AValues[I] := Items[I].ToRttiValue; + Result := TValue.FromArray(TypeInfo(TValueArray), AValues); + end; + +begin + case DataType of + jdtString: + Result := AsString; + jdtInteger: + Result := AsInt64; + jdtFloat: + Result := AsFloat; + jdtDateTime: + Result := AsDateTime; + jdtBoolean: + Result := AsBoolean; + jdtArray, jdtObject: // Ͷֻܵ + AsDynValueArray + else + Result := TValue.Empty; + end; +end; +{$IFEND >=2010} + +function TQJson.ToString: string; +begin + Result := AsString; +end; + +function TQJson.TryParse(p: PWideChar; l: Integer): Boolean; + + procedure DoTry; + var + ABuilder: TQStringCatHelperW; + begin + ABuilder := TQStringCatHelperW.Create; + try + try + SkipSpaceW(p); + Result := ParseJsonPair(ABuilder, p) = 0; + except + on E: Exception do + Result := False; + end; + finally + FreeObject(ABuilder); + end; + end; + + procedure ParseCopy; + var + S: QStringW; + begin + S := StrDupW(p, 0, l); + p := PQCharW(S); + DoTry; + end; + +begin + if DataType in [jdtObject, jdtArray] then + Clear; + if (l > 0) and (p[l] <> #0) then + ParseCopy + else + DoTry; +end; + +function TQJson.TryParse(const S: QStringW): Boolean; +begin + Result := TryParse(PQCharW(S), Length(S)); +end; + +function TQJson.TryParseValue(ABuilder: TQStringCatHelperW; + var p: PQCharW): Integer; +var + ANum: Extended; +const + JsonEndChars: PWideChar = ',]}'; +begin + Result := 0; + if p^ = '"' then + begin + BuildJsonString(ABuilder, p); + AsString := ABuilder.Value; + end + else if p^ = '''' then + begin + if StrictJson then + Result := EParse_BadStringStart; + BuildJsonString(ABuilder, p); + AsString := ABuilder.Value; + end + else if ParseNumeric(p, ANum) then // ֣ + begin + SkipSpaceAndComment(p); + if (p^ = #0) or CharInW(p, JsonEndChars) then + begin + if SameValue(ANum, Trunc(ANum), 5E-324) then + AsInt64 := Trunc(ANum) + else + AsFloat := ANum; + end + else + Result := EParse_BadJson; + end + else if StartWithW(p, 'False', True) then // False + begin + Inc(p, 5); + SkipSpaceAndComment(p); + AsBoolean := False + end + else if StartWithW(p, 'True', True) then // True + begin + Inc(p, 4); + SkipSpaceAndComment(p); + AsBoolean := True; + end + else if StartWithW(p, 'NULL', True) then // Null + begin + Inc(p, 4); + SkipSpaceAndComment(p); + ResetNull; + end + else if (p^ = '[') or (p^ = '{') then + Result := ParseJsonPair(ABuilder, p) + else + Result := 2; +end; + +procedure TQJson.ValidArray; +begin + if DataType in [jdtArray, jdtObject] then +{$IFDEF UNICODE} + FItems := TList.Create +{$ELSE} + FItems := TList.Create +{$ENDIF} + else + raise Exception.Create(Format(SVarNotArray, [FName])); +end; + +function TQJson.ValueByName(AName, ADefVal: QStringW): QStringW; +var + AChild: TQJson; +begin + AChild := ItemByName(AName); + if Assigned(AChild) then + Result := AChild.Value + else + Result := ADefVal; +end; + +function TQJson.ValueByPath(APath, ADefVal: QStringW): QStringW; +var + AItem: TQJson; +begin + AItem := ItemByPath(APath); + if Assigned(AItem) then + Result := AItem.Value + else + Result := ADefVal; +end; + +procedure TQJson.ValueFromFile(AFileName: QStringW); +var + AStream: TFileStream; +begin + AStream := TFileStream.Create(AFileName, fmOpenRead); + try + ValueFromStream(AStream, 0); + finally + FreeObject(AStream); + end; +end; + +procedure TQJson.ValueFromStream(AStream: TStream; ACount: Cardinal); +var + ABytes: TBytes; +begin + if ACount = 0 then + begin + AStream.Position := 0; + ACount := AStream.Size; + end + else if AStream.Position + ACount > AStream.Size then + ACount := AStream.Size - AStream.Position; + SetLength(ABytes, ACount); + AStream.ReadBuffer(ABytes[0], ACount); + AsBytes := ABytes; +end; + +procedure TQJson.Delete; +begin + if Assigned(FParent) then + FParent.Delete(ItemIndex) + else + FreeObject(Self); +end; + +{ TQJsonEnumerator } + +constructor TQJsonEnumerator.Create(AList: TQJson); +begin + inherited Create; + FList := AList; + FIndex := -1; +end; + +function TQJsonEnumerator.GetCurrent: TQJson; +begin + Result := FList[FIndex]; +end; + +function TQJsonEnumerator.MoveNext: Boolean; +begin + if FIndex < FList.Count - 1 then + begin + Inc(FIndex); + Result := True; + end + else + Result := False; +end; + +{ TQHashedJson } + +procedure TQHashedJson.Assign(ANode: TQJson); +begin + inherited; + if (Length(FName) > 0) then + begin + if FNameHash = 0 then + FNameHash := HashOf(PQCharW(FName), Length(FName) shl 1); + if Assigned(Parent) then + TQHashedJson(Parent).FHashTable.Add(Pointer(Parent.Count - 1), FNameHash); + end; +end; + +procedure TQHashedJson.Clear; +begin + inherited; + FHashTable.Clear; +end; + +constructor TQHashedJson.Create; +begin + inherited; + FHashTable := TQHashTable.Create(); + FHashTable.AutoSize := True; +end; + +function TQHashedJson.CreateJson: TQJson; +begin + if Assigned(OnQJsonCreate) then + Result := OnQJsonCreate + else + Result := TQHashedJson.Create; +end; + +destructor TQHashedJson.Destroy; +begin + FreeObject(FHashTable); + inherited; +end; + +procedure TQHashedJson.DoJsonNameChanged(AJson: TQJson); + procedure Rehash; + var + AIndex: Integer; + AHash: TQHashType; + AList: PQHashList; + AItem: TQJson; + begin + AHash := HashName(AJson.Name); + if AHash <> AJson.FNameHash then + begin + AList := FHashTable.FindFirst(AJson.FNameHash); + while AList <> nil do + begin + AIndex := Integer(AList.Data); + AItem := Items[AIndex]; + if AItem = AJson then + begin + FHashTable.ChangeHash(Pointer(AIndex), AJson.FNameHash, AHash); + AJson.FNameHash := AHash; + Break; + end + else + AList := FHashTable.FindNext(AList); + end; + end; + end; + +begin + if AJson.FNameHash = 0 then + begin + AJson.FNameHash := HashName(AJson.Name); + if Assigned(AJson.Parent) then + begin + TQHashedJson(AJson.Parent).FHashTable.Add(Pointer(Count - 1), + AJson.FNameHash); + end; + end + else + Rehash; +end; + +function TQHashedJson.IndexOf(const AName: QStringW): Integer; +var + AIndex, AHash: Integer; + AList: PQHashList; + AItem: TQJson; +begin + AHash := HashName(AName); + AList := FHashTable.FindFirst(AHash); + Result := -1; + while AList <> nil do + begin + AIndex := Integer(AList.Data); + AItem := Items[AIndex]; + if AItem.Name = AName then + begin + Result := AIndex; + Break; + end + else + AList := FHashTable.FindNext(AList); + end; +end; + +procedure TQHashedJson.DoParsed; +var + I: Integer; + AJson: TQJson; +begin + FHashTable.Resize(Count); + for I := 0 to Count - 1 do + begin + AJson := Items[I]; + if Length(AJson.FName) > 0 then + begin + if AJson.FNameHash = 0 then + AJson.FNameHash := HashName(AJson.FName); + FHashTable.Add(Pointer(I), AJson.FNameHash); + end; + if AJson.Count > 0 then + AJson.DoParsed; + end; +end; + +function TQHashedJson.Remove(AIndex: Integer): TQJson; +begin + Result := inherited Remove(AIndex); + if Assigned(Result) then + FHashTable.Delete(Pointer(AIndex), Result.NameHash); +end; + +procedure TQHashedJson.Replace(AIndex: Integer; ANewItem: TQJson); +var + AOld: TQJson; +begin + if not(ANewItem is TQHashedJson) then + raise Exception.CreateFmt(SReplaceTypeNeed, ['TQHashedJson']); + AOld := Items[AIndex]; + FHashTable.Delete(Pointer(AIndex), AOld.NameHash); + inherited; + if Length(ANewItem.FName) > 0 then + FHashTable.Add(Pointer(AIndex), ANewItem.FNameHash); +end; + +procedure DoEncodeAsBase64(const ABytes: TBytes; var AResult: QStringW); +{$IFNDEF UNICODE} + function EncodeBase64(const V: Pointer; len: Integer): QStringW; + var + AIn, AOut: TMemoryStream; + T: QStringA; + begin + AIn := TMemoryStream.Create; + AOut := TMemoryStream.Create; + try + AIn.WriteBuffer(V^, len); + AIn.Position := 0; + EncodeStream(AIn, AOut); + T.Length := AOut.Size; + Move(AOut.Memory^, PQCharA(T)^, AOut.Size); + Result := qstring.Utf8Decode(T); + finally + FreeObject(AIn); + FreeObject(AOut); + end; + end; +{$ENDIF} + +begin + if Length(ABytes) > 0 then + AResult := QStringW(EncodeBase64(@ABytes[0], Length(ABytes))) + else + SetLength(AResult, 0); +end; + +procedure DoDecodeAsBase64(const S: QStringW; var AResult: TBytes); +{$IFNDEF UNICODE} + function DecodeBase64(const S: QStringW): TBytes; + var + AIn, AOut: TMemoryStream; + T: QStringA; + begin + AIn := TMemoryStream.Create; + AOut := TMemoryStream.Create; + try + T := qstring.UTF8Encode(S); + AIn.WriteBuffer(PQCharA(T)^, T.Length); + AIn.Position := 0; + DecodeStream(AIn, AOut); + SetLength(Result, AOut.Size); + Move(AOut.Memory^, Result[0], AOut.Size); + finally + FreeObject(AIn); + FreeObject(AOut); + end; + end; +{$ENDIF} + +begin + if Length(S) > 0 then + AResult := DecodeBase64({$IFNDEF NEXTGEN}AnsiString(S){$ELSE}S{$ENDIF}) + else + SetLength(AResult, 0); +end; + +procedure EncodeJsonBinaryAsBase64; +begin + OnQJsonEncodeBytes := DoEncodeAsBase64; + OnQJsonDecodeBytes := DoDecodeAsBase64; +end; + +procedure EncodeJsonBinaryAsHex; +begin + OnQJsonEncodeBytes := nil; + OnQJsonDecodeBytes := nil; +end; + +{ TQJsonStreamHelper } + +procedure TQJsonStreamHelper.BeginArray; +begin + InternalWriteString('[', False); +end; + +procedure TQJsonStreamHelper.BeginObject; +begin + InternalWriteString('{', False); +end; + +procedure TQJsonStreamHelper.BeginObject(const AName: QStringW); +begin + InternalWriteString('"' + TQJson.JsonEscape(AName, FDoEscape) + '":{', False); +end; + +procedure TQJsonStreamHelper.BeginWrite(AStream: TStream; + AEncoding: TTextEncoding; ADoEscape: Boolean); +begin + FStream := AStream; + FEncoding := AEncoding; + FDoEscape := ADoEscape; +end; + +procedure TQJsonStreamHelper.EndArray; +begin + case FEncoding of + teUnicode16LE, teUnicode16BE: + FStream.Seek(-2, soCurrent); + else + FStream.Seek(-1, soCurrent); + end; + InternalWriteString(']'); +end; + +procedure TQJsonStreamHelper.EndObject; +begin + case FEncoding of + teUnicode16LE, teUnicode16BE: + FStream.Seek(-2, soCurrent); + else + FStream.Seek(-1, soCurrent); + end; + InternalWriteString('}'); +end; + +procedure TQJsonStreamHelper.EndWrite; +begin + if not IsEmpty then + begin + if FStream.Size > FStream.Position then + FStream.Seek(-1, soCurrent) + else + FStream.Size := FStream.Size - 1; + end; +end; + +procedure TQJsonStreamHelper.InternalWriteString(S: QStringW; + ADoAppend: Boolean); + procedure AnsiWrite; + var + T: QStringA; + begin + T := AnsiEncode(S); + FStream.WriteBuffer(T.Data^, T.Length); + end; + procedure Utf8Write; + var + T: QStringA; + begin + T := qstring.UTF8Encode(S); + FStream.WriteBuffer(T.Data^, T.Length); + end; + procedure BEWrite; + var + T: QStringW; + begin + T := StrDupX(PQCharW(S), Length(S)); + ExchangeByteOrder(PQCharA(T), Length(T) shl 1); + FStream.WriteBuffer(PQCharW(T)^, Length(T) shl 1); + end; + +begin + FIsEmpty := False; + if ADoAppend then + S := S + ','; + case FEncoding of + teAnsi: + AnsiWrite; + teUnicode16LE: + FStream.Write(PQCharW(S)^, Length(S) shl 1); + teUnicode16BE: + BEWrite; + else + Utf8Write; + end; +end; + +procedure TQJsonStreamHelper.Write(const S: QStringW); +begin + InternalWriteString('"' + TQJson.JsonEscape(S, FDoEscape) + '"'); +end; + +procedure TQJsonStreamHelper.Write(const ABytes: TBytes); +var + S: QStringW; +begin + if Assigned(OnQJsonEncodeBytes) then + OnQJsonEncodeBytes(ABytes, S) + else + S := qstring.BinToHex(ABytes); + InternalWriteString('"' + S + '"'); +end; + +procedure TQJsonStreamHelper.Write(const p: PByte; l: Integer); +var + ATemp: TBytes; +begin + SetLength(ATemp, l); + Move(p^, ATemp[0], l); + Write(ATemp); +end; + +procedure TQJsonStreamHelper.Write(const b: Boolean); +begin + if b then + InternalWriteString('true') + else + InternalWriteString('false'); +end; + +procedure TQJsonStreamHelper.Write(const I: Int64); +begin + InternalWriteString(IntToStr(I)); +end; + +procedure TQJsonStreamHelper.Write(const D: Double); +begin + InternalWriteString(FloatToStr(D)); +end; + +procedure TQJsonStreamHelper.Write(const c: Currency); +begin + InternalWriteString(CurrToStr(c)); +end; + +procedure TQJsonStreamHelper.WriteDateTime(const V: TDateTime); + function JsonDateTime: QStringW; + var + MS: Int64; // ʱϢ + begin + MS := Trunc(V * 86400000); + Result := '"/DATE(' + IntToStr(MS) + ')/"'; + end; + function FormatedJsonTime: QStringW; + var + ADate: Integer; + begin + ADate := Trunc(V); + if SameValue(ADate, 0) then // DateΪ0ʱ + begin + if SameValue(V, 0) then + Result := '"' + FormatDateTime(JsonDateFormat, V) + '"' + else + Result := '"' + FormatDateTime(JsonTimeFormat, V) + '"'; + end + else + begin + if SameValue(V - ADate, 0) then + Result := '"' + FormatDateTime(JsonDateFormat, V) + '"' + else + Result := '"' + FormatDateTime(JsonDateTimeFormat, V) + '"'; + end; + end; + +begin + if StrictJson then + InternalWriteString(JsonDateTime) + else + InternalWriteString(FormatedJsonTime); +end; + +procedure TQJsonStreamHelper.WriteName(const S: QStringW); +begin + InternalWriteString('"' + TQJson.JsonEscape(S, FDoEscape) + '":', False); +end; + +procedure TQJsonStreamHelper.WriteNull(const AName: QStringW); +begin + WriteName(AName); + WriteNull; +end; + +procedure TQJsonStreamHelper.WriteNull; +begin + InternalWriteString('null'); +end; + +procedure TQJsonStreamHelper.Write(const AName: QStringW; AValue: Double); +begin + WriteName(AName); + InternalWriteString(FloatToStr(AValue)); +end; + +procedure TQJsonStreamHelper.Write(const AName: QStringW; AValue: Int64); +begin + WriteName(AName); + Write(IntToStr(AValue)); +end; + +procedure TQJsonStreamHelper.Write(const AName, AValue: QStringW); +begin + WriteName(AName); + Write(AValue); +end; + +procedure TQJsonStreamHelper.Write(const AName: QStringW; const p: PByte; + const l: Integer); +begin + WriteName(AName); + Write(p, l); +end; + +procedure TQJsonStreamHelper.Write(const AName: QStringW; AValue: Boolean); +begin + WriteName(AName); + Write(AValue); +end; + +procedure TQJsonStreamHelper.Write(const AName: QStringW; AValue: TBytes); +begin + WriteName(AName); + Write(AValue); +end; + +procedure TQJsonStreamHelper.WriteDateTime(const AName: QStringW; + AValue: TDateTime); +begin + WriteName(AName); + WriteDateTime(AValue); +end; + +procedure TQJsonStreamHelper.BeginArray(const AName: QStringW); +begin + InternalWriteString('"' + TQJson.JsonEscape(AName, FDoEscape) + '":[', False); +end; + +initialization + +StrictJson := False; +JsonRttiEnumAsInt := True; +JsonCaseSensitive := True; +JsonDateFormat := 'yyyy-mm-dd'; +JsonDateTimeFormat := 'yyyy-mm-dd''T''hh:nn:ss.zzz'; +JsonTimeFormat := 'hh:nn:ss.zzz'; +OnQJsonCreate := nil; +OnQJsonFree := nil; + +end. diff --git a/qdac/qrbtree.pas b/qdac/qrbtree.pas new file mode 100644 index 0000000..dc23d38 --- /dev/null +++ b/qdac/qrbtree.pas @@ -0,0 +1,1567 @@ +unit qrbtree; + +interface + +{ + ԪıLinux 3.14.4ں˺ʵ֣ճҲ֤Ϯ100% + ȷ:)оòԵʱ򣬿linuxں˵rbtree.h/rbtree_augmented.h/rbtree.c + տǷһСij©ˡ + GPLЭԭģչ涨ŵ֣ + /* + Red Black Trees + (C) 1999 Andrea Arcangeli + (C) 2002 David Woodhouse + (C) 2012 Michel Lespinasse + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/lib/rbtree.c + */ + ˳˵һ䣬⣬˳㷢Ϣһ£ + QDACwww.qdac.cc + QDACٷȺ250530692 + QQ:109867294@qq.com +} +{$I 'qdac.inc'} + +uses + Classes, sysutils, qstring; +{$HPPEMIT '#pragma link "qrbtree"'} + +type + /// ȽϺһǾ of objectˣǸľȥ + /// һҪȽϵIJ + /// ڶҪȽϵIJ + /// P1P2ش0ֵȣ0 + TQRBCompare = function(P1, P2: Pointer): Integer of object; + + TQRBNode = class; + PQRBNode = ^TQRBNode; + TQRBTree = class; + PQRBTree = ^TQRBTree; + /// + /// ɾ֪ͨ¼ɾһʱ + /// + /// ¼ĺ + /// ҪɾĽ + TQRBDeleteNotify = procedure(ASender: TQRBTree; ANode: TQRBNode) of object; + // ¼û뵽ɶʱҪԭLinuxдˣҲͱ + TQRBRotateNotify = procedure(ASender: TQRBTree; AOld, ANew: TQRBNode) + of object; + TQRBCopyNotify = TQRBRotateNotify; + TQRBPropagateNotify = procedure(ASender: TQRBTree; ANode, AStop: TQRBNode) + of object; + + /// TQRBNodeһ¼LinuxȣһDataԱ + /// ֱӱⲿָ룬ԭVirtualTreeViewһһDataSize + /// ÷Ҫڴ棬ûͷˣȻijrecordĻӦ + /// һڴ䣬ʱȲŻ˵(record+record helper) + /// + TQRBNode = class + private + FParent_Color: IntPtr; + FLeft, FRight: TQRBNode; + FData: Pointer; + function GetNext: TQRBNode; inline; + function GetParent: TQRBNode; inline; + function GetPrior: TQRBNode; inline; + function GetIsEmpty: Boolean; inline; + procedure SetBlack; inline; + function RedParent: TQRBNode; inline; + procedure SetParentColor(AParent: TQRBNode; AColor: Integer); inline; + function GetIsBlack: Boolean; inline; + function GetIsRed: Boolean; inline; + procedure SetParent(const Value: TQRBNode); inline; + function GetLeftDeepest: TQRBNode; inline; + public + constructor Create; overload; + destructor Destroy; override; + /// + /// ӦݳԱ + /// + /// Դ + procedure Assign(src: TQRBNode); + /// һ㣬ҷòϣӦrb_next_postorder + function NextPostOrder: TQRBNode; + /// Ϊս㣬úIsEmptytrue + procedure Clear; + /// һ + property Next: TQRBNode read GetNext; // rb_next + /// ǰһ + property Prior: TQRBNode read GetPrior; // rb_prev + /// + property Parent: TQRBNode read GetParent write SetParent; // rb_parent + /// Ƿǿս + property IsEmpty: Boolean read GetIsEmpty; // RB_NODE_EMPTY + /// ǷǺڽ + property IsBlack: Boolean read GetIsBlack; // rb_is_black + /// ǷΪ + property IsRed: Boolean read GetIsRed; // rb_is_red + /// ҽ + property Right: TQRBNode read FRight write FRight; // rb_right + /// + property Left: TQRBNode read FLeft write FLeft; // rb_left + /// ݳԱ + property Data: Pointer read FData write FData; // ݳԱ + /// + property LeftDeepest: TQRBNode read GetLeftDeepest; + end; + + /// Delphiװ + TQRBTree = class + protected + FRoot: TQRBNode; + FCount: Integer; + FOnCompare: TQRBCompare; + FOnDelete: TQRBDeleteNotify; + FOnRotate: TQRBRotateNotify; + FOnCopy: TQRBCopyNotify; + FOnPropagate: TQRBPropagateNotify; + function GetIsEmpty: Boolean; inline; + procedure RotateSetParents(AOld, ANew: TQRBNode; color: Integer); inline; + procedure InsertNode(node: TQRBNode); inline; + procedure EraseColor(AParent: TQRBNode); inline; + procedure ChangeChild(AOld, ANew, Parent: TQRBNode); inline; + function EraseAugmented(node: TQRBNode): TQRBNode; inline; + procedure DoCopy(node1, node2: TQRBNode); inline; + procedure DoPropagate(node1, node2: TQRBNode); inline; + procedure InsertColor(AChild: TQRBNode); inline; + procedure DoRotate(AOld, ANew: TQRBNode); inline; + procedure LinkNode(node, Parent: TQRBNode; var rb_link: TQRBNode); inline; + public + /// 캯һСȽϺȥԱڲͲʱܹȷ + constructor Create(AOnCompare: TQRBCompare); overload; + destructor Destroy; override; + /// ɾһ + /// ҪɾĽ + /// ɹرɾDataݳԱַʧܻ򲻴ڣnil + /// ָOnDelete¼ӦͷDataԱͲҪԷʷصĵַ + function Delete(AChild: TQRBNode): Pointer; // rb_erase + /// ׸ + function First: TQRBNode; // rb_first + /// ׸ + function Last: TQRBNode; // rb_last + /// ׸ + function FirstPostOrder: TQRBNode; // rb_first_postorder + /// һݣȽɹʱ¼ص + /// ݳԱ + /// ɹtrueʧܣfalse + /// ָͬѾڣͻ᷵false + function Insert(AData: Pointer): Boolean; + /// ָͬĽ + /// Ҫ + /// ҵĽ + function Find(AData: Pointer): TQRBNode; + /// еĽ + procedure Clear; + /// + /// ƷҪ滻Ľ + /// ½ + /// 滻ҪԼ֤ݺƷһ£ңܱ֤, + /// ɾ+滻 + /// + procedure Replace(victim, ANew: TQRBNode); + /// жǷΪ + property IsEmpty: Boolean read GetIsEmpty; + /// ȽϺעⲻҪıȽ㷨 + property OnCompare: TQRBCompare read FOnCompare write FOnCompare; + /// ɾ¼Ӧ + property OnDelete: TQRBDeleteNotify read FOnDelete write FOnDelete; + /// ת¼ + property OnRotate: TQRBRotateNotify read FOnRotate write FOnRotate; + /// ¼ + property OnCopy: TQRBCopyNotify read FOnCopy write FOnCopy; + /// ɢ¼ + property OnPropagate: TQRBPropagateNotify read FOnPropagate + write FOnPropagate; + // + property Count: Integer read FCount; + end; + + /// ͰԪصĹϣֵб + TQHashType = Cardinal; + PQHashList = ^TQHashList; + + TQHashList = record + Next: PQHashList; + /// һԪ + Hash: TQHashType; + /// ǰԪعϣֵ¼Ա·ͰʱҪٴⲿ + Data: Pointer; + /// ݳԱ + end; + + TQHashArray = array of PQHashList; + TQHashTable = class; + /// ɾϣһԪص֪ͨ + /// ϣ + /// ҪɾĶĹϣֵ + /// ҪɾĶָ + TQHashNotify = procedure(ATable: TQHashTable; AHash: TQHashType; + AData: Pointer) of object; + TQBucketNotify = procedure(ATable: TQHashTable; ABucketIndex: Integer) + of object; + + TQHashStatics = record + Count: Integer; // ЧͰ + MaxDepth: Integer; // + AvgDepth: Double; // ƽ + TotalDepth: Integer; // + MaxItems: PQHashList; // б + DepthList: array of Integer; // ͬб + end; + + TQHashTableIterator = class + protected + FCurrent: PQHashList; + FList: TQHashTable; + FBucket: Integer; + public + constructor Create(AList: TQHashTable); + function GetCurrent: PQHashList; inline; + function MoveNext: Boolean; + property Current: PQHashList read GetCurrent; + end; + + { + ϣڴһЩڲѯɢݣϣЧȡںʵͰСͺ + ĹϣµЧO(1) + 1. + AddһԪؽȥҪֹظȵFind¡ + 2.ɾ + Deleteɾ + 3. + ǹϣҪ֮ǰOnCompareԱȽData + ֵϵͳֻȽDataָĵַǷһ¡ + } + TQHashTable = class + private + procedure SetAutoSize(const Value: Boolean); + protected + FBuckets: TQHashArray; + FCount: Integer; + FOnDelete: TQHashNotify; + FOnCompare: TQRBCompare; + FAfterBucketUsed, FAfterBucketEmpty: TQBucketNotify; + FAutoSize: Boolean; + procedure DoDelete(AHash: TQHashType; AData: Pointer); + function GetBuckets(AIndex: Integer): PQHashList; inline; + function GetBucketCount: Integer; inline; + function Compare(Data1, Data2: Pointer; var AResult: Integer) + : Boolean; inline; + procedure InternalDelete(AIndex: Integer; APrior, AHashList: PQHashList); + public + /// 캯ͰΪڿԵResize + constructor Create(ASize: Integer); overload; + /// 캯 + constructor Create; overload; + destructor Destroy; override; + /// Ͱ + /// µͰΪ0ԶΪӽԪֵ + procedure Resize(ASize: Cardinal); + /// һԪ + /// ҪӵԪصַ + /// ҪԪصĹϣֵ + /// ϣֵļⲿ߸ɣϣٶĹϣֵѾɵ + procedure Add(AData: Pointer; AHash: TQHashType); + /// ָĹϣֵԪб + /// ҪҵĹϣֵ + /// ҵĹϣֵбûУnil + /// صĹϣбӦFreeHashListͷ + function Find(AHash: TQHashType): PQHashList; overload; + /// ָĹϣֵԪб + /// Ҫҵָ룬OnCompareȽǷһ + /// ҪҵĹϣֵ + /// ҵĸݵַûУnil + function Find(AData: Pointer; AHash: TQHashType): Pointer; overload; + /// ָϣֵĵһԪֵ + /// ҪҵĹϣֵ + /// ҵԪݵַûУnil + function FindFirstData(AHash: TQHashType): Pointer; + /// ָĹϣֵԪб + /// ҪҵĹϣֵ + /// ҵĹϣֵбûУnil + /// ҪͷŷصĹϣб׸Ԫأصڲбҵ׸ַ + function FindFirst(AHash: TQHashType): PQHashList; inline; + /// ָĹϣֵԪбһԪ + /// FindFirst/FindNextصб + /// عϣֵбһԪأûУnil + function FindNext(AList: PQHashList): PQHashList; inline; + /// ͷŹϣֵԪб + /// Findصб + procedure FreeHashList(AList: PQHashList); + /// жָԪǷ + /// ҪжϵԪֵַָ + /// ADataӦĹϣֵ + /// ڣtrue򷵻false + function Exists(AData: Pointer; AHash: TQHashType): Boolean; + /// ɾָϣֵָԪ + /// ҪɾԪֵַָ + /// ADataӦĹϣֵ + procedure Delete(AData: Pointer; AHash: TQHashType); overload; + procedure Delete(AHashList: PQHashList); overload; + /// ͳϣͰݷֲϢԱûĽϣ + procedure Statics(var AResult: TQHashStatics); + /// + /// һݵĹϣֵݲڣ + /// + /// ݵ + /// ݶӦԭϣֵ + /// ݱɺ¹ϣֵ + procedure ChangeHash(AData: Pointer; AOldHash, ANewHash: TQHashType); + /// б + procedure Clear; + procedure ForEach(ACallback: TQHashNotify); + function GetEnumerator: TQHashTableIterator; + /// Ԫظ + property Count: Integer read FCount; + /// Ͱ + property BucketCount: Integer read GetBucketCount; + /// Ͱб + property Buckets[AIndex: Integer]: PQHashList read GetBuckets; default; + /// ȽϺ + property OnCompare: TQRBCompare read FOnCompare write FOnCompare; + /// ɾ¼֪ͨ + property OnDelete: TQHashNotify read FOnDelete write FOnDelete; + /// һͰʹʱ + /// ע⣬AutoSizeΪTrueʱͰϢܻڴ¼ + property AfterBucketUsed: TQBucketNotify read FAfterBucketUsed + write FAfterBucketUsed; + /// һͰɿʱ + property AfterBucketEmpty: TQBucketNotify read FAfterBucketEmpty + write FAfterBucketEmpty; + /// ǷԶͰС + property AutoSize: Boolean read FAutoSize write SetAutoSize; + end; + + TQRBComparor = class + private + public + function IntComp(P1, P2: Pointer): Integer; + function SingleComp(P1, P2: Pointer): Integer; + function FloatComp(P1, P2: Pointer): Integer; + function Int64Comp(P1, P2: Pointer): Integer; + function QStringWComp(P1, P2: Pointer): Integer; + function QStringWCompI(P1, P2: Pointer): Integer; + function Int2Pointer(const V: Integer): Pointer; inline; + function Pointer2Int(const V: Pointer): Integer; inline; + end; + +var + RBDefaultComparor: TQRBComparor; + +implementation + +const + RB_RED = 0; + RB_BLACK = 1; + + { TQRBTree } +procedure TQRBTree.DoCopy(node1, node2: TQRBNode); +begin + if Assigned(FOnCopy) then + FOnCopy(Self, node1, node2); +end; + +procedure TQRBTree.DoPropagate(node1, node2: TQRBNode); +begin + if Assigned(FOnPropagate) then + FOnPropagate(Self, node1, node2); +end; + +procedure TQRBTree.ChangeChild(AOld, ANew, Parent: TQRBNode); +begin + if Parent <> nil then + begin + if Parent.Left = AOld then + Parent.Left := ANew + else + Parent.Right := ANew; + end + else + FRoot := ANew; +end; + +procedure TQRBTree.Clear; +var + ANode: TQRBNode; +begin + if Assigned(OnDelete) then + begin + ANode := First; + while ANode <> nil do + begin + OnDelete(Self, ANode); + ANode := ANode.Next; + end; + end; + FreeAndNil(FRoot); + FCount := 0; +end; + +constructor TQRBTree.Create(AOnCompare: TQRBCompare); +begin + inherited Create; + FOnCompare := AOnCompare; +end; + +destructor TQRBTree.Destroy; +begin + Clear; + inherited; +end; + +procedure TQRBTree.DoRotate(AOld, ANew: TQRBNode); +begin + if Assigned(FOnRotate) then + FOnRotate(Self, AOld, ANew); +end; + +function TQRBTree.Delete(AChild: TQRBNode): Pointer; +var + rebalance: TQRBNode; +begin + Result := AChild.Data; + rebalance := EraseAugmented(AChild); + if rebalance <> nil then + EraseColor(rebalance); + AChild.FLeft := nil; + AChild.FRight := nil; + Dec(FCount); + if Assigned(FOnDelete) then + FOnDelete(Self, AChild); + FreeObject(AChild); +end; + +function TQRBTree.EraseAugmented(node: TQRBNode): TQRBNode; +var + child, tmp, AParent, rebalance: TQRBNode; + pc, pc2: IntPtr; + successor, child2: TQRBNode; +begin + child := node.Right; + tmp := node.Left; + if tmp = nil then + begin + pc := node.FParent_Color; + AParent := node.Parent; + ChangeChild(node, child, AParent); + if Assigned(child) then + begin + child.FParent_Color := pc; + rebalance := nil; + end + else if (pc and RB_BLACK) <> 0 then + rebalance := AParent + else + rebalance := nil; + tmp := AParent; + end + else if not Assigned(child) then + begin + tmp.FParent_Color := node.FParent_Color; + AParent := node.Parent; + ChangeChild(node, tmp, AParent); + rebalance := nil; + tmp := AParent; + end + else + begin + successor := child; + tmp := child.Left; + if not Assigned(tmp) then + begin + AParent := successor; + child2 := successor.Right; + DoCopy(node, successor); + end + else + begin + repeat + AParent := successor; + successor := tmp; + tmp := tmp.Left; + until tmp = nil; + AParent.Left := successor.Right; + child2 := successor.Right; + successor.Right := child; + child.Parent := successor; + DoCopy(node, successor); + DoPropagate(AParent, successor); + end; + successor.Left := node.Left; + tmp := node.Left; + tmp.Parent := successor; + pc := node.FParent_Color; + tmp := node.Parent; + ChangeChild(node, successor, tmp); + if Assigned(child2) then + begin + successor.FParent_Color := pc; + child2.SetParentColor(AParent, RB_BLACK); + rebalance := nil; + end + else + begin + pc2 := successor.FParent_Color; + successor.FParent_Color := pc; + if (pc2 and RB_BLACK) <> 0 then + rebalance := AParent + else + rebalance := nil; + end; + tmp := successor; + end; + DoPropagate(tmp, nil); + Result := rebalance; +end; + +procedure TQRBTree.EraseColor(AParent: TQRBNode); +var + node, sibling, tmp1, tmp2: TQRBNode; +begin + node := nil; + while (true) do + begin + sibling := AParent.Right; + if node <> sibling then + begin +{$REGION 'node<>sibling'} + if sibling.IsRed then +{$REGION 'slbling.IsRed'} + begin + AParent.Right := sibling.Left; + tmp1 := sibling.Left; + sibling.Left := AParent; + tmp1.SetParentColor(AParent, RB_BLACK); + RotateSetParents(AParent, sibling, RB_RED); + DoRotate(AParent, sibling); + sibling := tmp1; + end; +{$ENDREGION 'slbling.IsRed'} + tmp1 := sibling.Right; + if (not Assigned(tmp1)) or tmp1.IsBlack then + begin +{$REGION 'tmp1.IsBlack'} + tmp2 := sibling.Left; + if (not Assigned(tmp2)) or tmp2.IsBlack then + begin +{$REGION 'tmp2.IsBlack'} + sibling.SetParentColor(AParent, RB_RED); + if AParent.IsRed then + AParent.SetBlack + else + begin + node := AParent; + AParent := node.Parent; + if Assigned(AParent) then + Continue; + end; + Break; +{$ENDREGION 'tmp2.IsBlack'} + end; + sibling.Left := tmp2.Right; + tmp1 := tmp2.Right; + tmp2.Right := sibling; + AParent.Right := tmp2; + if Assigned(tmp1) then + tmp1.SetParentColor(sibling, RB_BLACK); + DoRotate(sibling, tmp2); + tmp1 := sibling; + sibling := tmp2; +{$ENDREGION 'tmp1.IsBlack'} + end; + AParent.Right := sibling.Left; + tmp2 := sibling.Left; + sibling.Left := AParent; + tmp1.SetParentColor(sibling, RB_BLACK); + if Assigned(tmp2) then + tmp2.Parent := AParent; + RotateSetParents(AParent, sibling, RB_BLACK); + DoRotate(AParent, sibling); + Break; +{$ENDREGION 'node<>sibling'} + end + else + begin +{$REGION 'RootElse'} + sibling := AParent.Left; + if (sibling.IsRed) then + begin +{$REGION 'Case 1 - right rotate at AParent'} + AParent.Left := sibling.Right; + tmp1 := sibling.Right; + tmp1.SetParentColor(AParent, RB_BLACK); + RotateSetParents(AParent, sibling, RB_RED); + DoRotate(AParent, sibling); + sibling := tmp1; +{$ENDREGION 'Case 1 - right rotate at AParent'} + end; + tmp1 := sibling.Left; + if (tmp1 = nil) or tmp1.IsBlack then + begin +{$REGION 'tmp1.IsBlack'} + tmp2 := sibling.Right; + if (tmp2 = nil) or tmp2.IsBlack then + begin +{$REGION 'tmp2.IsBlack'} + sibling.SetParentColor(AParent, RB_RED); + if AParent.IsRed then + AParent.SetBlack + else + begin + node := AParent; + AParent := node.Parent; + if Assigned(AParent) then + Continue; + end; + Break; +{$ENDREGION 'tmp2.IsBlack'} + end; + sibling.Right := tmp2.Left; + tmp1 := tmp2.Left; + tmp2.Left := sibling; + AParent.Left := tmp2; + if Assigned(tmp1) then + tmp1.SetParentColor(sibling, RB_BLACK); + DoRotate(sibling, tmp2); + tmp1 := sibling; + sibling := tmp2; +{$ENDREGION ''tmp1.IsBlack'} + end; + AParent.Left := sibling.Right; + tmp2 := sibling.Right; + sibling.Right := AParent; + tmp1.SetParentColor(sibling, RB_BLACK); + if Assigned(tmp2) then + tmp2.Parent := AParent; + RotateSetParents(AParent, sibling, RB_BLACK); + DoRotate(AParent, sibling); + Break; +{$ENDREGION 'RootElse'} + end; + end; +end; + +function TQRBTree.Find(AData: Pointer): TQRBNode; +var + rc: Integer; +begin + Result := FRoot; + while Assigned(Result) do + begin + rc := OnCompare(AData, Result.Data); + if rc < 0 then + Result := Result.Left + else if rc > 0 then + Result := Result.Right + else + Break; + end +end; + +function TQRBTree.First: TQRBNode; +begin + Result := FRoot; + if Result <> nil then + begin + while Assigned(Result.Left) do + Result := Result.Left; + end; +end; + +function TQRBTree.FirstPostOrder: TQRBNode; +begin + if Assigned(FRoot) then + Result := FRoot.LeftDeepest + else + Result := nil; +end; + +function TQRBTree.GetIsEmpty: Boolean; +begin + Result := (FRoot = nil); +end; + +procedure TQRBTree.InsertColor(AChild: TQRBNode); +begin + InsertNode(AChild); +end; + +// static __always_inline void +// __rb_insert(struct rb_node *node, struct rb_root *root, +/// void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) +function TQRBTree.Insert(AData: Pointer): Boolean; +var + new: PQRBNode; + Parent, AChild: TQRBNode; + rc: Integer; +begin + new := @FRoot; + Parent := nil; + while new^ <> nil do + begin + rc := OnCompare(AData, new.Data); + Parent := new^; + if rc < 0 then + new := @new^.FLeft + else if rc > 0 then + new := @new^.FRight + else // Ѵ + begin + Result := False; + Exit; + end; + end; + AChild := TQRBNode.Create; + AChild.Data := AData; + LinkNode(AChild, Parent, new^); + InsertColor(AChild); + Inc(FCount); + Result := true; +end; + +procedure TQRBTree.InsertNode(node: TQRBNode); +var + AParent, GParent, tmp: TQRBNode; +begin + AParent := node.RedParent; + while true do + begin + if AParent = nil then + begin + node.SetParentColor(nil, RB_BLACK); + Break; + end + else if AParent.IsBlack then + Break; + GParent := AParent.RedParent; + tmp := GParent.Right; + if AParent <> tmp then + begin + if Assigned(tmp) and tmp.IsRed then + begin + tmp.SetParentColor(GParent, RB_BLACK); + AParent.SetParentColor(GParent, RB_BLACK); + node := GParent; + AParent := node.Parent; + node.SetParentColor(AParent, RB_RED); + Continue; + end; + tmp := AParent.Right; + if node = tmp then + begin + AParent.Right := node.Left; + tmp := node.Left; + node.Left := AParent; + if Assigned(tmp) then + tmp.SetParentColor(AParent, RB_BLACK); + AParent.SetParentColor(node, RB_RED); + DoRotate(AParent, node); // augment_rotate(parent,node) + AParent := node; + tmp := node.Right; + end; + GParent.Left := tmp; + AParent.Right := GParent; + if tmp <> nil then + tmp.SetParentColor(GParent, RB_BLACK); + RotateSetParents(GParent, AParent, RB_RED); + DoRotate(GParent, AParent); + Break; + end + else + begin + tmp := GParent.Left; + if Assigned(tmp) and tmp.IsRed then + begin + tmp.SetParentColor(GParent, RB_BLACK); + AParent.SetParentColor(GParent, RB_BLACK); + node := GParent; + AParent := node.Parent; + node.SetParentColor(AParent, RB_RED); + Continue; + end; + tmp := AParent.Left; + if node = tmp then + begin + AParent.Left := node.Right; + tmp := node.Right; + node.Right := AParent; + if tmp <> nil then + tmp.SetParentColor(AParent, RB_BLACK); + AParent.SetParentColor(node, RB_RED); + DoRotate(AParent, node); + AParent := node; + tmp := node.Left; + end; + GParent.Right := tmp; + AParent.Left := GParent; + if tmp <> nil then + tmp.SetParentColor(GParent, RB_BLACK); + RotateSetParents(GParent, AParent, RB_RED); + DoRotate(GParent, AParent); + Break; + end; + end; +end; + +function TQRBTree.Last: TQRBNode; +begin + Result := FRoot; + if Result <> nil then + begin + while Assigned(Result.Right) do + Result := Result.Right; + end; +end; + +procedure TQRBTree.LinkNode(node, Parent: TQRBNode; var rb_link: TQRBNode); +begin + node.FParent_Color := IntPtr(Parent); + node.FLeft := nil; + node.FRight := nil; + rb_link := node; +end; + +procedure TQRBTree.Replace(victim, ANew: TQRBNode); +var + Parent: TQRBNode; +begin + Parent := victim.Parent; + ChangeChild(victim, ANew, Parent); + if Assigned(victim.Left) then + victim.Left.SetParent(ANew) + else + victim.Right.SetParent(ANew); + ANew.Assign(victim); +end; + +// __rb_rotate_set_parents(struct rb_node *old, struct rb_node *new,struct rb_root *root, int color) +{ + struct rb_node *parent = rb_parent(old); + new->__rb_parent_color = old->__rb_parent_color; + rb_set_parent_color(old, new, color); + __rb_change_child(old, new, parent, root); +} +procedure TQRBTree.RotateSetParents(AOld, ANew: TQRBNode; color: Integer); +var + AParent: TQRBNode; +begin + AParent := AOld.Parent; + ANew.FParent_Color := AOld.FParent_Color; + AOld.SetParentColor(ANew, color); + ChangeChild(AOld, ANew, AParent); +end; + +{ TQRBNode } + +procedure TQRBNode.Assign(src: TQRBNode); +begin + FParent_Color := src.FParent_Color; + FLeft := src.FLeft; + FRight := src.FRight; + FData := src.FData; +end; + +procedure TQRBNode.Clear; +begin + FParent_Color := IntPtr(Self); +end; + +constructor TQRBNode.Create; +begin + +end; + +destructor TQRBNode.Destroy; +begin + if Assigned(FLeft) then + FreeObject(FLeft); + if Assigned(FRight) then + FreeObject(FRight); + inherited; +end; + +function TQRBNode.GetIsBlack: Boolean; +begin + Result := (IntPtr(FParent_Color) and $1) <> 0; +end; + +function TQRBNode.GetIsEmpty: Boolean; +begin + Result := (FParent_Color = IntPtr(Self)); +end; + +function TQRBNode.GetIsRed: Boolean; +begin + Result := ((IntPtr(FParent_Color) and $1) = 0); +end; + +function TQRBNode.GetLeftDeepest: TQRBNode; +begin + Result := Self; + while true do + begin + if Assigned(Result.Left) then + Result := Result.Left + else if Assigned(Result.Right) then + Result := Result.Right + else + Break; + end; +end; + +function TQRBNode.GetNext: TQRBNode; +var + node, Parent: TQRBNode; +begin + if IsEmpty then + Result := nil + else + begin + if Assigned(FRight) then + begin + Result := FRight; + while Assigned(Result.Left) do + Result := Result.Left; + Exit; + end; + node := Self; + repeat + Parent := node.Parent; + if Assigned(Parent) and (node = Parent.Right) then + node := Parent + else + Break; + until Parent = nil; + Result := Parent; + end; +end; + +function TQRBNode.GetParent: TQRBNode; +begin + Result := TQRBNode(IntPtr(FParent_Color) and (not $3)); +end; + +function TQRBNode.GetPrior: TQRBNode; +var + node, AParent: TQRBNode; +begin + if IsEmpty then + Result := nil + else + begin + if Assigned(FLeft) then + begin + Result := FLeft; + while Assigned(Result.Right) do + Result := Result.Right; + Exit; + end; + node := Self; + repeat + AParent := node.Parent; + if Assigned(Parent) and (node = AParent.Left) then + node := AParent + else + Break; + until AParent = nil; + Result := AParent; + end; +end; + +function TQRBNode.NextPostOrder: TQRBNode; +begin + Result := Parent; + if Assigned(Result) and (Self = Result.Left) and Assigned(Result.Right) then + Result := Result.Right.LeftDeepest; +end; +// struct rb_node *rb_red_parent(struct rb_node *red) + +function TQRBNode.RedParent: TQRBNode; +begin + Result := TQRBNode(FParent_Color); +end; + +// rbtree.c rb_set_black(struct rb_node *rb) +procedure TQRBNode.SetBlack; +begin + FParent_Color := FParent_Color or RB_BLACK; +end; + +procedure TQRBNode.SetParent(const Value: TQRBNode); +begin + FParent_Color := IntPtr(Value) or (IntPtr(FParent_Color) and $1); +end; + +procedure TQRBNode.SetParentColor(AParent: TQRBNode; AColor: Integer); +begin + FParent_Color := IntPtr(AParent) or AColor; +end; + +{ TQHashTable } + +procedure TQHashTable.Add(AData: Pointer; AHash: TQHashType); +var + AIndex: Integer; + ABucket: PQHashList; +begin + new(ABucket); + ABucket.Hash := AHash; + ABucket.Data := AData; + AIndex := AHash mod Cardinal(Length(FBuckets)); + ABucket.Next := FBuckets[AIndex]; + FBuckets[AIndex] := ABucket; + Inc(FCount); + if (not Assigned(ABucket.Next)) and Assigned(FAfterBucketUsed) then + FAfterBucketUsed(Self, AIndex); + if FAutoSize and ((FCount div Length(FBuckets)) > 3) then + Resize(0); +end; + +procedure TQHashTable.ChangeHash(AData: Pointer; + AOldHash, ANewHash: TQHashType); +var + AList, APrior: PQHashList; + ACmpResult: Integer; + AIndex: Integer; + AChanged: Boolean; +begin + AChanged := False; + AIndex := AOldHash mod Cardinal(Length(FBuckets)); + AList := FBuckets[AIndex]; + APrior := nil; + while AList <> nil do + begin + if (AList.Hash = AOldHash) then + begin + if (AList.Data = AData) or (Compare(AData, AList.Data, ACmpResult) and + (ACmpResult = 0)) then + begin + if Assigned(APrior) then + APrior.Next := AList.Next + else + FBuckets[AIndex] := AList.Next; + AList.Hash := ANewHash; + AIndex := ANewHash mod Cardinal(Length(FBuckets)); + AList.Next := FBuckets[AIndex]; + FBuckets[AIndex] := AList; + AChanged := true; + Break; + end; + end; + APrior := AList; + AList := AList.Next; + end; + if not AChanged then + Add(AData, ANewHash); +end; + +procedure TQHashTable.Clear; +var + I, H: Integer; + ABucket: PQHashList; +begin + H := High(FBuckets); + for I := 0 to H do + begin + ABucket := FBuckets[I]; + if ABucket <> nil then + begin + while ABucket <> nil do + begin + FBuckets[I] := ABucket.Next; + DoDelete(ABucket.Hash, ABucket.Data); + Dispose(ABucket); + ABucket := FBuckets[I]; + end; + if Assigned(FAfterBucketEmpty) then + FAfterBucketEmpty(Self, I); + end; + end; + FCount := 0; +end; + +function TQHashTable.Compare(Data1, Data2: Pointer; + var AResult: Integer): Boolean; +begin + if Assigned(FOnCompare) then + begin + AResult := FOnCompare(Data1, Data2); + Result := true; + end + else + Result := False; +end; + +constructor TQHashTable.Create; +begin + inherited; + Resize(0); +end; + +constructor TQHashTable.Create(ASize: Integer); +begin + if ASize = 0 then + ASize := 17; + Resize(ASize); +end; + +procedure TQHashTable.Delete(AData: Pointer; AHash: TQHashType); +var + AIndex, ACompare: Integer; + AHashList, APrior: PQHashList; +begin + AIndex := AHash mod Cardinal(Length(FBuckets)); + AHashList := FBuckets[AIndex]; + APrior := nil; + while Assigned(AHashList) do + begin + if (AHashList.Data = AData) or + ((Compare(AHashList.Data, AData, ACompare) and (ACompare = 0))) then + // ͬһݣϣֵֻΪͬͬϵȥ + begin + InternalDelete(AIndex, APrior, AHashList); + Break; + end + else + begin + APrior := AHashList; + AHashList := APrior.Next; + end; + end; +end; + +procedure TQHashTable.Delete(AHashList: PQHashList); +var + AIndex: Integer; + APrior: PQHashList; +begin + AIndex := AHashList.Hash mod Cardinal(Length(FBuckets)); + APrior := FBuckets[AIndex]; + if APrior = AHashList then + InternalDelete(AIndex, nil, AHashList) + else + begin + while Assigned(APrior) and (APrior.Next <> AHashList) do + APrior := APrior.Next; + if Assigned(APrior) then + InternalDelete(AIndex, APrior, AHashList); + end; +end; + +destructor TQHashTable.Destroy; +begin + Clear; + inherited; +end; + +procedure TQHashTable.DoDelete(AHash: TQHashType; AData: Pointer); +begin + if Assigned(FOnDelete) then + FOnDelete(Self, AHash, AData); +end; + +function TQHashTable.Exists(AData: Pointer; AHash: TQHashType): Boolean; +var + AList: PQHashList; + AResult: Integer; +begin + AList := FindFirst(AHash); + Result := False; + while AList <> nil do + begin + if (AList.Data = AData) or (Compare(AList.Data, AData, AResult) and + (AResult = 0)) then + begin + Result := true; + Break; + end; + AList := FindNext(AList); + end; +end; + +function TQHashTable.Find(AHash: TQHashType): PQHashList; +var + AIndex: Integer; + AList, AItem: PQHashList; +begin + AIndex := AHash mod Cardinal(Length(FBuckets)); + Result := nil; + AList := FBuckets[AIndex]; + while AList <> nil do + begin + if AList.Hash = AHash then + begin + new(AItem); + AItem.Data := AList.Data; + AItem.Next := Result; + AItem.Hash := AHash; + Result := AItem; + end; + AList := AList.Next; + end; +end; + +function TQHashTable.Find(AData: Pointer; AHash: TQHashType): Pointer; +var + ACmpResult: Integer; + AList: PQHashList; +begin + Result := nil; + AList := FindFirst(AHash); + while AList <> nil do + begin + if (AList.Data = AData) or (Compare(AData, AList.Data, ACmpResult) and + (ACmpResult = 0)) then + begin + Result := AList.Data; + Break; + end; + AList := AList.Next; + end; +end; + +function TQHashTable.FindFirst(AHash: TQHashType): PQHashList; +var + AIndex: Integer; + AList: PQHashList; +begin + Result := nil; + if Length(FBuckets) > 0 then + begin + AIndex := AHash mod Cardinal(Length(FBuckets)); + AList := FBuckets[AIndex]; + while AList <> nil do + begin + if AList.Hash = AHash then + begin + Result := AList; + Break; + end; + AList := AList.Next; + end; + end; +end; + +function TQHashTable.FindFirstData(AHash: TQHashType): Pointer; +var + AList: PQHashList; +begin + AList := FindFirst(AHash); + if AList <> nil then + Result := AList.Data + else + Result := nil; +end; + +function TQHashTable.FindNext(AList: PQHashList): PQHashList; +begin + Result := nil; + if Assigned(AList) then + begin + Result := AList.Next; + while Result <> nil do + begin + if Result.Hash = AList.Hash then + Break + else + Result := Result.Next; + end; + end; +end; + +procedure TQHashTable.ForEach(ACallback: TQHashNotify); +var + I, H: Integer; + ABucket: PQHashList; +begin + H := High(FBuckets); + for I := 0 to H do + begin + ABucket := FBuckets[I]; + while ABucket <> nil do + begin + ACallback(Self, ABucket.Hash, ABucket.Data); + ABucket := ABucket.Next; + end; + end; +end; + +procedure TQHashTable.FreeHashList(AList: PQHashList); +var + ANext: PQHashList; +begin + while AList <> nil do + begin + ANext := AList.Next; + Dispose(AList); + AList := ANext; + end; +end; + +function TQHashTable.GetBucketCount: Integer; +begin + Result := Length(FBuckets); +end; + +function TQHashTable.GetBuckets(AIndex: Integer): PQHashList; +begin + Result := FBuckets[AIndex]; +end; + +function TQHashTable.GetEnumerator: TQHashTableIterator; +begin + Result := TQHashTableIterator.Create(Self); +end; + +procedure TQHashTable.InternalDelete(AIndex: Integer; + APrior, AHashList: PQHashList); +begin + DoDelete(AHashList.Hash, AHashList.Data); + if Assigned(APrior) then + APrior.Next := AHashList.Next + else + FBuckets[AIndex] := AHashList.Next; + if FBuckets[AIndex] = nil then + begin + if Assigned(FAfterBucketEmpty) then + FAfterBucketEmpty(Self, AIndex); + end; + Dispose(AHashList); + Dec(FCount); +end; + +procedure TQHashTable.Resize(ASize: Cardinal); +const + // 28ĬϵͰߴ磬ASize=0ʱӦ + BucketSizes: array [0 .. 27] of Integer = (17, 37, 79, 163, 331, 673, 1361, + 2729, 5471, 10949, 21911, 43853, 87719, 175447, 350899, 701819, 1403641, + 2807303, 5614657, 11229331, 22458671, 44917381, 89834777, 179669557, + 359339171, 718678369, 1437356741, 2147483647); +var + I, AIndex: Integer; + AHash: Cardinal; + ALastBuckets: TQHashArray; + AList, ANext: PQHashList; +begin + if ASize = 0 then + begin + for I := 0 to 27 do + begin + if BucketSizes[I] > FCount then + begin + ASize := BucketSizes[I]; + Break; + end; + end; + if ASize = 0 then // ͰС + ASize := BucketSizes[27]; + if ASize = Cardinal(Length(FBuckets)) then + Exit; + end; + if ASize <> Cardinal(Length(FBuckets)) then + begin + // Ͱߴ·ԪڵĹϣͰԶõĻĽһͰһԪ + ALastBuckets := FBuckets; + SetLength(FBuckets, ASize); + for I := 0 to ASize - 1 do + FBuckets[I] := nil; + for I := 0 to High(ALastBuckets) do + begin + AList := ALastBuckets[I]; + while AList <> nil do + begin + AHash := AList.Hash; + AIndex := AHash mod ASize; + ANext := AList.Next; + AList.Next := FBuckets[AIndex]; + FBuckets[AIndex] := AList; + AList := ANext; + end; + end; + end; +end; + +procedure TQHashTable.SetAutoSize(const Value: Boolean); +begin + if FAutoSize <> Value then + begin + FAutoSize := Value; + if AutoSize then + begin + if (FCount div Length(FBuckets)) > 3 then + Resize(0); + end; + end; +end; + +procedure TQHashTable.Statics(var AResult: TQHashStatics); +var + I, L, D: Integer; + AList: PQHashList; + ADeptList: array of Integer; +begin + L := Length(FBuckets); + AResult.Count := 0; + AResult.MaxDepth := 0; + AResult.TotalDepth := 0; + SetLength(ADeptList, L); + for I := 0 to L - 1 do + begin + AList := FBuckets[I]; + if AList <> nil then + begin + D := 0; + while AList <> nil do + begin + Inc(D); + AList := AList.Next; + end; + if D > AResult.MaxDepth then + begin + AResult.MaxDepth := D; + AResult.MaxItems := FBuckets[I]; + end; + Inc(AResult.Count); + Inc(AResult.TotalDepth, D); + ADeptList[I] := D; + end; + end; + SetLength(AResult.DepthList, AResult.MaxDepth); + if AResult.Count > 0 then + AResult.AvgDepth := AResult.TotalDepth / AResult.Count; + for I := 0 to L - 1 do + begin + D := ADeptList[I]; + if D <> 0 then + Inc(AResult.DepthList[D - 1]); + end; +end; + +{ TQHashTableIterator } + +constructor TQHashTableIterator.Create(AList: TQHashTable); +begin + inherited Create; + FCurrent := nil; + FList := AList; + FBucket := -1; +end; + +function TQHashTableIterator.GetCurrent: PQHashList; +begin + Result := FCurrent; +end; + +function TQHashTableIterator.MoveNext: Boolean; +begin + if FCurrent <> nil then + begin + FCurrent := FCurrent.Next; + if FCurrent = nil then + begin + Inc(FBucket); + while FBucket < FList.BucketCount do + begin + FCurrent := FList.Buckets[FBucket]; + if Assigned(FCurrent) then + Break; + Inc(FBucket); + end; + end; + end + else + begin + FBucket := 0; + while FBucket < FList.BucketCount do + begin + FCurrent := FList.Buckets[FBucket]; + if Assigned(FCurrent) then + Break; + end; + end; + Result := FCurrent <> nil; +end; + +{ TQRBComparor } + +function TQRBComparor.IntComp(P1, P2: Pointer): Integer; +var + R: IntPtr; +begin + R := IntPtr(P1) - IntPtr(P2); + if R < 0 then + Result := -1 + else if R > 0 then + Result := 1 + else + Result := 0; +end; + +function TQRBComparor.Pointer2Int(const V: Pointer): Integer; +begin + Result := IntPtr(V); +end; + +function TQRBComparor.FloatComp(P1, P2: Pointer): Integer; +begin + if PDouble(P1)^ < PDouble(P2)^ then + Result := -1 + else if PDouble(P1)^ > PDouble(P2)^ then + Result := 1 + else + Result := 0; +end; + +function TQRBComparor.Int2Pointer(const V: Integer): Pointer; +begin + Result := Pointer(V); +end; + +function TQRBComparor.Int64Comp(P1, P2: Pointer): Integer; +begin + Result := PInt64(P1)^ - PInt64(P2)^; +end; + +function TQRBComparor.QStringWComp(P1, P2: Pointer): Integer; +begin + Result := StrCmpW(PQCharW(PQStringW(P1)^), PQCharW(PQStringW(P2)^), False); +end; + +function TQRBComparor.QStringWCompI(P1, P2: Pointer): Integer; +begin + Result := StrCmpW(PQCharW(PQStringW(P1)^), PQCharW(PQStringW(P2)^), true); +end; + +function TQRBComparor.SingleComp(P1, P2: Pointer): Integer; +begin + if PSingle(P1)^ < PSingle(P2)^ then + Result := -1 + else if PSingle(P1)^ > PSingle(P2)^ then + Result := 1 + else + Result := 0; +end; + +initialization + +RBDefaultComparor := TQRBComparor.Create; + +finalization + +FreeAndNil(RBDefaultComparor); + +end. diff --git a/source/Base64.pas b/source/Base64.pas new file mode 100644 index 0000000..70db163 --- /dev/null +++ b/source/Base64.pas @@ -0,0 +1,382 @@ +{*******************************************************} +{ } +{ YxdInclude Base64ӽģ } +{ } +{ Ȩ (C) 2013 YangYxd } +{ } +{*******************************************************} + +unit Base64; + +interface + +uses SysUtils, Classes; + +type +{$IFDEF UNICODE} + Base64String = AnsiString; +{$ELSE} + Base64String = string; +{$ENDIF} + +// ԴSourceSizeBase64軺ֽ +function Base64EncodeBufSize(SourceSize: Integer): Integer; +// ȡSourecBase64룬Base64Buf㹻ȡʵʱֽ +function Base64Encode(const Source; SourceSize: Integer; var Base64Buf): Integer; overload; +// SourceΪBase64ַ +function Base64Encode(const Source; SourceSize: Integer): Base64String; overload; +// SourceStartPosʼSizeȵԴΪBase64дDest +// Size=0 ʾһֱ뵽ļβ +procedure Base64Encode(Source, Dest: TStream; StartPos: Int64 = 0; Size: Int64 = 0); overload; +// ַStrΪBase64ַ +{$IFDEF UNICODE} +function StrToBase64(const Str: AnsiString): Base64String; overload; +function StrToBase64(const Str: string): Base64String; overload; +{$ELSE} +function StrToBase64(const Str: string): Base64String; +{$ENDIF} + +// ıԴSourceͳSourceSize㲢ؽ뻺ֽ +function Base64DecodeBufSize(const Base64Source; SourceSize: Integer): Integer; +// Base64ԴBase64Source룬Buf㹻ȡʵʽֽ +function Base64Decode(const Base64Source; SourceSize: Integer; var Buf): Integer; overload; +// SourceStartPosʼSizeȵBase64ݽ룬дDest +// Size=0 ʾһֱ뵽ļβ +procedure Base64Decode(Source, Dest: TStream; StartPos: Int64 = 0; Size: Int64 = 0); overload; +// Base64ԴBase64SourceΪַ +function Base64Decode(const Base64Source; SourceSize: Integer): string; overload; +// Base64ַBase64StrΪַ +function Base64ToStr(const Base64Str: Base64String): string; +// ַתΪUnicodeٱBase64ַ +function StrToUnicodeBase64(const Value: string): string; +function UnicodeBase64ToStr(const Value: string): string; + +function StrBase64ToUNICODE(const Value: string): string; + +implementation + +const + Base64_Chars: array[0..63] of AnsiChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + Base64_Bytes: array[0..79] of Byte = + ( + 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + ); + +type + Base64Proc = function(const Source; SourceSize: Integer; var Buf): Integer; + + +function StrToUnicodeBase64(const Value: string): string; +var + cSize: Integer; + tmp: Byte; + i: Integer; + ppszW: array of Byte; + ww: WideString; +begin + ww := Value; + cSize := length(ww) * 2; + SetLength(ppszW, cSize); + try + Move(ww[1], ppszW[0], cSize); + i := 0; + while i < cSize do begin + tmp := ppszw[i]; + ppszw[i] := ppszw[i + 1]; + ppszw[i + 1] := tmp; + inc(i, 2); + end; + Result := Base64Encode(ppszW[0], High(ppszW)+1); + finally + SetLength(ppszW, 0); + end; +end; + +function StrBase64ToUNICODE(const Value: string): string; +begin + Result := UnicodeBase64ToStr(Value); +end; + +function UnicodeBase64ToStr(const Value: string): string; +var + cSize: Integer; + tmp: Byte; + i: Integer; + ppszW: array of Byte; + ww: WideString; +begin + ww := Value; + cSize := length(ww); + SetLength(ppszW, cSize); + Base64Decode(Value[1], Length(ww), ppszW[0]); + try + i := 0; + while i < High(ppszW) do begin + tmp := ppszw[i]; + ppszw[i] := ppszw[i + 1]; + ppszw[i + 1] := tmp; + inc(i, 2); + end; + Result := Trim(WideString(ppszW)); + finally + SetLength(ppszW, 0); + end; +end; + +procedure Base64Stream(Source, Dest: TStream; Proc: Base64Proc; + StartPos, Size: Int64; RBufSize, WBufSize: Integer); +var + RBuf: array of Byte; + WBuf: array of Byte; + RSize, WSize: Integer; +begin + if (StartPos < 0) or (StartPos >= Source.Size) then Exit; + Source.Position := StartPos; + if (Size <= 0) or (Size > Source.Size - Source.Position) then + Size := Source.Size + else + Size := Size + Source.Position; + SetLength(RBuf, RBufSize); + SetLength(WBuf, WBufSize); + while Size <> Source.Position do + begin + if RBufSize > Size - Source.Position then + RBufSize := Size - Source.Position; + RSize := Source.Read(RBuf[0], RBufSize); + WSize := Proc(RBuf[0], RSize, WBuf[0]); + Dest.Write(WBuf[0], WSize); + end; +end; + +function Base64EncodeBufSize(SourceSize: Integer): Integer; +begin + Result := ((SourceSize + 2) div 3) shl 2; +end; + +(**************************************************************************** +* * +* BASE64 Encode hint: * +* * +* addr: (high) 4 byte 3 byte 2 byte 1 byte (low) * +* sourec ASCII(3 bytes): 33333333 22222222 11111111 * +* bswap: 11111111 22222222 33333333 00000000 * +* b4 = Base64_Chars[(source >> 8) & 63]: [00333333]->44444444 * +* b3 = Base64_Chars[(source >> 14) & 63]: [00222233]->33333333 * +* b2 = Base64_Chars[(source >> 20) & 63]: [00112222]->22222222 * +* b1 = Base64_Chars[source >> 26]: [00111111]->11111111 * +* b4 << 24 b3 << 16 b2 << 8 b1 * +* dest BASE64(4 bytes) 44444444 33333333 22222222 11111111 * +* * +****************************************************************************) + +function Base64Encode(const Source; SourceSize: Integer; var Base64Buf): Integer; +asm + push ebp + push esi + push edi + push ebx + mov esi, eax // esi = Source + mov edi, ecx // edi = Buf + mov eax, edx + cdq + mov ecx, 3 + div ecx // edx = SourceSize % 3 + mov ecx, eax // ecx = SourceSize / 3 + test edx, edx + jz @@1 + inc eax // eax = (SourceSize + 2) / 3 + @@1: + push eax + push edx + lea ebp, Base64_Chars + jecxz @Last + cld + @EncodeLoop: // while (ecx > 0){ + mov edx, [esi] // edx = 00000000 33333333 22222222 11111111 + bswap edx // edx = 11111111 22222222 33333333 00000000 + push edx + push edx + push edx + pop ebx // ebx = edx + shr edx, 20 + shr ebx, 26 // ebx = 00111111 + and edx, 63 // edx = 00112222 + mov ah, [ebp + edx] // *(word*)edi = (Base64_Chars[edx] << 8) | + mov al, [ebp + ebx] // Base64_Chars[ebx] + stosw // edi += 2 + pop edx // edx = 11111111 22222222 33333333 00000000 + pop ebx // ebx = edx + shr edx, 8 + shr ebx, 14 + and edx, 63 // edx = 00333333 + and ebx, 63 // ebx = 00222233 + mov ah, [ebp + edx] // *(word*)edi = (Base64_Chars[edx] << 8) | + mov al, [ebp + ebx] // Base64_Chars[ebx] + stosw // edi += 2 + add esi, 3 // esi += 3 + loop @EncodeLoop // } + @Last: + pop ecx // ecx = SourceSize % 3 + jecxz @end // if (ecx == 0) return + mov eax, 3d3d0000h // preset 2 bytes '=' + mov [edi], eax + test ecx, 2 + jnz @@3 + mov al, [esi] // if (ecx == 1) + shl eax, 4 // eax = *esi << 4 + jmp @@4 + @@3: + mov ax, [esi] // else + xchg al, ah // eax = ((*esi << 8) or *(esi + 1)) << 2 + shl eax, 2 + @@4: + add edi, ecx // edi += ecx + inc ecx // ecx = last encode bytes + @LastLoop: + mov edx, eax // for (; cex > 0; ecx --, edi --) + and edx, 63 // { + mov dl, [ebp + edx] // edx = eax & 63 + mov [edi], dl // *edi = Base64_Chars[edx] + shr eax, 6 // eax >>= 6 + dec edi // } + loop @LastLoop + @end: + pop eax + shl eax, 2 // return encode bytes + pop ebx + pop edi + pop esi + pop ebp +end; + +function Base64Encode(const Source; SourceSize: Integer): Base64String; +begin + SetLength(Result, Base64EncodeBufSize(SourceSize)); + Base64Encode(Source, SourceSize, Result[1]); +end; + +procedure Base64Encode(Source, Dest: TStream; StartPos: Int64; Size: Int64); +begin + Base64Stream(Source, Dest, Base64Encode, StartPos, Size, 6144, 8192); +end; + +{$IFDEF UNICODE} +function StrToBase64(const Str: AnsiString): Base64String; +begin + Result := Base64Encode(Str[1], Length(Str)); +end; + +function StrToBase64(const Str: string): Base64String; +begin + Result := StrToBase64(AnsiString(Str)); +end; +{$ELSE} +function StrToBase64(const Str: string): Base64String; +begin + Result := Base64Encode(Str[1], Length(Str)); +end; +{$ENDIF} + +function Base64DecodeBufSize(const Base64Source; SourceSize: Integer): Integer; +asm + mov ecx, eax // ecx = Source + Size + add ecx, edx + mov eax, edx // eax = Size / 4 * 3 + shr edx, 2 + shr eax, 1 + add eax, edx + mov edx, eax + jz @@2 + @@1: + dec ecx + cmp byte ptr [ecx], 61 + jne @@2 // if (*--ecx == '=') + dec eax // eax -- + jmp @@1 + @@2: // return eax: BufSize; edx: Size / 4 * 3 +end; + +function Base64Decode(const Base64Source; SourceSize: Integer; var Buf): Integer; +asm + push ebp + push esi + push edi + push ebx + mov esi, eax // esi = Source + mov edi, ecx // edi = Buf + mov ebx, edx + call Base64DecodeBufSize + push eax // eax = Base64DecodeBufSize(Source, SourceSize) + sub edx, eax // edx -= eax // edx: '=' count + lea ebp, Base64_Bytes + shr ebx, 2 // ebx = SourceSize / 4 + test ebx, ebx + jz @end + push edx + cld + @DecodeLoop: // for (; ebx > 0; ebx --; edi += 3) + mov ecx, 4 // { + xor eax, eax + @xchgLoop: // for (ecx = 4, eax = 0; ecx > 0; ecx --) + movzx edx, [esi] // { + sub edx, 43 // edx = *(int*)esi - 43 + shl eax, 6 // eax <<= 6 + or al, [ebp + edx]// al |= Base64_Bytes[edx] + inc esi // esi ++ + loop @xchgLoop // } + bswap eax // bswap(eax) + dec ebx // if (ebx == 1) break + jz @Last + shr eax, 8 // eax >>= 8 + stosw // *edi = ax; edi += 2 + shr eax, 16 // eax >>= 16 + stosb // *edi++ = al + jmp @DecodeLoop // } + @Last: + pop ecx + xor ecx, 3 // ecx = last bytes + @LastLoop: // for (; ecx > 0; ecx --) + shr eax, 8 // { + stosb // eax >>= 8; *edi ++ = al + loop @LastLoop // } + @end: + pop eax // return eax + pop ebx + pop edi + pop esi + pop ebp +end; + +procedure Base64Decode(Source, Dest: TStream; StartPos: Int64; Size: Int64); +begin + Base64Stream(Source, Dest, Base64Decode, StartPos, Size, 8192, 6144); +end; + +{$IFDEF UNICODE} +function Base64Decode(const Base64Source; SourceSize: Integer): string; +var + s: AnsiString; +begin + SetLength(s, Base64DecodeBufSize(Base64Source, SourceSize)); + Base64Decode(Base64Source, SourceSize, s[1]); + Result := string(s); +end; +{$ELSE} +function Base64Decode(const Base64Source; SourceSize: Integer): string; +begin + SetLength(Result, Base64DecodeBufSize(Base64Source, SourceSize)); + Base64Decode(Base64Source, SourceSize, Result[1]); +end; +{$ENDIF} + +function Base64ToStr(const Base64Str: Base64String): string; +begin + Result := Base64Decode(Base64Str[1], Length(Base64Str)); +end; + + +end. diff --git a/source/PerlRegEx.pas b/source/PerlRegEx.pas new file mode 100644 index 0000000..60084c7 --- /dev/null +++ b/source/PerlRegEx.pas @@ -0,0 +1,963 @@ +{**************************************************************************************************} +{ } +{ Perl Regular Expressions VCL component } +{ } +{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } +{ you may not use this file except in compliance with the License. You may obtain a copy of the } +{ License at http://www.mozilla.org/MPL/ } +{ } +{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } +{ ANY KIND, either express or implied. See the License for the specific language governing rights } +{ and limitations under the License. } +{ } +{ The Original Code is PerlRegEx.pas. } +{ } +{ The Initial Developer of the Original Code is Jan Goyvaerts. } +{ Portions created by Jan Goyvaerts are Copyright (C) 1999, 2005, 2008, 2010 Jan Goyvaerts. } +{ All rights reserved. } +{ } +{ Design & implementation, by Jan Goyvaerts, 1999, 2005, 2008, 2010 } +{ } +{ TPerlRegEx is available at http://www.regular-expressions.info/delphi.html } +{ } +{**************************************************************************************************} + +unit PerlRegEx; + +interface + +uses + Windows, Messages, SysUtils, Classes, + pcre; +{$HPPEMIT '#pragma comment(lib,"msvcrt.lib")'} +type + TPerlRegExOptions = set of ( + preCaseLess, // /i -> Case insensitive + preMultiLine, // /m -> ^ and $ also match before/after a newline, not just at the beginning and the end of the string + preSingleLine, // /s -> Dot matches any character, including \n (newline). Otherwise, it matches anything except \n + preExtended, // /x -> Allow regex to contain extra whitespace, newlines and Perl-style comments, all of which will be filtered out + preAnchored, // /A -> Successful match can only occur at the start of the subject or right after the previous match + preUnGreedy, // Repeat operators (+, *, ?) are not greedy by default (i.e. they try to match the minimum number of characters instead of the maximum) + preNoAutoCapture // (group) is a non-capturing group; only named groups capture + ); + +type + TPerlRegExState = set of ( + preNotBOL, // Not Beginning Of Line: ^ does not match at the start of Subject + preNotEOL, // Not End Of Line: $ does not match at the end of Subject + preNotEmpty // Empty matches not allowed + ); + +const + // Maximum number of subexpressions (backreferences) + // Subexpressions are created by placing round brackets in the regex, and are referenced by \1, \2, ... + // In Perl, they are available as $1, $2, ... after the regex matched; with TPerlRegEx, use the Subexpressions property + // You can also insert \1, \2, ... in the replacement string; \0 is the complete matched expression + MAX_SUBEXPRESSIONS = 99; + +{$IFDEF UNICODE} +// All implicit string casts have been verified to be correct +{$WARN IMPLICIT_STRING_CAST OFF} +// Use UTF-8 in Delphi 2009 and later, so Unicode strings are handled correctly. +// PCRE does not support UTF-16 +type + PCREString = UTF8String; +{$ELSE UNICODE} +// Use AnsiString in Delphi 2007 and earlier +type + PCREString = AnsiString; +{$ENDIF UNICODE} + +type + TPerlRegExReplaceEvent = procedure(Sender: TObject; var ReplaceWith: PCREString) of object; + +type + TPerlRegEx = class + private // *** Property storage, getters and setters + FCompiled, FStudied: Boolean; + FOptions: TPerlRegExOptions; + FState: TPerlRegExState; + FRegEx, FReplacement, FSubject: PCREString; + FStart, FStop: Integer; + FOnMatch: TNotifyEvent; + FOnReplace: TPerlRegExReplaceEvent; + function GetMatchedText: PCREString; + function GetMatchedLength: Integer; + function GetMatchedOffset: Integer; + procedure SetOptions(Value: TPerlRegExOptions); + procedure SetRegEx(const Value: PCREString); + function GetGroupCount: Integer; + function GetGroups(Index: Integer): PCREString; + function GetGroupLengths(Index: Integer): Integer; + function GetGroupOffsets(Index: Integer): Integer; + procedure SetSubject(const Value: PCREString); + procedure SetStart(const Value: Integer); + procedure SetStop(const Value: Integer); + function GetFoundMatch: Boolean; + private // *** Variables used by PCRE + Offsets: array[0..(MAX_SUBEXPRESSIONS+1)*3] of Integer; + OffsetCount: Integer; + pcreOptions: Integer; + pattern, hints, chartable: Pointer; + FSubjectPChar: PAnsiChar; + FHasStoredGroups: Boolean; + FStoredGroups: array of PCREString; + function GetSubjectLeft: PCREString; + function GetSubjectRight: PCREString; + protected + procedure CleanUp; + // Dispose off whatever we created, so we can start over. Called automatically when needed, so it is not made public + procedure ClearStoredGroups; + public + constructor Create; + // Come to life + destructor Destroy; override; + // Clean up after ourselves + class function EscapeRegExChars(const S: string): string; + // Escapes regex characters in S so that the regex engine can be used to match S as plain text + procedure Compile; + // Compile the regex. Called automatically by Match + procedure Study; + // Study the regex. Studying takes time, but will make the execution of the regex a lot faster. + // Call study if you will be using the same regex many times + function Match: Boolean; + // Attempt to match the regex, starting the attempt from the beginning of Subject + function MatchAgain: Boolean; + // Attempt to match the regex to the remainder of Subject after the previous match (as indicated by Start) + function Replace: PCREString; + // Replace matched expression in Subject with ComputeReplacement. Returns the actual replacement text from ComputeReplacement + function ReplaceAll: Boolean; + // Repeat MatchAgain and Replace until you drop. Returns True if anything was replaced at all. + function ComputeReplacement: PCREString; + // Returns Replacement with backreferences filled in + procedure StoreGroups; + // Stores duplicates of Groups[] so they and ComputeReplacement will still return the proper strings + // even if FSubject is changed or cleared + function NamedGroup(const Name: PCREString): Integer; + // Returns the index of the named group Name + procedure Split(Strings: TStrings; Limit: Integer); + // Split Subject along regex matches. Capturing groups are ignored. + procedure SplitCapture(Strings: TStrings; Limit: Integer); overload; + procedure SplitCapture(Strings: TStrings; Limit: Integer; Offset: Integer); overload; + // Split Subject along regex matches. Capturing groups are added to Strings as well. + property Compiled: Boolean read FCompiled; + // True if the RegEx has already been compiled. + property FoundMatch: Boolean read GetFoundMatch; + // Returns True when Matched* and Group* indicate a match + property Studied: Boolean read FStudied; + // True if the RegEx has already been studied + property MatchedText: PCREString read GetMatchedText; + // The matched text + property MatchedLength: Integer read GetMatchedLength; + // Length of the matched text + property MatchedOffset: Integer read GetMatchedOffset; + // Character offset in the Subject string at which MatchedText starts + property Start: Integer read FStart write SetStart; + // Starting position in Subject from which MatchAgain begins + property Stop: Integer read FStop write SetStop; + // Last character in Subject that Match and MatchAgain search through + property State: TPerlRegExState read FState write FState; + // State of Subject + property GroupCount: Integer read GetGroupCount; + // Number of matched capturing groups + property Groups[Index: Integer]: PCREString read GetGroups; + // Text matched by capturing groups + property GroupLengths[Index: Integer]: Integer read GetGroupLengths; + // Lengths of the text matched by capturing groups + property GroupOffsets[Index: Integer]: Integer read GetGroupOffsets; + // Character offsets in Subject at which the capturing group matches start + property Subject: PCREString read FSubject write SetSubject; + // The string on which Match() will try to match RegEx + property SubjectLeft: PCREString read GetSubjectLeft; + // Part of the subject to the left of the match + property SubjectRight: PCREString read GetSubjectRight; + // Part of the subject to the right of the match + public + property Options: TPerlRegExOptions read FOptions write SetOptions; + // Options + property RegEx: PCREString read FRegEx write SetRegEx; + // The regular expression to be matched + property Replacement: PCREString read FReplacement write FReplacement; + // Text to replace matched expression with. \number and $number backreferences will be substituted with Groups + // TPerlRegEx supports the "JGsoft" replacement text flavor as explained at http://www.regular-expressions.info/refreplace.html + property OnMatch: TNotifyEvent read FOnMatch write FOnMatch; + // Triggered by Match and MatchAgain after a successful match + property OnReplace: TPerlRegExReplaceEvent read FOnReplace write FOnReplace; + // Triggered by Replace and ReplaceAll just before the replacement is done, allowing you to determine the new PCREString + end; + +{ + You can add TPerlRegEx instances to a TPerlRegExList to match them all together on the same subject, + as if they were one regex regex1|regex2|regex3|... + TPerlRegExList does not own the TPerlRegEx components, just like a TList + If a TPerlRegEx has been added to a TPerlRegExList, it should not be used in any other situation + until it is removed from the list +} + +type + TPerlRegExList = class + private + FList: TList; + FSubject: PCREString; + FMatchedRegEx: TPerlRegEx; + FStart, FStop: Integer; + function GetRegEx(Index: Integer): TPerlRegEx; + procedure SetRegEx(Index: Integer; Value: TPerlRegEx); + procedure SetSubject(const Value: PCREString); + procedure SetStart(const Value: Integer); + procedure SetStop(const Value: Integer); + function GetCount: Integer; + protected + procedure UpdateRegEx(ARegEx: TPerlRegEx); + public + constructor Create; + destructor Destroy; override; + public + function Add(ARegEx: TPerlRegEx): Integer; + procedure Clear; + procedure Delete(Index: Integer); + function IndexOf(ARegEx: TPerlRegEx): Integer; + procedure Insert(Index: Integer; ARegEx: TPerlRegEx); + public + function Match: Boolean; + function MatchAgain: Boolean; + property RegEx[Index: Integer]: TPerlRegEx read GetRegEx write SetRegEx; + property Count: Integer read GetCount; + property Subject: PCREString read FSubject write SetSubject; + property Start: Integer read FStart write SetStart; + property Stop: Integer read FStop write SetStop; + property MatchedRegEx: TPerlRegEx read FMatchedRegEx; + end; + +implementation + + + { ********* Unit support routines ********* } + +function FirstCap(const S: string): string; +begin + if S = '' then Result := '' + else begin + Result := AnsiLowerCase(S); + {$IFDEF UNICODE} + CharUpperBuffW(@Result[1], 1); + {$ELSE} + CharUpperBuffA(@Result[1], 1); + {$ENDIF} + end +end; + +function InitialCaps(const S: string): string; +var + I: Integer; + Up: Boolean; +begin + Result := AnsiLowerCase(S); + Up := True; +{$IFDEF UNICODE} + for I := 1 to Length(Result) do begin + case Result[I] of + #0..'&', '(', '*', '+', ',', '-', '.', '?', '<', '[', '{', #$00B7: + Up := True + else + if Up and (Result[I] <> '''') then begin + CharUpperBuffW(@Result[I], 1); + Up := False + end + end; + end; +{$ELSE UNICODE} + if SysLocale.FarEast then begin + I := 1; + while I <= Length(Result) do begin + if Result[I] in LeadBytes then begin + Inc(I, 2) + end + else begin + if Result[I] in [#0..'&', '('..'.', '?', '<', '[', '{'] then Up := True + else if Up and (Result[I] <> '''') then begin + CharUpperBuffA(@Result[I], 1); + Result[I] := UpperCase(Result[I])[1]; + Up := False + end; + Inc(I) + end + end + end + else + for I := 1 to Length(Result) do begin + if Result[I] in [#0..'&', '('..'.', '?', '<', '[', '{', #$B7] then Up := True + else if Up and (Result[I] <> '''') then begin + CharUpperBuffA(@Result[I], 1); + Result[I] := AnsiUpperCase(Result[I])[1]; + Up := False + end + end; +{$ENDIF UNICODE} +end; + + + { ********* TPerlRegEx component ********* } + +procedure TPerlRegEx.CleanUp; +begin + FCompiled := False; FStudied := False; + pcre_dispose(pattern, hints, nil); + pattern := nil; + hints := nil; + ClearStoredGroups; + OffsetCount := 0; +end; + +procedure TPerlRegEx.ClearStoredGroups; +begin + FHasStoredGroups := False; + FStoredGroups := nil; +end; + +procedure TPerlRegEx.Compile; +var + Error: PAnsiChar; + ErrorOffset: Integer; +begin + if FRegEx = '' then + raise Exception.Create('TPerlRegEx.Compile() - Please specify a regular expression in RegEx first'); + CleanUp; + Pattern := pcre_compile(PAnsiChar(FRegEx), pcreOptions, @Error, @ErrorOffset, chartable); + if Pattern = nil then + raise Exception.Create(Format('TPerlRegEx.Compile() - Error in regex at offset %d: %s', [ErrorOffset, AnsiString(Error)])); + FCompiled := True +end; + +(* Backreference overview: + +Assume there are 13 backreferences: + +Text TPerlRegex .NET Java ECMAScript +$17 $1 + "7" "$17" $1 + "7" $1 + "7" +$017 $1 + "7" "$017" $1 + "7" $1 + "7" +$12 $12 $12 $12 $12 +$012 $1 + "2" $12 $12 $1 + "2" +${1}2 $1 + "2" $1 + "2" error "${1}2" +$$ "$" "$" error "$" +\$ "$" "\$" "$" "\$" +*) + +function TPerlRegEx.ComputeReplacement: PCREString; +var + Mode: AnsiChar; + S: PCREString; + I, J, N: Integer; + + procedure ReplaceBackreference(Number: Integer); + var + Backreference: PCREString; + begin + Delete(S, I, J-I); + if Number <= GroupCount then begin + Backreference := Groups[Number]; + if Backreference <> '' then begin + // Ignore warnings; converting to UTF-8 does not cause data loss + case Mode of + 'L', 'l': Backreference := AnsiLowerCase(Backreference); + 'U', 'u': Backreference := AnsiUpperCase(Backreference); + 'F', 'f': Backreference := FirstCap(Backreference); + 'I', 'i': Backreference := InitialCaps(Backreference); + end; + if S <> '' then begin + Insert(Backreference, S, I); + I := I + Length(Backreference); + end + else begin + S := Backreference; + I := MaxInt; + end + end; + end + end; + + procedure ProcessBackreference(NumberOnly, Dollar: Boolean); + var + Number, Number2: Integer; + Group: PCREString; + begin + Number := -1; + if (J <= Length(S)) and (S[J] in ['0'..'9']) then begin + // Get the number of the backreference + Number := Ord(S[J]) - Ord('0'); + Inc(J); + if (J <= Length(S)) and (S[J] in ['0'..'9']) then begin + // Expand it to two digits only if that would lead to a valid backreference + Number2 := Number*10 + Ord(S[J]) - Ord('0'); + if Number2 <= GroupCount then begin + Number := Number2; + Inc(J) + end; + end; + end + else if not NumberOnly then begin + if Dollar and (J < Length(S)) and (S[J] = '{') then begin + // Number or name in curly braces + Inc(J); + case S[J] of + '0'..'9': begin + Number := Ord(S[J]) - Ord('0'); + Inc(J); + while (J <= Length(S)) and (S[J] in ['0'..'9']) do begin + Number := Number*10 + Ord(S[J]) - Ord('0'); + Inc(J) + end; + end; + 'A'..'Z', 'a'..'z', '_': begin + Inc(J); + while (J <= Length(S)) and (S[J] in ['A'..'Z', 'a'..'z', '0'..'9', '_']) do Inc(J); + if (J <= Length(S)) and (S[J] = '}') then begin + Group := Copy(S, I+2, J-I-2); + Number := NamedGroup(Group); + end + end; + end; + if (J > Length(S)) or (S[J] <> '}') then Number := -1 + else Inc(J) + end + else if Dollar and (S[J] = '_') then begin + // $_ (whole subject) + Delete(S, I, J+1-I); + Insert(Subject, S, I); + I := I + Length(Subject); + Exit; + end + else case S[J] of + '&': begin + // \& or $& (whole regex match) + Number := 0; + Inc(J); + end; + '+': begin + // \+ or $+ (highest-numbered participating group) + Number := GroupCount; + Inc(J); + end; + '`': begin + // \` or $` (backtick; subject to the left of the match) + Delete(S, I, J+1-I); + Insert(SubjectLeft, S, I); + I := I + Offsets[0] - 1; + Exit; + end; + '''': begin + // \' or $' (straight quote; subject to the right of the match) + Delete(S, I, J+1-I); + Insert(SubjectRight, S, I); + I := I + Length(Subject) - Offsets[1]; + Exit; + end + end; + end; + if Number >= 0 then ReplaceBackreference(Number) + else Inc(I) + end; + +begin + S := FReplacement; + I := 1; + while I < Length(S) do begin + case S[I] of + '\': begin + J := I + 1; + Assert(J <= Length(S), 'CHECK: We let I stop one character before the end, so J cannot point beyond the end of the PCREString here'); + case S[J] of + '$', '\': begin + Delete(S, I, 1); + Inc(I); + end; + 'g': begin + if (J < Length(S)-1) and (S[J+1] = '<') and (S[J+2] in ['A'..'Z', 'a'..'z', '_']) then begin + // Python-style named group reference \g + J := J+3; + while (J <= Length(S)) and (S[J] in ['0'..'9', 'A'..'Z', 'a'..'z', '_']) do Inc(J); + if (J <= Length(S)) and (S[J] = '>') then begin + N := NamedGroup(Copy(S, I+3, J-I-3)); + Inc(J); + Mode := #0; + if N > 0 then ReplaceBackreference(N) + else Delete(S, I, J-I) + end + else I := J + end + else I := I+2; + end; + 'l', 'L', 'u', 'U', 'f', 'F', 'i', 'I': begin + Mode := S[J]; + Inc(J); + ProcessBackreference(True, False); + end; + else begin + Mode := #0; + ProcessBackreference(False, False); + end; + end; + end; + '$': begin + J := I + 1; + Assert(J <= Length(S), 'CHECK: We let I stop one character before the end, so J cannot point beyond the end of the PCREString here'); + if S[J] = '$' then begin + Delete(S, J, 1); + Inc(I); + end + else begin + Mode := #0; + ProcessBackreference(False, True); + end + end; + else Inc(I) + end + end; + Result := S +end; + +constructor TPerlRegEx.Create; +begin + inherited Create; + FState := [preNotEmpty]; + chartable := pcre_maketables; +{$IFDEF UNICODE} + pcreOptions := PCRE_UTF8 or PCRE_NEWLINE_ANY; +{$ELSE} + pcreOptions := PCRE_NEWLINE_ANY; +{$ENDIF} +end; + +destructor TPerlRegEx.Destroy; +begin + pcre_dispose(pattern, hints, chartable); + inherited Destroy; +end; + +class function TPerlRegEx.EscapeRegExChars(const S: string): string; +var + I: Integer; +begin + Result := S; + I := Length(Result); + while I > 0 do begin + case Result[I] of + '.', '[', ']', '(', ')', '?', '*', '+', '{', '}', '^', '$', '|', '\': + Insert('\', Result, I); + #0: begin + Result[I] := '0'; + Insert('\', Result, I); + end; + end; + Dec(I); + end; +end; + +function TPerlRegEx.GetFoundMatch: Boolean; +begin + Result := OffsetCount > 0; +end; + +function TPerlRegEx.GetMatchedText: PCREString; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + Result := GetGroups(0); +end; + +function TPerlRegEx.GetMatchedLength: Integer; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + Result := GetGroupLengths(0) +end; + +function TPerlRegEx.GetMatchedOffset: Integer; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + Result := GetGroupOffsets(0) +end; + +function TPerlRegEx.GetGroupCount: Integer; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + Result := OffsetCount-1 +end; + +function TPerlRegEx.GetGroupLengths(Index: Integer): Integer; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + Assert((Index >= 0) and (Index <= GroupCount), 'REQUIRE: Index <= GroupCount'); + Result := Offsets[Index*2+1]-Offsets[Index*2] +end; + +function TPerlRegEx.GetGroupOffsets(Index: Integer): Integer; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + Assert((Index >= 0) and (Index <= GroupCount), 'REQUIRE: Index <= GroupCount'); + Result := Offsets[Index*2] +end; + +function TPerlRegEx.GetGroups(Index: Integer): PCREString; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + if Index > GroupCount then Result := '' + else if FHasStoredGroups then Result := FStoredGroups[Index] + else Result := Copy(FSubject, Offsets[Index*2], Offsets[Index*2+1]-Offsets[Index*2]); +end; + +function TPerlRegEx.GetSubjectLeft: PCREString; +begin + Result := Copy(Subject, 1, Offsets[0]-1); +end; + +function TPerlRegEx.GetSubjectRight: PCREString; +begin + Result := Copy(Subject, Offsets[1], MaxInt); +end; + +function TPerlRegEx.Match: Boolean; +var + I, Opts: Integer; +begin + ClearStoredGroups; + if not Compiled then Compile; + if preNotBOL in State then Opts := PCRE_NOTBOL else Opts := 0; + if preNotEOL in State then Opts := Opts or PCRE_NOTEOL; + if preNotEmpty in State then Opts := Opts or PCRE_NOTEMPTY; + OffsetCount := pcre_exec(Pattern, Hints, FSubjectPChar, FStop, 0, Opts, @Offsets[0], High(Offsets)); + Result := OffsetCount > 0; + // Convert offsets into PCREString indices + if Result then begin + for I := 0 to OffsetCount*2-1 do + Inc(Offsets[I]); + FStart := Offsets[1]; + if Offsets[0] = Offsets[1] then Inc(FStart); // Make sure we don't get stuck at the same position + if Assigned(OnMatch) then OnMatch(Self) + end; +end; + +function TPerlRegEx.MatchAgain: Boolean; +var + I, Opts: Integer; +begin + ClearStoredGroups; + if not Compiled then Compile; + if preNotBOL in State then Opts := PCRE_NOTBOL else Opts := 0; + if preNotEOL in State then Opts := Opts or PCRE_NOTEOL; + if preNotEmpty in State then Opts := Opts or PCRE_NOTEMPTY; + if FStart-1 > FStop then OffsetCount := -1 + else OffsetCount := pcre_exec(Pattern, Hints, FSubjectPChar, FStop, FStart-1, Opts, @Offsets[0], High(Offsets)); + Result := OffsetCount > 0; + // Convert offsets into PCREString indices + if Result then begin + for I := 0 to OffsetCount*2-1 do + Inc(Offsets[I]); + FStart := Offsets[1]; + if Offsets[0] = Offsets[1] then Inc(FStart); // Make sure we don't get stuck at the same position + if Assigned(OnMatch) then OnMatch(Self) + end; +end; + +function TPerlRegEx.NamedGroup(const Name: PCREString): Integer; +begin + Result := pcre_get_stringnumber(Pattern, PAnsiChar(Name)); +end; + +function TPerlRegEx.Replace: PCREString; +begin + Assert(FoundMatch, 'REQUIRE: There must be a successful match first'); + // Substitute backreferences + Result := ComputeReplacement; + // Allow for just-in-time substitution determination + if Assigned(OnReplace) then OnReplace(Self, Result); + // Perform substitution + Delete(FSubject, MatchedOffset, MatchedLength); + if Result <> '' then Insert(Result, FSubject, MatchedOffset); + FSubjectPChar := PAnsiChar(FSubject); + // Position to continue search + FStart := FStart - MatchedLength + Length(Result); + FStop := FStop - MatchedLength + Length(Result); + // Replacement no longer matches regex, we assume + ClearStoredGroups; + OffsetCount := 0; +end; + +function TPerlRegEx.ReplaceAll: Boolean; +begin + if Match then begin + Result := True; + repeat + Replace + until not MatchAgain; + end + else Result := False; +end; + +procedure TPerlRegEx.SetOptions(Value: TPerlRegExOptions); +begin + if (FOptions <> Value) then begin + FOptions := Value; + {$IFDEF UNICODE} + pcreOptions := PCRE_UTF8 or PCRE_NEWLINE_ANY; + {$ELSE} + pcreOptions := PCRE_NEWLINE_ANY; + {$ENDIF} + if (preCaseLess in Value) then pcreOptions := pcreOptions or PCRE_CASELESS; + if (preMultiLine in Value) then pcreOptions := pcreOptions or PCRE_MULTILINE; + if (preSingleLine in Value) then pcreOptions := pcreOptions or PCRE_DOTALL; + if (preExtended in Value) then pcreOptions := pcreOptions or PCRE_EXTENDED; + if (preAnchored in Value) then pcreOptions := pcreOptions or PCRE_ANCHORED; + if (preUnGreedy in Value) then pcreOptions := pcreOptions or PCRE_UNGREEDY; + if (preNoAutoCapture in Value) then pcreOptions := pcreOptions or PCRE_NO_AUTO_CAPTURE; + CleanUp + end +end; + +procedure TPerlRegEx.SetRegEx(const Value: PCREString); +begin + if FRegEx <> Value then begin + FRegEx := Value; + CleanUp + end +end; + +procedure TPerlRegEx.SetStart(const Value: Integer); +begin + if Value < 1 then FStart := 1 + else FStart := Value; + // If FStart > Length(Subject), MatchAgain() will simply return False +end; + +procedure TPerlRegEx.SetStop(const Value: Integer); +begin + if Value > Length(Subject) then FStop := Length(Subject) + else FStop := Value; +end; + +procedure TPerlRegEx.SetSubject(const Value: PCREString); +begin + FSubject := Value; + FSubjectPChar := PAnsiChar(Value); + FStart := 1; + FStop := Length(Subject); + if not FHasStoredGroups then OffsetCount := 0; +end; + +procedure TPerlRegEx.Split(Strings: TStrings; Limit: Integer); +var + Offset, Count: Integer; +begin + Assert(Strings <> nil, 'REQUIRE: Strings'); + if (Limit = 1) or not Match then Strings.Add(Subject) + else begin + Offset := 1; + Count := 1; + repeat + Strings.Add(Copy(Subject, Offset, MatchedOffset - Offset)); + Inc(Count); + Offset := MatchedOffset + MatchedLength; + until ((Limit > 1) and (Count >= Limit)) or not MatchAgain; + Strings.Add(Copy(Subject, Offset, MaxInt)); + end +end; + +procedure TPerlRegEx.SplitCapture(Strings: TStrings; Limit, Offset: Integer); +var + Count: Integer; + bUseOffset : boolean; + iOffset : integer; +begin + Assert(Strings <> nil, 'REQUIRE: Strings'); + if (Limit = 1) or not Match then Strings.Add(Subject) + else + begin + bUseOffset := Offset <> 1; + if Offset <> 1 then + Dec(Limit); + iOffset := 1; + Count := 1; + repeat + if bUseOffset then + begin + if MatchedOffset >= Offset then + begin + bUseOffset := False; + Strings.Add(Copy(Subject, 1, MatchedOffset -1)); + if Self.GroupCount > 0 then + Strings.Add(Self.Groups[Self.GroupCount]); + end; + end + else + begin + Strings.Add(Copy(Subject, iOffset, MatchedOffset - iOffset)); + Inc(Count); + if Self.GroupCount > 0 then + Strings.Add(Self.Groups[Self.GroupCount]); + end; + iOffset := MatchedOffset + MatchedLength; + until ((Limit > 1) and (Count >= Limit)) or not MatchAgain; + Strings.Add(Copy(Subject, iOffset, MaxInt)); + end +end; + +procedure TPerlRegEx.SplitCapture(Strings: TStrings; Limit: Integer); +begin + SplitCapture(Strings,Limit,1); +end; + +procedure TPerlRegEx.StoreGroups; +var + I: Integer; +begin + if OffsetCount > 0 then begin + ClearStoredGroups; + SetLength(FStoredGroups, GroupCount+1); + for I := GroupCount downto 0 do + FStoredGroups[I] := Groups[I]; + FHasStoredGroups := True; + end +end; + +procedure TPerlRegEx.Study; +var + Error: PAnsiChar; +begin + if not FCompiled then Compile; + Hints := pcre_study(Pattern, 0, @Error); + if Error <> nil then + raise Exception.Create('TPerlRegEx.Study() - Error studying the regex: ' + AnsiString(Error)); + FStudied := True +end; + +{ TPerlRegExList } + +function TPerlRegExList.Add(ARegEx: TPerlRegEx): Integer; +begin + Result := FList.Add(ARegEx); + UpdateRegEx(ARegEx); +end; + +procedure TPerlRegExList.Clear; +begin + FList.Clear; +end; + +constructor TPerlRegExList.Create; +begin + inherited Create; + FList := TList.Create; +end; + +procedure TPerlRegExList.Delete(Index: Integer); +begin + FList.Delete(Index); +end; + +destructor TPerlRegExList.Destroy; +begin + FList.Free; + inherited +end; + +function TPerlRegExList.GetCount: Integer; +begin + Result := FList.Count; +end; + +function TPerlRegExList.GetRegEx(Index: Integer): TPerlRegEx; +begin + Result := TPerlRegEx(Pointer(FList[Index])); +end; + +function TPerlRegExList.IndexOf(ARegEx: TPerlRegEx): Integer; +begin + Result := FList.IndexOf(ARegEx); +end; + +procedure TPerlRegExList.Insert(Index: Integer; ARegEx: TPerlRegEx); +begin + FList.Insert(Index, ARegEx); + UpdateRegEx(ARegEx); +end; + +function TPerlRegExList.Match: Boolean; +begin + SetStart(1); + FMatchedRegEx := nil; + Result := MatchAgain; +end; + +function TPerlRegExList.MatchAgain: Boolean; +var + I, MatchStart, MatchPos: Integer; + ARegEx: TPerlRegEx; +begin + if FMatchedRegEx <> nil then + MatchStart := FMatchedRegEx.MatchedOffset + FMatchedRegEx.MatchedLength + else + MatchStart := FStart; + FMatchedRegEx := nil; + MatchPos := MaxInt; + for I := 0 to Count-1 do begin + ARegEx := RegEx[I]; + if (not ARegEx.FoundMatch) or (ARegEx.MatchedOffset < MatchStart) then begin + ARegEx.Start := MatchStart; + ARegEx.MatchAgain; + end; + if ARegEx.FoundMatch and (ARegEx.MatchedOffset < MatchPos) then begin + MatchPos := ARegEx.MatchedOffset; + FMatchedRegEx := ARegEx; + end; + if MatchPos = MatchStart then Break; + end; + Result := MatchPos < MaxInt; +end; + +procedure TPerlRegExList.SetRegEx(Index: Integer; Value: TPerlRegEx); +begin + FList[Index] := Value; + UpdateRegEx(Value); +end; + +procedure TPerlRegExList.SetStart(const Value: Integer); +var + I: Integer; +begin + if FStart <> Value then begin + FStart := Value; + for I := Count-1 downto 0 do + RegEx[I].Start := Value; + FMatchedRegEx := nil; + end; +end; + +procedure TPerlRegExList.SetStop(const Value: Integer); +var + I: Integer; +begin + if FStop <> Value then begin + FStop := Value; + for I := Count-1 downto 0 do + RegEx[I].Stop := Value; + FMatchedRegEx := nil; + end; +end; + +procedure TPerlRegExList.SetSubject(const Value: PCREString); +var + I: Integer; +begin + if FSubject <> Value then begin + FSubject := Value; + for I := Count-1 downto 0 do + RegEx[I].Subject := Value; + FMatchedRegEx := nil; + end; +end; + +procedure TPerlRegExList.UpdateRegEx(ARegEx: TPerlRegEx); +begin + ARegEx.Subject := FSubject; + ARegEx.Start := FStart; +end; + +end. diff --git a/source/YxdAdoStream.pas b/source/YxdAdoStream.pas new file mode 100644 index 0000000..b48d073 --- /dev/null +++ b/source/YxdAdoStream.pas @@ -0,0 +1,78 @@ +{*******************************************************} +{ } +{ ADO } +{ } +{ Ȩ (C) 2013 YangYxd } +{ } +{*******************************************************} + +unit YxdAdoStream; + +interface + +uses + Windows, Classes, Sysutils, comobj, ActiveX, ole2, adoint, adodb; + +/// +/// мݼ +/// +procedure StreamToDataSet(AStream: TStream; ADataSet: TCustomADODataSet); +/// +/// ݼд +/// +procedure DataSetToStream(ADataSet: TCustomADODataSet; AStream: TStream); + +implementation + +procedure DataSetToStream(ADataSet:TCustomADODataSet; AStream:TStream); +var + ATemp: TStreamAdapter; + ADataSetStream: IPersistStream; + AIntf: IStream; + ARecs: OleVariant; + ASet: _Recordset; +begin + ASet := ADataSet.Recordset; + while (ASet.State = adStateClosed) do begin //ִд洢һĽű,ܴڶ + ASet := ADataSet.Recordset.NextRecordset(ARecs); + if ASet = nil then + raise Exception.Create('ݼ'); + end; + OleCheck(ASet.QueryInterface(System.PGuid(@IID_IPersistStream)^, ADataSetStream)); + ATemp := TStreamAdapter.Create(AStream); + try + ATemp.GetInterface(System.PGuid(@IID_IStream)^, AIntf); + OleCheck(OleSaveToStream(ADataSetStream, AIntf)); + finally + ASet._Release; + ATemp.FreeInstance; + AIntf := nil; + end; +end; + +procedure StreamToDataSet(AStream:TStream; ADataSet: TCustomADODataSet); +var + ATemp: Classes.TStreamAdapter; + ARecordSet: ADOInt.Recordset; + AIntf: IStream; +begin + ATemp := Classes.TStreamAdapter.Create(AStream); + try + ADataSet.LockType := ltBatchOptimistic; + ADataSet.Recordset := nil; + try + ATemp.GetInterface(System.PGuid(@IID_IStream)^, AIntf); + ComObj.OleCheck(Ole2.OleLoadFromStream(AIntf, + Ole2.PGuid(@AdoInt.IID__Recordset)^, ARecordset)); + ADataSet.Recordset := ARecordSet; + except + OutputDebugString(PChar(Exception(ExceptObject).Message)); + end; + finally + ATemp.FreeInstance; + AIntf := nil; + end; +end; + +end. +r \ No newline at end of file diff --git a/source/YxdJson.pas b/source/YxdJson.pas new file mode 100644 index 0000000..f237369 --- /dev/null +++ b/source/YxdJson.pas @@ -0,0 +1,6179 @@ +{*******************************************************} +{ } +{ YxdJSON Library } +{ } +{ Ȩ (C) 2014 YangYxd, Swish } +{ } +{*******************************************************} +{ + ---------------------------------------------------------------- + ˵ + ---------------------------------------------------------------- + YXDJSONswishQJSON޸ģлswishлQJson + QJsonQDACĿȨswish(QQ:109867294) + лѵֺ֧֣롢è + QDACٷȺ250530692 + + -------------------------------------------------------------------- + ¼¼ + -------------------------------------------------------------------- + + ver 1.0.15 2015.09.01 + -------------------------------------------------------------------- + + SuperJSON ʹ÷ʽ + + ver 1.0.14 2015.07.15 + -------------------------------------------------------------------- + - ParseObjectByName һBUG RE: ҹɱ֣ + + ver 1.0.13 2015.06.09 + -------------------------------------------------------------------- + - ParseStringByName һBUG RE: ҹɱ֣ + + ver 1.0.11 2014.12.08 + -------------------------------------------------------------------- + - ParseNumericڽʱδмɽĽַ + ʽһɵ + + ver 1.0.10 2014.11.12 + -------------------------------------------------------------------- + - ĽJSONBaseSetNameĽPut(Key, JSONObject/JSONArray) + DestroỵʵֵJSONBase޸ʱҲЧ + + ver 1.0.9 2014.11.08 + -------------------------------------------------------------------- + - ޸עʹBUG + - ޸XE汾δUSERTTIѡʱ벻ͨ + - ĽFloatToStr + - IsJSONObjectIsJSONArrayжJSONBaseJSON + + ver 1.0.8 2014.08.05 + -------------------------------------------------------------------- + - ĸʽjdtObjectĻ. + - ŻĻڵJsonObject⡣ + - ֧DataSetл뷴лUSEDBRTTI뿪ء + - "@[]ʵXE[]й"BUG. + + ver 1.0.6 2014.08.01 + -------------------------------------------------------------------- + - RTTI ֧֣USERTTIѡ(YxdRttiԪ) + - ƽ̨֧֣FMXܣ֧Win32, Android + - Copy, CopyIf, FindIf, DeleteIf, ForcePath, ItemByPathȺ + - ֧For..Inܡ + - ຯParseObject(TObject) + - getVariantǷزNULL⣨RE: й죩 + + ver 1.0.5 2014.07.24 + -------------------------------------------------------------------- + - Next ظڵڵһJSONֵ + - JSONValue ֵ͵ĴʽputgetFloat + ⡣ + - parseStringByName ȡjsonַָ + keyֵַ + - parseObjectByName ຯjsonַ + + ver 1.0.2 2014.07.15 + -------------------------------------------------------------------- + - Ż ^_^ + + ver 1.0.1 2014.07.13 + -------------------------------------------------------------------- + - XE6֧ + + -------------------------------------------------------------------- +} + +unit YxdJson; + +interface + +(* ܿѡ *) +{$DEFINE USEYxdStr} // ǷʹYxdStrԪ +{$DEFINE USERTTI} // ǷʹRTTI +{$DEFINE USERegEx} // ǷʹʽܣD2010֮ǰ汾ҪصԪ +{$IFDEF USERTTI} +{$DEFINE USEDBRTTI} // ǷʹDataSetлܣUSERTTI +{$ENDIF} + +(* Delphi 汾 *) +//Delphi 2007 +{$IFDEF VER185} +{$DEFINE JSON_SUPPORT} +{$ENDIF} + +//Delphi XE +{$IFDEF VER220} +{$DEFINE JSON_SUPPORT} +{$DEFINE JSON_UNICODE} +{$IFDEF USERTTI} +{$DEFINE JSON_RTTI} +{$ENDIF} +{$ENDIF} + +//Rad Studio XE6 +{$IFDEF VER270} +{$DEFINE JSON_SUPPORT} +{$DEFINE JSON_UNICODE} +{$IFDEF USERTTI} +{$DEFINE JSON_RTTI} +{$DEFINE JSON_RTTI_NAMEFIELD} +{$ENDIF} +{$ENDIF} + +{$IFNDEF JSON_SUPPORT} +{$MESSAGE WARN '!!!JSON Only test in 2007 and XE6,No support in other version!!!'} +{$ENDIF} + +uses + {$IFDEF USEYxdStr}YxdStr, {$ENDIF} + {$IFNDEF JSON_UNICODE}Windows, {$ELSE} {$IFDEF MSWINDOWS}Windows, {$ENDIF}{$ENDIF} + {$IFDEF USEDBRTTI}DB, {$ENDIF} + {$IFDEF JSON_UNICODE}Generics.Collections, {$ENDIF} + {$IFDEF USERTTI}{$IFDEF JSON_RTTI}{$IFDEF JSON_UNICODE}Rtti, {$ENDIF}{$ENDIF}TypInfo, {$ENDIF} + {$IF (RTLVersion>=26) and (not Defined(NEXTGEN))}AnsiStrings, {$IFEND} + {$IFDEF USERegEx}{$IF RTLVersion<22}{2007-2010}PerlRegEx, pcre, {$ELSE}RegularExpressionsCore, {$IFEND}{$ENDIF} + SysUtils, Classes, Variants, Math, DateUtils; + +type + {$IFDEF JSON_UNICODE} + JSONStringW = UnicodeString; + JSONString = JSONStringW; + {$ELSE} + JSONStringW = WideString; + {$ENDIF} + {$IFNDEF USEYxdStr}{$IFDEF NEXTGEN} + AnsiChar = Byte; + PAnsiChar = ^AnsiChar; + WideString = UnicodeString; + AnsiString = record + private + FValue:TBytes; + function GetChars(AIndex: Integer): AnsiChar; + procedure SetChars(AIndex: Integer; const Value: AnsiChar); + function GetLength:Integer; + procedure SetLength(const Value: Integer); + function GetIsUtf8: Boolean; + public + class operator Implicit(const S:WideString):AnsiString; + class operator Implicit(const S:AnsiString):PAnsiChar; + class operator Implicit(const S:AnsiString):TBytes; + class operator Implicit(const ABytes:TBytes):AnsiString; + class operator Implicit(const S:AnsiString):JSONStringW; + //class operator Implicit(const S:PAnsiChar):AnsiString; + //ַȽ + procedure From(p:PAnsiChar;AOffset,ALen:Integer); + property Chars[AIndex:Integer]:AnsiChar read GetChars write SetChars;default; + property Length:Integer read GetLength write SetLength; + property IsUtf8:Boolean read GetIsUtf8; + end; + {$ENDIF} {$ENDIF} + JSONStringA = AnsiString; + {$IFDEF JSON_UNICODE} + JSONChar = WideChar; + PJSONChar = PWideChar; + {$IFNDEF USEYxdStr} + TIntArray = TArray; + {$ENDIF} + {$ELSE} + JSONString = JSONStringA; + JSONChar = AnsiChar; + PJSONChar = PAnsiChar; + {$IFNDEF USEYxdStr} + TIntArray = array of Integer; + IntPtr = Integer; + {$ENDIF} + {$ENDIF} + +{$IFNDEF USEYxdStr} +type + TTextEncoding = (teUnknown, {δ֪ı} teAuto,{Զ} teAnsi, { Ansi } + teUnicode16LE, { Unicode LE } teUnicode16BE, { Unicode BE } + teUTF8 { UTF8 } ); +{$ENDIF} + +type + JSONDataType = (jdtUnknown, jdtNull, jdtString, jdtInteger, jdtFloat, + jdtBoolean, jdtDateTime, jdtObject); + +{$IFNDEF USEYxdStr} +type + TStringCatHelper = class + private + FValue: array of JSONChar; + FStart, FDest: PJSONChar; + FBlockSize: Integer; + FSize: Integer; + function GetValue: JSONString; + function GetPosition: Integer; + function GetChars(AIndex:Integer): JSONChar; + procedure SetPosition(const Value: Integer); + procedure NeedSize(ASize:Integer); + public + constructor Create; overload; + constructor Create(ASize: Integer); overload; + destructor Destroy; override; + function Cat(p: PJSONChar; len: Integer): TStringCatHelper; overload; + function Cat(const s: JSONString): TStringCatHelper; overload; + function Cat(c: JSONChar): TStringCatHelper; overload; + function Cat(const V:Int64): TStringCatHelper;overload; + function Cat(const V:Double): TStringCatHelper;overload; + function Cat(const V:Boolean): TStringCatHelper;overload; + function Cat(const V:Currency): TStringCatHelper;overload; + function Cat(const V:TGuid): TStringCatHelper;overload; + function Cat(const V:Variant): TStringCatHelper;overload; + function Space(count:Integer): TStringCatHelper; + function Back(ALen: Integer): TStringCatHelper; + function BackIf(const s: PJSONChar): TStringCatHelper; + property Value: JSONString read GetValue; + property Chars[Index: Integer]: JSONChar read GetChars; + property Start: PJSONChar read FStart; + property Current: PJSONChar read FDest; + property Position: Integer read GetPosition write SetPosition; + end; +{$ENDIF} + +type + JSONBase = class; + JSONObject = class; + JSONArray = class; + + /// + /// JSONڵ + /// + PJSONValue = ^JSONValue; + JSONValue = packed record + private + FObject: JSONBase; + function ValueAsDateTime(const DateFormat, TimeFormat, DateTimeFormat: JSONString): JSONString; + function GetAsBoolean: Boolean; + function GetAsByte: Byte; + function GetAsDouble: Double; + function GetAsFloat: Extended; + function GetAsInt64: Int64; + function GetAsInteger: Integer; + function GetAsJSONArray: JSONArray; + function GetAsJSONObject: JSONObject; + function GetAsString: JSONString; + function GetAsVariant: Variant; + function GetAsWord: Word; + procedure SetAsBoolean(const Value: Boolean); + procedure SetAsByte(const Value: Byte); + procedure SetAsDouble(const Value: Double); + procedure SetAsFloat(const Value: Extended); + procedure SetAsInt64(const Value: Int64); + procedure SetAsInteger(const Value: Integer); + procedure SetAsJSONArray(const Value: JSONArray); + procedure SetAsJSONObject(const Value: JSONObject); + procedure SetAsString(const Value: JSONString); + procedure SetAsVariant(const Value: Variant); + procedure SetAsWord(const Value: Word); + function GetAsDateTime: TDateTime; + procedure SetAsDateTime(const Value: TDateTime); + function GetSize: Cardinal; + procedure Free(); + procedure SetAsDWORD(const Value: Cardinal); + public + FType: JSONDataType; + FName: JSONString; + FNameHash: Cardinal; + FValue: TBytes; + + function ToString: JSONString; overload; + function ToString(AIndent: Integer; ADoEscape: Boolean = False): JSONString; overload; + function GetPath(const ADelimiter: JSONChar = '.'): JSONString; + function GetObject: JSONBase; + function GetString: string; + {$IFDEF JSON_RTTI} + // ǰjsonתΪTValue͵ֵ + function ToObjectValue: TValue; + {$ENDIF} + procedure CopyValue(ASource: PJSONValue); inline; + + property AsBoolean: Boolean read GetAsBoolean write SetAsBoolean; + property AsByte: Byte read GetAsByte write SetAsByte; + property AsWord: Word read GetAsWord write SetAsWord; + property AsInteger: Integer read GetAsInteger write SetAsInteger; + property AsInt64: Int64 read GetAsInt64 write SetAsInt64; + property AsFloat: Extended read GetAsFloat write SetAsFloat; + property AsDouble: Double read GetAsDouble write SetAsDouble; + property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime; + property AsString: JSONString read GetAsString write SetAsString; + property AsVariant: Variant read GetAsVariant write SetAsVariant; // ֳ֧ + property AsJsonObject: JSONObject read GetAsJSONObject write SetAsJSONObject; + property AsJsonArray: JSONArray read GetAsJSONArray write SetAsJSONArray; + property Size: Cardinal read GetSize; + end; + + JSONEnumerator = class + private + FIndex: Integer; + FList: JSONBase; + public + constructor Create(AList: JSONBase); + function GetCurrent: PJSONValue; inline; + function MoveNext: Boolean; + property Current: PJSONValue read GetCurrent; + end; + + {$IFDEF UNICODE} + JSONList = TList; + {$ELSE} + JSONList = class(TList) + protected + function Get(Index: Integer): PJSONValue; inline; + procedure Put(Index: Integer; Item: PJSONValue); inline; + public + property Items[Index: Integer]: PJSONValue read Get write Put; default; + end; + {$ENDIF} + + {$IFDEF UNICODE} + /// + /// ˴XE6֧ + /// + /// ¼TQJson + /// Ҫ˵Ķ + /// ǷҪö + /// ûӵ + JSONFilterEventA = reference to procedure(ASender: JSONBase; AItem: PJSONValue; + var Accept: Boolean; ATag: Pointer); + {$ENDIF} + /// + /// ˴XE6֧ + /// + /// ¼TQJson + /// Ҫ˵Ķ + /// ǷҪö + /// ûӵ + JSONFilterEvent = procedure(ASender: JSONBase; AItem: PJSONValue; + var Accept: Boolean; ATag: Pointer) of object; + + JSONBase = class(TObject) + private + FParent: JSONBase; + FItems: JSONList; + FData: Pointer; + FValue: PJSONValue; // FParentΪnilʱ, FValueضΪnil + function GetItemIndex: Integer; + function GetValue: JSONString; + procedure SetValue(const Value: JSONString); + function GetName: JSONString; + procedure SetName(const Value: JSONString); + procedure RemoveObject(obj: JSONBase); + function FormatParseError(ACode: Integer; AMsg: JSONString; ps,p:PJSONChar): JSONString; + procedure RaiseParseException(ACode: Integer; ps, p: PJSONChar); + function GetIsJSONArray: Boolean; + function GetIsJSONObject: Boolean; + + //¼һJSON + function NewChildObject(const key: JSONString): JSONObject; //inline; + //¼һJSON + function NewChildArray(const key: JSONString): JSONArray; //inline; + protected + function GetIsArray: Boolean; virtual; + function GetCount: Integer; virtual; + function GetItems(Index: Integer): PJSONValue; virtual; + class function InternalEncode(Obj: JSONBase; ABuilder: TStringCatHelper; AIndent: Integer; ADoEscape: Boolean): TStringCatHelper; + /// JSONΪַ + /// Ƿʽַӿɶ + /// ADoFormatΪTrueʱС + /// رַ + class function Encode(Obj: JSONBase; AIndent: Integer = 0; ADoEscape: Boolean = True): JSONString; overload; + procedure DecodeObject(var p: PJSONChar); + function ParseJsonPair(ABuilder: TStringCatHelper; var p: PJSONChar): Integer; + class function ParseValue(ABuilder: TStringCatHelper; var p: PJSONChar): Variant; overload; + function ParseValue(ABuilder: TStringCatHelper; var p: PJSONChar; + const FName: JSONString): Integer; overload; + class procedure BuildJsonString(ABuilder: TStringCatHelper; var p: PJSONChar); overload; + {$IFDEF JSON_UNICODE} + class function CharUnescape(var p: PJSONChar): JSONChar; + {$ELSE} + class procedure CharUnescape(ABuilder: TStringCatHelper; var p: PJSONChar); + {$ENDIF} + public + constructor Create; virtual; + destructor Destroy; override; + procedure Clear(); virtual; + + // JaonKeyǷСд + class procedure SetJsonCaseSensitive(v: Boolean); + + function TryParse(const text: JSONString): Boolean; overload; + function TryParse(p: PJSONChar; len: Integer = -1): Boolean; overload; + /// + /// ַ IgnoreZero Ϊ TrueʱԴַе #0 תΪ #32 ٽ + /// + function Parse(const text: JSONString; IgnoreZero: Boolean = False): Boolean; overload; + function Parse(p: PJSONChar; len: Integer = -1): Boolean; overload; virtual; + {$IFDEF JSON_UNICODE} + function ToString: JSONString; overload; override; + {$ENDIF} + function ToString(AIndent: Integer{$IFNDEF JSON_UNICODE} = 0{$ENDIF}; ADoEscape: Boolean = False): JSONString; {$IFDEF JSON_UNICODE}reintroduce; overload;{$ENDIF} + procedure Assign(ANode: JSONBase); + + /// ȡfor..inҪGetEnumerator֧ + function GetEnumerator: JSONEnumerator; + /// ȡǰڵ· + function GetPath: JSONString; overload; + function GetPath(const ADelimiter: JSONChar): JSONString; overload; + + /// JSONΪַ, toStringͬ + /// λС + /// Ƿתĸַ + function Encode(AIndent: Integer; ADoEscape: Boolean = False): JSONString; overload; virtual; + /// ָJSONַ + /// ҪJSONַ + procedure Decode(const s: JSONString); overload; + /// ָJSONַ + /// Ҫַ + /// ַȣ<=0Ϊ\0(#0)βCԱ׼ַ + procedure Decode(p: PJSONChar; len: Integer = -1); overload; + + /// 浱ǰݵ + /// Ŀ + /// ʽ + /// ǷдBOM + /// ע⵱ǰƲᱻд + procedure SaveToStream(AStream: TStream; AIndent: Integer; AEncoding: TTextEncoding; AWriteBOM: Boolean); overload; + procedure SaveToStream(AStream: TStream; AIndent: Integer = 0); overload; + /// ĵǰλÿʼJSON + /// Դ + /// Դļ룬ΪteUnknownԶж + /// ĵǰλõijȱ2ֽڣ + procedure LoadFromStream(AStream: TStream; AEncoding: TTextEncoding=teUnknown); + /// 浱ǰݵļ + /// ļ + /// ʽ + /// ǷдUTF-8BOM + /// ע⵱ǰƲᱻд + procedure SaveToFile(const AFileName: JSONString; AIndent: Integer = 0); overload; + procedure SaveToFile(const AFileName: JSONString; AIndent: Integer; AEncoding: TTextEncoding; AWriteBOM: Boolean); overload; + /// ָļмصǰ + /// Ҫصļ + /// Դļ룬ΪteUnknownԶж + procedure LoadFromFile(const AFileName: JSONString; AEncoding: TTextEncoding=teUnknown); + + procedure Remove(Index: Integer); virtual; + + //һ + function Next: PJSONValue; + + /// ָƵĽ + /// ҪҵĽ + /// ֵδҵ-1 + function IndexOf(const Key: JSONString): Integer; virtual; + /// жָƵĽǷ + /// + function Exist(const Key: JSONString): Boolean; + {$IFDEF UNICODE} + /// ҷĽ + /// ûԶĸӶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnil򷵻nil + function FindIf(const ATag: Pointer; ANest: Boolean; AFilter: JSONFilterEventA): PJSONValue; overload; + {$ENDIF UNICODE} + /// ҷĽ + /// ûԶĸӶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnil򷵻nil + function FindIf(const ATag: Pointer; ANest: Boolean; AFilter: JSONFilterEvent): PJSONValue; overload; + /// һµʵ + /// µĿʵ + /// Ϊǿ¾ɶ֮ݱûκιϵһ + /// 󣬲һӰ졣 + /// + function Copy: JSONBase; + {$IFDEF UNICODE} + /// һµʵ + /// ûӵıǩ + /// û¼ڿҪ + /// µĿʵ + /// Ϊǿ¾ɶ֮ݱûκιϵһ + /// 󣬲һӰ졣 + /// + function CopyIf(const ATag: Pointer; AFilter: JSONFilterEventA): JSONBase; overload; + {$ENDIF UNICODE} + /// һµʵ + /// ûӵıǩ + /// û¼ڿҪ + /// µĿʵ + /// Ϊǿ¾ɶ֮ݱûκιϵһ + /// 󣬲һӰ졣 + /// + function CopyIf(const ATag: Pointer; AFilter: JSONFilterEvent): JSONBase; overload; + {$IFDEF UNICODE} + /// + /// ɾӽ + /// + /// ûԼӵĶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnilȼClear + procedure DeleteIf(const ATag: Pointer; ANest: Boolean; AFilter: JSONFilterEventA); overload; + {$ENDIF UNICODE} + /// + /// ɾӽ + /// + /// ûԼӵĶ + /// ǷǶ׵ãΪfalseֻԵǰӽ + /// ˻صΪnilȼClear + procedure DeleteIf(const ATag: Pointer; ANest: Boolean; AFilter: JSONFilterEvent); overload; + + // ָJSONַ + class function ParseObject(const Text: JSONString; RaiseError: Boolean = True): JSONObject; overload; + // ָJSONַ + class function ParseArray(const Text: JSONString; RaiseError: Boolean = True): JSONArray; overload; + + /// + /// ǿһ·,,δҪĽ + /// ·ӦĶ + /// + /// ·ȫڣForcePathᰴ¹ִ: + /// 1APathа[]ΪӦ·Ϊ飬ʾ£ + /// (1)'a.b[].name' + /// a -> jdtObject + /// b -> jdtArray + /// b[0].name -> jdtNull(bδָԶΪb[0] + /// 2·ָ./\ǵȼ۵ģҽвӦַ֮һ, + /// 3APathָĶͲƥ䣬׳쳣aΪ󣬵ʹa[0].bʱ + /// + /// + function ForcePath(const APath: JSONString; const ADelimiter: JSONChar = '.'): PJSONValue; + /// ȡָ·JSON + /// · + /// ·ָĬʹ"." + /// ҵӽ㣬δҵNULL(nil) + function ItemByPath(const APath: JSONString; const ADelimiter: JSONChar = '.'): PJSONValue; + {$IFDEF USERegEx} + /// ȡָƹĽ㵽б + /// ʽ + /// ڱб + /// Ƿݹӽ + /// ҵĽδҵ0 + function ItemByRegex(const ARegex: JSONString; AList: JSONList; + ANest: Boolean = False): Integer; overload; + {$ENDIF} + + {$IFDEF USERTTI} + // ǰjsonõָĶʵ + procedure ToObjectValue(ADest: Pointer; AType: PTypeInfo); overload; + // ǰjsonõָĶʵ + procedure ToObject(ADest: TObject); + // ָԴַݼjson + procedure PutObjectValue(const Key: JSONString; ASource: Pointer; AType: PTypeInfo); overload; + // ָĶʵjson + procedure PutObject(const Key: JSONString; ASource: TObject); + {$IFDEF USEDBRTTI} + /// + /// ָݼʵݼjson + /// + procedure PutDataSet(const Key: JSONString; aIn: TDataSet); overload; + /// + /// ָݼʵݼjson + /// KeyΪգAoutһKeyӶ + /// лDataSetݼ + /// ӵڼҳʼлPageSize > 0 ʱЧ + /// ҳʱÿҳ + /// лֶָΡΪգֻлArgsFieldsֶָͬ + /// + procedure PutDataSet(const Key: JSONString; aIn: TDataSet; + const PageIndex, PageSize: Integer; Base64Blob: Boolean = True); overload; + /// + /// ǰjsonתDataSetУתɹ + /// + function ToDataSet(aOut: TDataSet): Integer; + {$ENDIF} + {$ENDIF} + {$IFDEF JSON_RTTI} + /// + /// ǰjsonõAInstanceָԴַ + /// + procedure ToObjectValue(AInstance: TValue); overload; + /// + /// ǰjsonתΪTValue͵ֵ + /// + function ToObjectValue(): TValue; overload; + /// + /// ǰjsonõָļ¼ʵ + /// + procedure ToRecord(out AInstance: T); + /// + /// ָRTTIʵjson + /// + procedure PutObjectValue(const Key: JSONString; AInstance: TValue); overload; + /// + /// ָļ¼ʵjson + /// + procedure PutRecord(const Key: JSONString; const ASource: T); + /// ʹõǰJsonָӦ + /// Ķʵ + /// غõĽ + /// ΪǰƣIJӽҪһ + function Invoke(AInstance: TValue): TValue; + {$ENDIF} + + /// + /// JSONַԶ + /// + procedure PutJSON(const Key, Value: JSONString; AType: JsonDataType = jdtUnknown); + + // + property Parent: JSONBase read FParent; + //ӽֵ + property Value: JSONString read GetValue write SetValue; + //··м"\"ָ + property Path: JSONString read GetPath; + //ڸе˳򣬴0ʼ-1ԼǸ + property ItemIndex: Integer read GetItemIndex; + //ڵ(ûиڵƺЧ) + property Name: JSONString read GetName write SetName; + //ĸݳԱû + property Data: Pointer read FData write FData; + //ӽ + property Count: Integer read GetCount; + //ȡһӽڵ + property Items[Index: Integer]: PJSONValue read GetItems; default; + //жǷJSONObject + property IsJSONObject: Boolean read GetIsJSONObject; + //жǷJSONArray + property IsJSONArray: Boolean read GetIsJSONArray; + end; + + JSONObject = class(JSONBase) + private + function GetChildItem(const Key: JSONString): PJSONValue; + function GetChildForceItem(const Path: JSONString): PJSONValue; + protected + procedure Put(const Key: JSONString; ABuilder: TStringCatHelper); overload; + public + function Add(const Key: JSONString): PJSONValue; + procedure Put(const Key: JSONString; Value: Boolean); overload; + procedure Put(const Key: JSONString; Value: Integer); overload; + procedure Put(const Key: JSONString; Value: Word); overload; + procedure Put(const Key: JSONString; Value: Cardinal); overload; + procedure Put(const Key: JSONString; Value: Byte); overload; + procedure Put(const Key: JSONString; const Value: JSONString); overload; + procedure Put(const Key: JSONString; const Value: Int64); overload; + procedure Put(const Key: JSONString; const Value: Extended); overload; + procedure Put(const Key: JSONString; const Value: Double); overload; + procedure Put(const Key: JSONString; const Value: Variant); overload; + procedure Put(const Key: JSONString; Value: JSONObject); overload; + procedure Put(const Key: JSONString; Value: JSONArray); overload; + procedure Put(const Key: JSONString; Value: array of const); overload; + procedure PutDateTime(const Key: JSONString; Value: TDateTime); + + procedure Delete(const Key: JSONString); + function Clone: JSONObject; + + function NextAsJsonObject: JSONObject; + + /// + /// textаָkeyvalueӶ, valueΪʱֻжkey + /// + class function ParseObjectByName(const Text, Key: JSONString; Value: Variant): JSONObject; + /// + /// textָkeyjsonStringֵ + /// + class function ParseStringByName(const Text, Key: JSONString): JSONString; + {$IFDEF USERTTI} + /// + /// һµJSONObject, ָĶʵݼ뵱 + /// + class function ParseObject(const aIn: TObject): JSONObject; overload; + {$ENDIF} + + function AddChildObject(const Key: JSONString): JSONObject; + function AddChildArray(const Key: JSONString): JSONArray; overload; + function AddChildArray(const Key: JSONString; AItems: array of const): JSONArray; overload; + + function GetItem(const Key: JSONString): PJSONValue; + function GetByte(const Key: JSONString): Byte; + function GetBoolean(const Key: JSONString): Boolean; + function GetInt(const Key: JSONString): Integer; + function GetInt64(const Key: JSONString): Int64; + function GetWord(const Key: JSONString): Word; + function GetDWORD(const Key: JSONString): Cardinal; + function GetFloat(const Key: JSONString): Extended; + function GetDouble(const Key: JSONString): Double; + function GetString(const Key: JSONString): JSONString; + function GetDateTime(const Key: JSONString): TDateTime; + function GetVariant(const Key: JSONString): Variant; + function GetJsonObject(const Key: JSONString): JSONObject; + function GetJsonArray(const Key: JSONString): JSONArray; + + procedure SetByte(const Key: JSONString; Value: Byte); + procedure SetBoolean(const Key: JSONString; const Value: Boolean); + procedure SetDouble(const Key: JSONString; const Value: Double); + procedure SetInt64(const Key: JSONString; const Value: Int64); + procedure SetInt(const Key: JSONString; const Value: Integer); + procedure SetWord(const Key: JSONString; const Value: Word); + procedure SetDWORD(const Key: JSONString; const Value: DWORD); + procedure SetJsonArray(const Key: JSONString; const Value: JSONArray); + procedure SetJsonObject(const Key: JSONString; const Value: JSONObject); + procedure SetString(const Key, Value: JSONString); + procedure SetVariant(const Key: JSONString; const Value: Variant); + procedure SetDateTime(const Key: JSONString; const Value: TDateTime); + + // SuperJson ӿ + function Contains(const Key: JSONString): Boolean; inline; + + property S[const Key: JSONString]: JSONString read GetString write SetString; + property I[const Key: JSONString]: Int64 read GetInt64 write SetInt64; + property B[const Key: JSONString]: Boolean read GetBoolean write SetBoolean; + property F[const Key: JSONString]: Double read GetDouble write SetDouble; + property O[const Key: JSONString]: JSONObject read GetJsonObject write SetJsonObject; + property A[const Key: JSONString]: JSONArray read GetJsonArray write SetJsonArray; + property V[const Key: JSONString]: Variant read GetVariant write SetVariant; + + // ʱԶ + property Child[const Key: JSONString]: PJSONValue read GetChildItem; + // PathKeyҲһ"."ָ·ǿƴڣѴڵͲʱ׳쳣 + property ChildForce[const Path: JSONString]: PJSONValue read GetChildForceItem; default; + end; + + JSONArray = class(JSONBase) + private + function NewJsonValue(): PJSONValue; inline; + protected + function GetIsArray: Boolean; override; + public + procedure Add(Value: Boolean); overload; + procedure Add(Value: Integer); overload; + procedure Add(Value: Word); overload; + procedure Add(Value: Cardinal); overload; + procedure Add(Value: Byte); overload; + procedure Add(const Value: JSONString); overload; + procedure Add(const Value: Int64); overload; + procedure Add(const Value: Extended); overload; + procedure Add(const Value: Double); overload; + procedure Add(const Value: Variant); overload; + procedure Add(const Value: array of const); overload; + procedure Add(Value: JSONObject); overload; + procedure Add(Value: JSONArray); overload; + procedure AddDateTime(Value: TDateTime); + /// + /// JSONַԶ + /// + procedure AddJSON(const Value: JSONString; AType: JsonDataType = jdtUnknown); overload; + {$IFDEF JSON_RTTI} + /// + /// ָĶʵjson + /// + procedure PutObject(ASource: TObject); + /// + /// ָļ¼ʵjson + /// + procedure PutRecord(const ASource: T); + {$ENDIF} + + function Clone: JSONArray; + function AddChildObject(): JSONObject; overload; + function AddChildObject(const Index: Integer): JSONObject; overload; + function AddChildArray(): JSONArray; overload; + function AddChildArray(const Index: Integer): JSONArray; overload; + + function NextAsJsonArray: JSONArray; + + function GetByte(Index: Integer): Byte; + function GetBoolean(Index: Integer): Boolean; + function GetInt(Index: Integer): Integer; + function GetInt64(Index: Integer): Int64; + function GetWord(Index: Integer): Word; + function GetDWORD(Index: Integer): Cardinal; + function GetFloat(Index: Integer): Extended; + function GetDouble(Index: Integer): Double; + function GetString(Index: Integer): JSONString; + function GetDateTime(Index: Integer): TDateTime; + function GetVariant(Index: Integer): Variant; + function GetJsonObject(Index: Integer): JSONObject; + function GetJsonArray(Index: Integer): JSONArray; + + procedure SetByte(Index: Integer; const Value: Byte); + procedure SetBoolean(Index: Integer; const Value: Boolean); + procedure SetDouble(Index: Integer; const Value: Double); + procedure SetInt(Index: Integer; const Value: Integer); + procedure SetWord(Index: Integer; const Value: Word); + procedure SetDWORD(Index: Integer; const Value: DWORD); + procedure SetInt64(Index: Integer; const Value: Int64); + procedure SetDateTime(Index: Integer; const Value: TDateTime); + procedure SetJsonArray(Index: Integer; const Value: JSONArray); + procedure SetJsonObject(Index: Integer; const Value: JSONObject); + procedure SetString(Index: Integer; const Value: JSONString); + procedure SetVariant(Index: Integer; const Value: Variant); + + property S[Index: Integer]: JSONString read GetString write SetString; + property I[Index: Integer]: Int64 read GetInt64 write SetInt64; + property B[Index: Integer]: Boolean read GetBoolean write SetBoolean; + property F[Index: Integer]: Double read GetDouble write SetDouble; + property O[Index: Integer]: JSONObject read GetJsonObject write SetJsonObject; + property A[Index: Integer]: JSONArray read GetJsonArray write SetJsonArray; + property V[Index: Integer]: Variant read GetVariant write SetVariant; + end; + +var + // Ƿϸģʽϸģʽ£ + // 1.ƻַʹ˫Ű,ΪFalseƿûŻʹõš + // 2.עͲ֧֣ΪFalse֧//עͺ/**/Ŀע + StrictJson: Boolean = False; + // ǷKeyСд + JsonCaseSensitive: Boolean = True; + // ָδRTTIеöٺͼ + JsonRttiEnumAsInt: Boolean = True; + {$IFNDEF USEYxdStr} + // Javaʽ룬#$0ַΪ#$C080 + JavaFormatUtf8: Boolean = True; + {$ENDIF} + +{$IFNDEF USEYxdStr} +function StrDupX(const s: PJSONChar; ACount:Integer): JSONString; +function StrDup(const S: PJSONChar; AOffset: Integer = 0; const ACount: Integer = MaxInt): JSONString; +function IsHexChar(c: JSONChar): Boolean; inline; +function HexValue(c: JSONChar): Integer; +function HexChar(v: Byte): JSONChar; +function BinToHex(p: Pointer; l: Integer): JSONString; overload; +function BinToHex(const ABytes:TBytes): JSONString; overload; +procedure HexToBin(p: Pointer; l: Integer; var AResult: TBytes); overload; +function HexToBin(const S: JSONString): TBytes; overload; +procedure HexToBin(const S: JSONString; var AResult: TBytes); overload; +{$ENDIF} +//ַǷָб +{$IFNDEF USEYxdStr} +function CharIn(const c, list: PJSONChar; ACharLen:PInteger = nil): Boolean; inline; +{$IFNDEF NEXTGEN} +function CharInA(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +function CharInU(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +{$ENDIF} +function CharInW(c, list: PWideChar; ACharLen: PInteger = nil): Boolean; +{$ENDIF} +//㵱ǰַij +{$IFNDEF USEYxdStr} +function CharSizeA(c: PAnsiChar): Integer; +function CharSizeU(c: PAnsiChar): Integer; +function CharSizeW(c: PWideChar): Integer; +function CharUpperA(c: AnsiChar): AnsiChar; +function CharUpperW(c: WideChar): WideChar; +//ַкţеʼַ +function StrPosA(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +function StrPosU(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +function StrPosW(start, current: PWideChar; var ACol, ARow:Integer): PWideChar; +//ȡһ +function DecodeLineA(var p:PAnsiChar; ASkipEmpty:Boolean=True): JSONStringA; +function DecodeLineW(var p:PWideChar; ASkipEmpty:Boolean=True): JSONStringW; +//հַ Ansi룬#9#10#13#161#161UCS룬#9#10#13#$3000 +function SkipSpaceA(var p: PAnsiChar): Integer; +function SkipSpaceU(var p: PAnsiChar): Integer; +function SkipSpaceW(var p: PWideChar): Integer; +//һ,#10Ϊнβ +function SkipLineA(var p: PAnsiChar): Integer; +function SkipLineU(var p: PAnsiChar): Integer; +function SkipLineW(var p: PWideChar): Integer; +//Ƿǿհַ +function IsSpaceA(const c:PAnsiChar; ASpaceSize:PInteger=nil): Boolean; +function IsSpaceU(const c:PAnsiChar; ASpaceSize:PInteger=nil): Boolean; +function IsSpaceW(const c:PWideChar; ASpaceSize:PInteger=nil): Boolean; +//ֱַָ +{$IFNDEF NEXTGEN} +function SkipUntilA(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar = {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF}): Integer; +function SkipUntilU(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar = {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF}): Integer; +{$ENDIF} +function SkipUntilW(var p: PWideChar; AExpects: PWideChar; AQuoter: WideChar = #0): Integer; +//жǷַָʼ +function StartWith(s, startby: PJSONChar; AIgnoreCase: Boolean): Boolean; +{$ENDIF} +//ı +{$IFNDEF USEYxdStr} +procedure SaveTextA(AStream: TStream; const S: JSONStringA); +procedure SaveTextU(AStream: TStream; const S: JSONStringA; AWriteBom: Boolean = True); +procedure SaveTextW(AStream: TStream; const S: JSONStringW; AWriteBom: Boolean = True); +procedure SaveTextWBE(AStream: TStream; const S: JSONStringW; AWriteBom: Boolean = True); +{$ENDIF} +//ı +{$IFNDEF USEYxdStr} +function LoadTextA(AStream: TStream; AEncoding: TTextEncoding=teUnknown): JSONStringA; overload; +function LoadTextU(AStream: TStream; AEncoding: TTextEncoding=teUnknown): JSONStringA; overload; +function LoadTextW(AStream: TStream; AEncoding: TTextEncoding=teUnknown): JSONStringW; overload; +{$ENDIF} +//ת +{$IFNDEF USEYxdStr} +function AnsiEncode(p:PWideChar; l:Integer): JSONStringA; overload; +function AnsiEncode(const p: JSONStringW): JSONStringA; overload; +{$IFNDEF MSWINDOWS} +function AnsiDecode(const S: AnsiString): JSONStringW; overload; +{$ENDIF} +function AnsiDecode(p: PAnsiChar; l:Integer): JSONStringW; overload; +function Utf8Encode(const p: JSONStringW): JSONStringA; overload; +function Utf8Encode(p: PWideChar; l: Integer): JSONStringA; overload; +{$IFNDEF MSWINDOWS} +function Utf8Decode(const S: AnsiString): JSONStringW; overload; +{$ENDIF} +function Utf8Decode(p: PAnsiChar; l: Integer): JSONStringW; overload; +{$ENDIF} + +implementation + +{$IFDEF USERTTI}uses YxdRtti;{$ENDIF} + +resourcestring + {$IFNDEF USEYxdStr}{$IFDEF NEXTGEN} + SOutOfIndex = 'Խ磬ֵ %d [%d..%d]ķΧڡ'; + {$ENDIF}{$ENDIF} + {$IFNDEF USEYxdStr} + SBadUnicodeChar = 'ЧUnicodeַ:%d'; + {$ENDIF} + SBadJson = 'ǰݲЧJSONַ.'; + SCharNeeded = 'ǰλӦ "%s", "%s".'; + SBadConvert = '%s һЧ %s ͵ֵ'; + SBadNumeric = '"%s"Чֵ.'; + SBadJsonTime = '"%s"һЧʱֵ.'; + SNameNotFound = 'Ŀδҵ.'; + SCommentNotSupport = 'ϸģʽ²֧ע, Ҫע͵JSON, 뽫StrictJsonΪFalse.'; + SUnsupportArrayItem = 'ӵĶ̬%dԪͲ֧֡'; + SBadStringStart = 'ϸJSONַ"ʼ'; + SUnknownToken = '޷ʶעͷ, עͱ///**/.'; + SNotSupport = ' [%s] ڵǰ²֧.'; + SBadJsonArray = '%s һЧJSON鶨.'; + SBadJsonObject = '%s һЧJSON.'; + SBadJsonEncoding = 'Чı, ֻUTF-8, ANSI, Unicode 16 LE, Unicode 16 BE.'; + SJsonParseError = '%dе%d: %s '#13#10': %s'; + SNoExistJSONKey = 'JSONвָKey: %s'; + SBadJsonName = '%s һЧJSON.'; + SBadNameStart = 'JsonӦ''"''ַʼ.'; + SBadNameEnd = 'Jsonδȷ.'; + SEndCharNeeded = 'ǰλҪJsonַ",]}".'; + SUnknownError = 'δ֪Ĵ.'; + SParamMissed = ' %s ͬĽδҵ.'; + SMethodMissed = 'ָĺ %s .'; + SObjectChildNeedName = ' %s ĵ %d ӽδֵ, ǰ踳ֵ.'; + +const + //תΪJsonʱתַθʽ + JsonDateFormat: JSONString = 'yyyy-mm-dd'; + //ʱתΪJsonʱתַθʽ + JsonTimeFormat: JSONString = 'hh:nn:ss.zzz'; + //ʱתΪJsonʱתַθʽ + JsonDateTimeFormat: JSONString = 'yyyy-mm-dd hh:nn:ss.zzz'; + // + JsonFloatDigits: Integer = 6; + +const + JsonTypeName: array [0 .. 8] of JSONString = ('Unknown', 'Null', 'String', + 'Integer', 'Float', 'Boolean', 'DateTime', 'Array', 'Object'); + EParse_Unknown = -1; + EParse_BadStringStart = 1; + EParse_BadJson = 2; + EParse_CommentNotSupport = 3; + EParse_UnknownToken = 4; + EParse_EndCharNeeded = 5; + EParse_BadNameStart = 6; + EParse_BadNameEnd = 7; + EParse_NameNotFound = 8; + +{$IFNDEF USEYxdStr} +//㵱ǰַij +// GB18030,GBKGB2312 +// ֽڣֵ00x7F +// ˫ֽڣһֽڵֵ0x810xFEڶֽڵֵ0x400xFE0x7F +// ֽڣһֽڵֵ0x810xFEڶֽڵֵ0x300x39ֽڴ0x810xFEĸֽڴ0x300x39 +function CharSizeA(c: PAnsiChar): Integer; +begin + {$IFDEF MSWINDOWS} + if GetACP = 936 then begin + {$ELSE} + if TEncoding.ANSI.CodePage = 936 then begin + {$ENDIF} + Result:=1; + {$IFDEF NEXTGEN} + if (c^>=$81) and (c^<=$FE) then begin + Inc(c); + if (c^>=$40) and (c^<=$FE) and (c^<>$7F) then + Result:=2 + else if (c^>=$30) and (c^<=$39) then begin + Inc(c); + if (c^>=$81) and (c^<=$FE) then begin + Inc(c); + if (c^>=$30) and (c^<=$39) then + Result:=4; + end; + end; + end; + {$ELSE} + if (c^>=#$81) and (c^<=#$FE) then begin + Inc(c); + if (c^>=#$40) and (c^<=#$FE) and (c^<>#$7F) then + Result:=2 + else if (c^>=#$30) and (c^<=#$39) then begin + Inc(c); + if (c^>=#$81) and (c^<=#$FE) then begin + Inc(c); + if (c^>=#$30) and (c^<=#$39) then + Result:=4; + end; + end; + end; + {$ENDIF} + end else + {$IFDEF JSON_UNICODE} + {$IFDEF NEXTGEN} + if TEncoding.ANSI.CodePage = CP_UTF8 then + Result := CharSizeU(c) + else if (c^<128) or (TEncoding.ANSI.CodePage=437) then + Result:=1 + else + Result:=2; + {$ELSE} + {$IF RTLVersion>26} + Result := AnsiStrings.StrCharLength(PAnsiChar(c)); + {$ELSE} + Result := sysutils.StrCharLength(PAnsiChar(c)); + {$IFEND} + {$ENDIF} + {$ELSE} + Result := StrCharLength(PAnsiChar(c)); + {$ENDIF} +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function CharSizeU(c: PAnsiChar): Integer; +begin + if (Ord(c^) and $80) = 0 then + Result := 1 + else begin + if (Ord(c^) and $FC) = $FC then //4000000+ + Result := 6 + else if (Ord(c^) and $F8)=$F8 then//200000-3FFFFFF + Result := 5 + else if (Ord(c^) and $F0)=$F0 then//10000-1FFFFF + Result := 4 + else if (Ord(c^) and $E0)=$E0 then//800-FFFF + Result := 3 + else if (Ord(c^) and $C0)=$C0 then//80-7FF + Result := 2 + else + Result := 1; + end +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function CharSizeW(c: PWideChar): Integer; +begin + if (c[0]>=#$DB00) and (c[0]<=#$DBFF) and (c[1] >= #$DC00) and (c[1] <= #$DFFF) then + Result := 2 + else + Result := 1; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +{$IFNDEF NEXTGEN} +procedure CalcCharLengthA(var Lens: TIntArray; list: PAnsiChar); +var + i, l: Integer; +begin + i := 0; + System.SetLength(Lens, Length(List)); + while i< Length(List) do begin + l := CharSizeA(@list[i]); + lens[i] := l; + Inc(i, l); + end; +end; +{$ENDIF} + +{$IFNDEF NEXTGEN} +procedure CalcCharLengthU(var Lens: TIntArray; list: PAnsiChar); +var + i, l: Integer; +begin + i := 0; + System.SetLength(Lens, Length(List)); + while i< Length(List) do begin + l := CharSizeU(@list[i]); + lens[i] := l; + Inc(i, l); + end; +end; +{$ENDIF} +{$ENDIF} + +// ַǷָб +{$IFNDEF USEYxdStr} +function CharIn(const c, list: PJSONChar; ACharLen:PInteger = nil): Boolean; +begin +{$IFDEF JSON_UNICODE} + Result := CharInW(c, list, ACharLen); +{$ELSE} + Result := CharInA(c, list, ACharLen); +{$ENDIF} +end; +{$ENDIF} + +{$IFNDEF USEYxdStr}{$IFNDEF NEXTGEN} +function CharInA(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +var + i: Integer; + lens: TIntArray; +begin + Result := False; + CalcCharLengthA(lens, list); + i := 0; + while i < Length(list) do begin + if CompareMem(c, @list[i], lens[i]) then begin + if ACharLen <> nil then + ACharLen^:=lens[i]; + Result := True; + Break; + end else + Inc(i, lens[i]); + end; +end; +{$ENDIF} {$ENDIF} + +{$IFNDEF USEYxdStr} +function CharInW(c, list: PWideChar; ACharLen: PInteger = nil): Boolean; +var + p: PWideChar; +begin + Result:=False; + p := list; + while p^ <> #0 do begin + if p^ = c^ then begin + if (p[0]>=#$DB00) and (p[0]<=#$DBFF) then begin + if p[1]=c[1] then begin + Result := True; + if ACharLen <> nil then + ACharLen^ := 2; + Break; + end; + end else begin + Result := True; + if ACharLen <> nil then + ACharLen^ := 1; + Break; + end; + end; + Inc(p); + end; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr}{$IFNDEF NEXTGEN} +function CharInU(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +var + i: Integer; + lens: TIntArray; +begin + Result := False; + CalcCharLengthU(lens, list); + i := 0; + while i < Length(list) do begin + if CompareMem(c, @list[i], lens[i]) then begin + if ACharLen <> nil then + ACharLen^ := lens[i]; + Result := True; + Break; + end else + Inc(i, lens[i]); + end; +end; +{$ENDIF} {$ENDIF} + +{$IFNDEF USEYxdStr} +function StrDupX(const s: PJSONChar; ACount:Integer): JSONString; +begin + SetLength(Result, ACount); + Move(s^, PJSONChar(Result)^, ACount{$IFDEF JSON_UNICODE} shl 1{$ENDIF}); +end; + +function StrDup(const S: PJSONChar; AOffset: Integer; const ACount: Integer): JSONString; +var + C, ACharSize: Integer; + p, pds, pd: PJSONChar; +begin + C := 0; + p := S + AOffset; + SetLength(Result, 4096); + pd := PJSONChar(Result); + pds := pd; + while (p^ <> #0) and (C < ACount) do begin + ACharSize := {$IFDEF JSON_UNICODE} CharSizeW(p); {$ELSE} CharSizeA(p); {$ENDIF} + AOffset := pd - pds; + if AOffset + ACharSize = Length(Result) then begin + SetLength(Result, Length(Result){$IFDEF JSON_UNICODE} shl 1{$ENDIF}); + pds := PJSONChar(Result); + pd := pds + AOffset; + end; + Inc(C); + pd^ := p^; + if ACharSize = 2 then + pd[1] := p[1]; + Inc(pd, ACharSize); + Inc(p, ACharSize); + end; + SetLength(Result, pd-pds); +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function StrPosA(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +begin + ACol := 1; + ARow := 1; + Result := start; + while IntPtr(start) < IntPtr(current) do begin + if start^={$IFDEF NEXTGEN}10{$ELSE}#10{$ENDIF} then begin + Inc(ARow); + ACol := 1; + Inc(start); + Result := start; + end else begin + Inc(start, CharSizeA(start)); + Inc(ACol); + end; + end; +end; + +function StrPosU(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +begin + ACol := 1; + ARow := 1; + Result := start; + while IntPtr(start){$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if (PWORD(p)^ = $0D0A) or (PWORD(p)^ = $0A0D) then + i := 2 + else if (p^ = {$IFDEF NEXTGEN}13{$ELSE}#13{$ENDIF}) then + i := 1 + else + i := 0; + if i > 0 then begin + if ps = p then begin + if ASkipEmpty then begin + Inc(p, i); + ps := p; + end else begin + Result := ''; + Exit; + end; + end else begin + {$IFDEF NEXTGEN} + Result.Length := IntPtr(p)-IntPtr(ps); + {$ELSE} + SetLength(Result, p-ps); + {$ENDIF} + Move(ps^, PAnsiChar(Result)^, IntPtr(p)-IntPtr(ps)); + Inc(p, i); + Exit; + end; + end else + Inc(p); + end; + if ps = p then + Result := '' + else begin + {$IFDEF NEXTGEN} + Result.Length := IntPtr(p)-IntPtr(ps); + {$ELSE} + SetLength(Result, p-ps); + {$ENDIF} + Move(ps^, PAnsiChar(Result)^, IntPtr(p)-IntPtr(ps)); + end; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function DecodeLineW(var p: PWideChar; ASkipEmpty: Boolean): JSONStringW; +var + ps: PWideChar; + i: Integer; +begin + ps := p; + while p^<>#0 do begin + if (PCardinal(p)^ = $000D000A) or (PCardinal(p)^ = $000A000D) then + i := 2 + else if (p^ = #13) then + i := 1 + else + i := 0; + if i > 0 then begin + if ps = p then begin + if ASkipEmpty then begin + Inc(p, i); + ps := p; + end else begin + Result := ''; + Exit; + end; + end else begin + SetLength(Result, p-ps); + Move(ps^, PWideChar(Result)^, p-ps); + Inc(p, i); + Exit; + end; + end else + Inc(p); + end; + if ps = p then + Result := '' + else begin + SetLength(Result, p-ps); + Move(ps^, PWideChar(Result)^, p-ps); + end; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function IsSpaceA(const c: PAnsiChar; ASpaceSize: PInteger): Boolean; +begin + {$IFDEF NEXTGEN} + if c^ in [9, 10, 13, 32] then begin + {$ELSE} + if c^ in [#9, #10, #13, #32] then begin + {$ENDIF} + Result := True; + if ASpaceSize <> nil then + ASpaceSize^ := 1; + end else if PWORD(c)^ = $A1A1 then begin + Result := True; + if ASpaceSize <> nil then + ASpaceSize^ := 2; + end else + Result:=False; +end; + +function IsSpaceW(const c: PWideChar; ASpaceSize: PInteger): Boolean; +begin + Result := (c^=#9) or (c^=#10) or (c^=#13) or (c^=#32) or (c^=#$3000); + if Result and (ASpaceSize <> nil) then + ASpaceSize^ := 1; +end; + +//ȫǿո$3000UTF-8227,128,128 +function IsSpaceU(const c: PAnsiChar; ASpaceSize: PInteger): Boolean; +begin + {$IFDEF NEXTGEN} + if c^ in [9, 10, 13, 32] then begin + {$ELSE} + if c^ in [#9, #10, #13, #32] then begin + {$ENDIF} + Result := True; + if (ASpaceSize <> nil) then + ASpaceSize^ := 1; + end else if (c^={$IFDEF NEXTGEN}227{$ELSE}#227{$ENDIF}) and (PWORD(IntPtr(c)+1)^ = $8080) then begin + Result := True; + if (ASpaceSize <> nil) then + ASpaceSize^ := 3; + end else + Result:=False; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function SkipSpaceA(var p: PAnsiChar): Integer; +var + ps: PAnsiChar; + L: Integer; +begin + ps := p; + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if IsSpaceA(p, @L) then + Inc(p, L) + else + Break; + end; + Result:= IntPtr(p) - IntPtr(ps); +end; + +function SkipSpaceU(var p: PAnsiChar): Integer; +var + ps: PAnsiChar; + L: Integer; +begin + ps := p; + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if IsSpaceU(p, @L) then + Inc(p, L) + else + Break; + end; + Result:= IntPtr(p) - IntPtr(ps); +end; + +function SkipSpaceW(var p: PWideChar): Integer; +var + ps: PWideChar; + L:Integer; +begin + ps := p; + while p^<>#0 do begin + if IsSpaceW(p, @L) then + Inc(p, L) + else + Break; + end; + Result := p - ps; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function SkipLineA(var p: PAnsiChar): Integer; +var + ps: PAnsiChar; +begin + ps := p; + while p^ <> {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if (PWORD(p)^ = $0D0A) or (PWORD(p)^ = $0A0D) then begin + Inc(p, 2); + Break; + end else if (p^ = {$IFDEF NEXTGEN}13{$ELSE}#13{$ENDIF}) then begin + Inc(p); + Break; + end else + Inc(p); + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipLineU(var p: PAnsiChar): Integer; +begin + Result := SkipLineA(p); +end; + +function SkipLineW(var p: PWideChar): Integer; +var + ps: PWideChar; +begin + ps := p; + while p^ <> #0 do begin + if (PCardinal(p)^ = $000D000A) or (PCardinal(p)^ = $000A000D) then begin + Inc(p, 2); + Break; + end else if (p^ = #13) then begin + Inc(p); + Break; + end else + Inc(p); + end; + Result := p - ps; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr}{$IFNDEF NEXTGEN} +function SkipUntilA(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar): Integer; +var + ps: PAnsiChar; +begin + ps := p; + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if (p^ = AQuoter) then begin + Inc(p); + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if p^ = {$IFDEF NEXTGEN}$5C{$ELSE}#$5C{$ENDIF} then begin + Inc(p); + if p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} then + Inc(p); + end else if p^ = AQuoter then begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end else + Inc(p); + end; + end else if CharInA(p, AExpects) then + Break + else + Inc(p, CharSizeA(p)); + end; + Result := IntPtr(p) - IntPtr(ps); +end; +{$ENDIF} {$ENDIF} +{$IFNDEF USEYxdStr}{$IFNDEF NEXTGEN} +function SkipUntilU(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar): Integer; +var + ps: PAnsiChar; +begin + ps := p; + while p^<>#0 do begin + if (p^ = AQuoter) then begin + Inc(p); + while p^<>#0 do begin + if p^=#$5C then begin + Inc(p); + if p^<>#0 then + Inc(p); + end else if p^=AQuoter then begin + Inc(p); + if p^=AQuoter then + Inc(p) + else + Break; + end else + Inc(p); + end; + end else if CharInU(p, AExpects) then + Break + else + Inc(p, CharSizeU(p)); + end; + Result := p - ps; +end; +{$ENDIF} {$ENDIF} + +{$IFNDEF USEYxdStr} +function SkipUntilW(var p: PWideChar; AExpects: PWideChar; AQuoter: WideChar): Integer; +var + ps: PWideChar; +begin + ps := p; + while p^<>#0 do begin + if (p^=AQuoter) then begin + Inc(p); + while p^<>#0 do begin + if p^=#$5C then begin + Inc(p); + if p^<>#0 then + Inc(p); + end else if p^=AQuoter then begin + Inc(p); + if p^=AQuoter then + Inc(p) + else + Break; + end else + Inc(p); + end; + end else if CharInW(p, AExpects) then + Break + else + Inc(p, CharSizeW(p)); + end; + Result := p - ps; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function CharUpperA(c: AnsiChar): AnsiChar; +begin + {$IFNDEF NEXTGEN} + if (c>=#$61) and (c<=#$7A) then + {$ELSE} + if (c>=$61) and (c<=$7A) then + {$ENDIF} + Result := AnsiChar(Ord(c)-$20) + else + Result := c; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function CharUpperW(c: WideChar): WideChar; +begin + if (c>=#$61) and (c<=#$7A) then + Result := WideChar(PWord(@c)^-$20) + else + Result := c; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function StartWith(s, startby: PJSONChar; AIgnoreCase: Boolean): Boolean; +begin + while (s^<>#0) and (startby^<>#0) do begin + if AIgnoreCase then begin + {$IFDEF JSON_UNICODE} + if CharUpperW(s^) <> CharUpperW(startby^) then + {$ELSE} + if CharUpperA(s^) <> CharUpperA(startby^) then + {$ENDIF} + Break; + end else if s^ <> startby^ then + Break; + Inc(s); + Inc(startby); + end; + Result := startby^ = #0; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function StartWithIgnoreCase(s, startby: PJSONChar): Boolean; +begin + while (s^<>#0) and (startby^<>#0) do begin + {$IFDEF JSON_UNICODE} + if CharUpperW(s^) <> CharUpperW(startby^) then + {$ELSE} + if CharUpperA(s^) <> CharUpperA(startby^) then + {$ENDIF} + Break; + Inc(s); + Inc(startby); + end; + Result := startby^ = #0; +end; +{$ENDIF} + +function HashOf(const Key: Pointer; KeyLen: Cardinal): Cardinal; +var + ps: PCardinal; + lr: Cardinal; +begin + Result := 0; + if KeyLen > 0 then begin + ps := Key; + lr := (KeyLen and $03);//鳤ǷΪ4 + KeyLen := (KeyLen and $FFFFFFFC);// + while KeyLen > 0 do begin + Result := ((Result shl 5) or (Result shr 27)) xor ps^; + Inc(ps); + Dec(KeyLen, 4); + end; + if lr <> 0 then begin + case lr of + 1: KeyLen := PByte(ps)^; + 2: KeyLen := PWORD(ps)^; + 3: KeyLen := PWORD(ps)^ or (PByte(Cardinal(ps) + 2)^ shl 16); + end; + Result := ((Result shl 5) or (Result shr 27)) xor KeyLen; + end; + end; +end; + +{$IFNDEF USEYxdStr} +function IsHexChar(c: JSONChar): Boolean; inline; +begin + Result:=((c>='0') and (c<='9')) or + ((c>='a') and (c<='f')) or + ((c>='A') and (c<='F')); +end; + +function HexValue(c: JSONChar): Integer; +begin + if (c>='0') and (c<='9') then + Result := Ord(c) - Ord('0') + else if (c>='a') and (c<='f') then + Result := 10+ Ord(c)-Ord('a') + else + Result := 10+ Ord(c)-Ord('A'); +end; + +function HexChar(v: Byte): JSONChar; +begin + if v<10 then + Result := JSONChar(v + Ord('0')) + else + Result := JSONChar(v-10 + Ord('A')); +end; + +function BinToHex(p:Pointer;l:Integer): JSONString; +const + B2HConvert: array[0..15] of JSONChar = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); +var + pd: PJSONChar; + pb: PByte; +begin + SetLength(Result, l shl 1); + pd := PJSONChar(Result); + pb := p; + while l>0 do begin + pd^ := B2HConvert[pb^ shr 4]; + Inc(pd); + pd^ := B2HConvert[pb^ and $0F]; + Inc(pd); + Inc(pb); + Dec(l); + end; +end; + +function BinToHex(const ABytes:TBytes): JSONString; +begin + Result:=BinToHex(@ABytes[0], Length(ABytes)); +end; + +procedure HexToBin(p: Pointer; l: Integer; var AResult: TBytes); +var + ps: PJSONChar; + pd: PByte; +begin + SetLength(AResult, l shr 1); + ps := p; + pd := @AResult[0]; + while ps - p < l do begin + if IsHexChar(ps[0]) and IsHexChar(ps[1]) then begin + pd^:=(HexValue(ps[0]) shl 4) + HexValue(ps[1]); + Inc(pd); + Inc(ps, 2); + end else begin + SetLength(AResult, 0); + Exit; + end; + end; +end; + +function HexToBin(const S: JSONString): TBytes; +begin + HexToBin(PJSONChar(S), System.Length(S), Result); +end; + +procedure HexToBin(const S: JSONString; var AResult: TBytes); +begin + HexToBin(PJSONChar(S), System.Length(S), AResult); +end; +{$ENDIF} + +function ParseHex(var p:PJSONChar;var Value:Int64):Integer; +var + ps: PJSONChar; +begin + Value := 0; + ps := p; + while IsHexChar(p^) do begin + Value := (Value shl 4) + HexValue(p^); + Inc(p); + end; + Result := p - ps; +end; + +function ParseInt(var s:PJSONChar; var ANum:Int64):Integer; +var + ps: PJSONChar; + ANeg: Boolean; +begin + ps := s; + //16ƿʼַ + if s^ = '$' then begin + Inc(s); + Result := ParseHex(s, ANum); + end else if (s^='0') and ((s[1]='x') or (s[1]='X')) then begin + Inc(s, 2); + Result := ParseHex(s, ANum); + end else begin + if (s^='-') then begin + ANeg := True; + Inc(s); + end else begin + ANeg := False; + if s^='+' then + Inc(s); + end; + ANum := 0; + while (s^>='0') and (s^<='9') do begin + ANum := ANum * 10 + Ord(s^)-Ord('0'); + Inc(s); + end; + if ANeg then + ANum := -ANum; + Result := s - ps; + end; +end; + +function ParseNumeric(var s: PJSONChar; var ANum:Extended): Boolean; +var + ps: PJSONChar; + + function ParseHexInt(var s: PJSONChar):Boolean; + var + iVal:Int64; + begin + iVal:=0; + while IsHexChar(s^) do begin + iVal := (iVal shl 4) + HexValue(s^); + Inc(s); + end; + Result := (s<>ps); + ANum := iVal; + end; + + function ParseDec(var s: PJSONChar): Boolean; + var + ACount:Integer; + iVal:Int64; + APow:Extended; + ANeg: Boolean; + begin + ANeg := S^ = '-'; + if ANeg then + Inc(S); + ParseInt(s, iVal); + if ANeg then + ANum := -iVal + else + ANum := iVal; + if s^='.' then begin //С + Inc(s); + ACount := ParseInt(s, iVal); + if ACount > 0 then begin + if ANum < 0 then + ANum := ANum - iVal / IntPower(10, ACount) + else + ANum := ANum + iVal / IntPower(10, ACount); + end; + end; + if (s^='e') or (s^='E') then begin + Inc(s); + if ParseNumeric(s, APow) then + ANum := ANum * Power(10, APow); + end; + Result := (s <> ps); + end; + +begin + ps := s; + if (S^ = '$') or (S^ = '&') then begin + Inc(s); + Result := ParseHexInt(s); + Exit; + end else if (s^='0') and ((s[1]='x') or (s[1]='X')) then begin + Inc(s, 2); + Result := ParseHexInt(s); + Exit; + end else + Result := ParseDec(s); +end; + +function ParseDateTime(s: PJSONChar; var AResult:TDateTime):Boolean; +var + Y,M,D,H,N,Sec,MS: Word; + AQuoter: JSONChar; + ADate: TDateTime; + + function ParseNum(var n:Word):Boolean; + var + neg: Boolean; + ps: PJSONChar; + begin + n := 0; ps := s; + if s^ = '-' then begin + neg := true; + Inc(s); + end else + neg:=false; + while s^<>#0 do begin + if (s^>='0') and (s^<='9') then begin + n:=n*10+Ord(s^)-48; + Inc(s); + end else + Break; + end; + if neg then + n := -n; + Result := ps <> s; + end; + +begin + if (s^='"') or (s^='''') then begin + AQuoter := s^; + Inc(s); + end else + AQuoter:=#0; + Result := ParseNum(Y); + if not Result then + Exit; + if s^='-' then begin + Inc(s); + Result:=ParseNum(M); + if (not Result) or (s^<>'-') then + Exit; + Inc(s); + Result:=ParseNum(D); + if (not Result) or ((s^<>'T') and (s^<>' ') and (s^<>#0)) then + Exit; + if s^<>#0 then Inc(s); + Result := TryEncodeDate(Y,M,D,ADate); + if not Result then + Exit; + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(s); + if s^<>#0 then begin + if not ParseNum(H) then begin //ûʱֵ + AResult:=ADate; + Exit; + end; + if s^<>':' then begin + if H in [0..23] then + AResult := ADate + EncodeTime(H,0,0,0) + else + Result:=False; + Exit; + end; + Inc(s); + end else begin + AResult:=ADate; + Exit; + end; + end else if s^=':' then begin + ADate:=0; + H:=Y; + Inc(s); + end else begin + Result:=False; + Exit; + end; + if H>23 then begin + Result:=False; + Exit; + end; + if not ParseNum(N) then begin + if AQuoter<>#0 then begin + if s^=AQuoter then + AResult:=ADate+EncodeTime(H,0,0,0) + else + Result:=False; + end else + AResult:=ADate+EncodeTime(H,0,0,0); + Exit; + end else if N>59 then begin + Result:=False; + Exit; + end; + Sec:=0; + MS:=0; + if s^=':' then begin + Inc(s); + if not ParseNum(Sec) then begin + if AQuoter<>#0 then begin + if s^=AQuoter then + AResult:=ADate+EncodeTime(H,N,0,0) + else + Result:=False; + end else + AResult:=ADate+EncodeTime(H,N,0,0); + Exit; + end else if Sec>59 then begin + Result:=False; + Exit; + end; + if s^='.' then begin + Inc(s); + if not ParseNum(MS) then begin + if AQuoter<>#0 then begin + if AQuoter=s^ then + AResult:=ADate+EncodeTime(H,N,Sec,0) + else + Result:=False; + end else + AResult:=ADate+EncodeTime(H,N,Sec,0); + Exit; + end else if MS>=1000 then begin//1000΢ΪλʱģתΪ + while MS>=1000 do + MS:=MS div 10; + end; + if AQuoter<>#0 then begin + if AQuoter=s^ then + AResult:=ADate+EncodeTime(H,N,Sec,MS) + else + Result:=False; + Exit; + end else + AResult:=ADate+EncodeTime(H,N,Sec,MS); + end else begin + if AQuoter<>#0 then begin + if AQuoter=s^ then + AResult:=ADate+EncodeTime(H,N,Sec,0) + else + Result:=False; + end else + AResult:=ADate+EncodeTime(H,N,Sec,0) + end; + end else begin + if AQuoter<>#0 then begin + if AQuoter=s^ then + AResult:=ADate+EncodeTime(H,N,0,0) + else + Result:=False; + end else + AResult:=ADate+EncodeTime(H,N,0,0); + end; +end; + +function ParseWebTime(p:PJSONChar;var AResult:TDateTime):Boolean; +var + I:Integer; + Y,M,D,H,N,S:Integer; +const + MonthNames:array [0..11] of JSONString=('Jan', 'Feb', 'Mar', 'Apr', 'May', + 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); + Comma: PJSONChar = ','; + Digits: PJSONChar = '0123456789'; +begin + //ڣֱͨڼҪ + {$IFDEF JSON_UNICODE}SkipUntilW{$ELSE}SkipUntilA{$ENDIF}(p, Comma, #0); + if p^=#0 then begin + Result:=false; + Exit; + end else + Inc(p); + {$IFDEF JSON_UNICODE}SkipUntilW{$ELSE}SkipUntilA{$ENDIF}(p, Digits, #0); + D := 0; + // + while (p^>='0') and (p^<='9') do begin + D:=D*10+Ord(p^)-Ord('0'); + Inc(p); + end; + if (D<1) or (D>31) then begin + Result:=false; + Exit; + end; + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + M:=0; + for I := 0 to 11 do begin + if StartWith(p, PJSONChar(MonthNames[I]),true) then begin + M:=I+1; + Break; + end; + end; + if (M<1) or (M>12) then begin + Result:=False; + Exit; + end; + while (p^<>#0) and ((p^<'0') or (p^>'9')) do + Inc(p); + Y:=0; + while (p^>='0') and (p^<='9') do begin + Y:=Y*10+Ord(p^)-Ord('0'); + Inc(p); + end; + while p^=' ' do Inc(p); + H:=0; + while (p^>='0') and (p^<='9') do begin + H:=H*10+Ord(p^)-Ord('0'); + Inc(p); + end; + while p^=':' do Inc(p); + N:=0; + while (p^>='0') and (p^<='9') do begin + N:=N*10+Ord(p^)-Ord('0'); + Inc(p); + end; + while p^=':' do Inc(p); + S:=0; + while (p^>='0') and (p^<='9') do begin + S:=S*10+Ord(p^)-Ord('0'); + Inc(p); + end; + while p^=':' do Inc(p); + Result := TryEncodeDateTime(Y,M,D,H,N,S,0,AResult); +end; + +function ParseJsonTime(p: PJSONChar; var ATime: TDateTime): Boolean; +var + MS, TimeZone: Int64; +begin + // JavascriptڸʽΪ/DATE(1970.1.1ڵĺ+ʱ)/ + Result := False; + if not StartWith(p, '/DATE', False) then + Exit; + Inc(p, 5); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + if p^ <> '(' then + Exit; + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + if ParseInt(p, MS) = 0 then + Exit; + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + if (p^ = '+') or (p^ = '-') then begin + if ParseInt(p, TimeZone) = 0 then + Exit; + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + end else + TimeZone := 0; + if p^ = ')' then begin + ATime := (MS div 86400000) + ((MS mod 86400000) / 86400000.0); + if TimeZone <> 0 then + ATime := IncHour(ATime, -TimeZone); + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + Result := True + end; +end; + +{$IFNDEF USEYxdStr} +function AnsiEncode(p:PWideChar; l:Integer): AnsiString; +var + ps: PWideChar; + len: Integer; +begin + if l<=0 then begin + ps:=p; + while ps^<>#0 do Inc(ps); + l:=ps-p; + end; + if l>0 then begin + {$IFDEF MSWINDOWS} + len := WideCharToMultiByte(CP_ACP,0,p,l,nil,0,nil,nil); + SetLength(Result, len); + WideCharToMultiByte(CP_ACP,0,p,l,PAnsiChar(Result), len, nil, nil); + {$ELSE} + Result.Length:=l shl 1; + Result.FValue[0]:=0; + Move(p^,PAnsiChar(Result)^,l shl 1); + Result:=TEncoding.Convert(TEncoding.Unicode,TEncoding.ANSI,Result.FValue,1,l shl 1); + {$ENDIF} + end else + Result := ''; +end; + +function AnsiEncode(const p: JSONStringW):AnsiString; +begin + Result := AnsiEncode(PWideChar(p), Length(p)); +end; + +{$IFNDEF MSWINDOWS} +function AnsiDecode(const S: AnsiString): JSONStringW; +begin + if S.IsUtf8 then + Result := Utf8Decode(S) + else + Result := TEncoding.ANSI.GetString(S.FValue, 1, S.Length); +end; +{$ENDIF} + +function AnsiDecode(p: PAnsiChar; l:Integer): JSONStringW; +var + ps: PAnsiChar; +{$IFNDEF MSWINDOWS} + ABytes:TBytes; +{$ENDIF} +begin + if l<=0 then begin + ps := p; + while ps^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do Inc(ps); + l:=IntPtr(ps)-IntPtr(p); + end; + if l>0 then begin + {$IFDEF MSWINDOWS} + System.SetLength(Result, MultiByteToWideChar(CP_ACP,0,PAnsiChar(p),l,nil,0)); + MultiByteToWideChar(CP_ACP, 0, PAnsiChar(p),l,PWideChar(Result),Length(Result)); + {$ELSE} + System.SetLength(ABytes, l); + Move(p^, PByte(@ABytes[0])^, l); + Result := TEncoding.ANSI.GetString(ABytes); + {$ENDIF} + end else + System.SetLength(Result,0); +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +procedure SaveTextA(AStream: TStream; const S: AnsiString); +begin + AStream.WriteBuffer(PAnsiChar(S)^, Length(S)) +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +function Utf8Encode(const p: JSONStringW): AnsiString; +begin + Result:=Utf8Encode(PWideChar(p), Length(p)); +end; + +function Utf8Encode(p:PWideChar; l:Integer): AnsiString; +var + ps:PWideChar; + pd,pds:PAnsiChar; + c:Cardinal; +begin + if p=nil then + Result := '' + else begin + if l<=0 then begin + ps:=p; + while ps^<>#0 do + Inc(ps); + l:=ps-p; + end; + {$IFDEF NEXTGEN} + Result.Length:=l*6; + {$ELSE} + SetLength(Result, l*6);//UTF8ÿַ6ֽڳ,һԷ㹻Ŀռ + {$ENDIF} + if l>0 then begin + Result[1] := {$IFDEF NEXTGEN}1{$ELSE}#1{$ENDIF}; + ps:=p; + pd:=PAnsiChar(Result); + pds:=pd; + while l>0 do begin + c:=Cardinal(ps^); + Inc(ps); + if (c>=$D800) and (c<=$DFFF) then begin//Unicode չַ + c:=(c-$D800); + if (ps^>=#$DC00) and (ps^<=#$DFFF) then begin + c:=$10000+((c shl 10) + (Cardinal(ps^)-$DC00)); + Inc(ps); + Dec(l); + end else + raise Exception.Create(Format(SBadUnicodeChar,[IntPtr(ps^)])); + end; + Dec(l); + if c=$0 then begin + if JavaFormatUtf8 then begin//Javaʽ룬#$0ַΪ#$C080 + pd^:={$IFDEF NEXTGEN}$C0{$ELSE}#$C0{$ENDIF}; + Inc(pd); + pd^:={$IFDEF NEXTGEN}$80{$ELSE}#$80{$ENDIF}; + Inc(pd); + end else begin + pd^:=AnsiChar(c); + Inc(pd); + end; + end else if c<=$7F then begin //1B + pd^:=AnsiChar(c); + Inc(pd); + end else if c<=$7FF then begin//$80-$7FF,2B + pd^:=AnsiChar($C0 or (c shr 6)); + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F)); + Inc(pd); + end else if c<=$FFFF then begin //$8000 - $FFFF,3B + pd^:=AnsiChar($E0 or (c shr 12)); + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F)); + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F)); + Inc(pd); + end else if c<=$1FFFFF then begin //$01 0000-$1F FFFF,4B + pd^:=AnsiChar($F0 or (c shr 18));//1111 0xxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 12) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F));//10 xxxxxx + Inc(pd); + end else if c<=$3FFFFFF then begin//$20 0000 - $3FF FFFF,5B + pd^:=AnsiChar($F8 or (c shr 24));//1111 10xx + Inc(pd); + pd^:=AnsiChar($F0 or ((c shr 18) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 12) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F));//10 xxxxxx + Inc(pd); + end else if c<=$7FFFFFFF then begin //$0400 0000-$7FFF FFFF,6B + pd^:=AnsiChar($FC or (c shr 30));//1111 11xx + Inc(pd); + pd^:=AnsiChar($F8 or ((c shr 24) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($F0 or ((c shr 18) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 12) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F));//10 xxxxxx + Inc(pd); + end; + end; + pd^:={$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF}; + {$IFDEF NEXTGEN} + Result.Length := IntPtr(pd)-IntPtr(pds); + {$ELSE} + SetLength(Result, IntPtr(pd)-IntPtr(pds)); + {$ENDIF} + end; + end; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} {$IFNDEF MSWINDOWS} +function Utf8Decode(const S: AnsiString): JSONStringW; overload; +begin + if S.IsUtf8 then + Result := Utf8Decode(PAnsiChar(S), S.Length) + else + Result := AnsiDecode(S); +end; +{$ENDIF} {$ENDIF} + +{$IFNDEF USEYxdStr} +function Utf8Decode(p: PAnsiChar; l: Integer): JSONStringW; +var + ps,pe: PByte; + pd,pds: PWord; + c: Cardinal; +begin + if l<=0 then begin + ps:=PByte(p); + while ps^<>0 do Inc(ps); + l := Integer(ps) - Integer(p); + end; + ps := PByte(p); + pe := ps; + Inc(pe, l); + System.SetLength(Result, l); + pd := PWord(PWideChar(Result)); + pds := pd; + while Integer(ps)0 then begin + if (ps^ and $FC)=$FC then begin //4000000+ + c:=(ps^ and $03) shl 30; + Inc(ps); + c:=c or ((ps^ and $3F) shl 24); + Inc(ps); + c:=c or ((ps^ and $3F) shl 18); + Inc(ps); + c:=c or ((ps^ and $3F) shl 12); + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + c:=c-$10000; + pd^:=$D800+((c shr 10) and $3FF); + Inc(pd); + pd^:=$DC00+(c and $3FF); + Inc(pd); + end else if (ps^ and $F8)=$F8 then begin //200000-3FFFFFF + c:=(ps^ and $07) shl 24; + Inc(ps); + c:=c or ((ps^ and $3F) shl 18); + Inc(ps); + c:=c or ((ps^ and $3F) shl 12); + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + c:=c-$10000; + pd^:=$D800+((c shr 10) and $3FF); + Inc(pd); + pd^:=$DC00+(c and $3FF); + Inc(pd); + end else if (ps^ and $F0)=$F0 then begin //10000-1FFFFF + c:=(ps^ and $0F) shr 18; + Inc(ps); + c:=c or ((ps^ and $3F) shl 12); + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + c:=c-$10000; + pd^:=$D800+((c shr 10) and $3FF); + Inc(pd); + pd^:=$DC00+(c and $3FF); + Inc(pd); + end else if (ps^ and $E0)=$E0 then begin //800-FFFF + c:=(ps^ and $1F) shl 12; + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + pd^:=c; + Inc(pd); + end else if (ps^ and $C0)=$C0 then begin //80-7FF + pd^:=(ps^ and $3F) shl 6; + Inc(ps); + pd^:=pd^ or (ps^ and $3F); + Inc(pd); + Inc(ps); + end else + raise Exception.Create(Format('ЧUTF8ַ:%d',[Integer(ps^)])); + end else begin + pd^ := ps^; + Inc(ps); + Inc(pd); + end; + end; + System.SetLength(Result, (Integer(pd)-Integer(pds)) shr 1); +end; +{$ENDIF} + +function DecodeToken(var p: PJSONChar; ADelimiter, AQuoter: JSONChar; AIgnoreSpace: Boolean): JSONString; +var + s: PJSONChar; +begin + if AIgnoreSpace then + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + s := p; + while p^ <> #0 do begin + if p^ = AQuoter then begin //õݲ + Inc(p); + while p^<>#0 do begin + if p^=#$5C then begin + Inc(p); + if p^<>#0 then + Inc(p); + end else if p^=AQuoter then begin + Inc(p); + if p^=AQuoter then + Inc(p) + else + Break; + end else + Inc(p); + end; + end else if p^ = ADelimiter then + Break + else + Inc(p); + end; + SetLength(Result, p-s); + Move(s^, PJSONChar(Result)^, (p-s){$IFDEF JSON_UNICODE} shl 1{$ENDIF}); + if p^ = ADelimiter then + Inc(p); +end; + +{$IFNDEF USEYxdStr} +procedure SaveTextU(AStream: TStream; const S: AnsiString; AWriteBom: Boolean); + + procedure WriteBom; + var + ABom:TBytes; + begin + SetLength(ABom,3); + ABom[0]:=$EF; + ABom[1]:=$BB; + ABom[2]:=$BF; + AStream.WriteBuffer(ABom[0],3); + end; + + procedure SaveAnsi; + var + T: AnsiString; + begin + T := {$IFDEF USEYxdStr}YxdStr.{$ELSE}YxdJson.{$ENDIF}Utf8Encode({$IFDEF NEXTGEN}AnsiDecode(S){$ELSE}JSONString(S){$ENDIF}); + AStream.WriteBuffer(PAnsiChar(T)^, Length(T)); + end; + +begin + if AWriteBom then + WriteBom; + SaveAnsi; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +procedure SaveTextW(AStream: TStream; const S: JSONStringW; AWriteBom: Boolean); + procedure WriteBom; + var + bom: Word; + begin + bom := $FEFF; + AStream.WriteBuffer(bom, 2); + end; +begin + if AWriteBom then + WriteBom; + AStream.WriteBuffer(PWideChar(S)^, System.Length(S) shl 1); +end; +{$ENDIF} + +{$IFNDEF USEYxdStr} +procedure SaveTextWBE(AStream: TStream; const S: JSONStringW; AWriteBom: Boolean); +var + pw, pe: PWord; + w: Word; + ABuilder: TStringCatHelper; +begin + pw := PWord(PWideChar(S)); + pe := pw; + Inc(pe, Length(S)); + ABuilder := TStringCatHelper.Create(IntPtr(pe)-IntPtr(pw)); + try + while IntPtr(pw)1 then begin + I := ACharSize-2; + if ((Utf8Masks[I] and ps^) = Utf8Masks[I]) then begin + Inc(ps); + Result:=True; + for I := 1 to ACharSize-1 do begin + if (ps^ and $80)<>$80 then begin + Result:=False; + Break; + end; + Inc(ps); + end; + end; + end; + end; + +begin + Result := teAnsi; + b := false; + if L >= 2 then begin + pAnsi := PByte(p); + pWide := PWideChar(p); + b := True; + if pWide^ = #$FEFF then + Result := teUnicode16LE + else if pWide^ = #$FFFE then + Result := teUnicode16BE + else if L >= 3 then begin + if (pAnsi^ = $EF) and (PByte(IntPtr(pAnsi) + 1)^ = $BB) and + (PByte(IntPtr(pAnsi) + 2)^ = $BF) then // UTF-8 + Result := teUTF8 + else begin// ַǷзUFT-8ַ11... + b := false; + Result := teUTF8;//ļΪUTF8룬ȻǷвUTF-8 + I := 0; + Dec(L, 2); + while I<=L do begin + if (pAnsi^ and $80) <> 0 then begin // λΪ1 + if IsUtf8Order(AUtf8CharSize) then begin + if AUtf8CharSize>2 then//ִ2ֽڳȵUTF8У99%UTF-8ˣж + Break; + Inc(pAnsi,AUtf8CharSize); + Inc(I,AUtf8CharSize); + end else begin + Result:=teAnsi; + Break; + end; + end else begin + if pAnsi^=0 then begin //00 xx (xx<128) λǰBE + if PByte(IntPtr(pAnsi)+1)^<128 then begin + Result := teUnicode16BE; + Break; + end; + end else if PByte(IntPtr(pAnsi)+1)^=0 then begin//xx 00 λǰLE + Result:=teUnicode16LE; + Break; + end; + Inc(pAnsi); + Inc(I); + end; + end; + end; + end; + end; +end; +{$ENDIF} + +procedure ExchangeByteOrder(p:PAnsiChar; l:Integer); +var + pe: PAnsiChar; + c: AnsiChar; +begin + pe := p; + Inc(pe,l); + while IntPtr(p) 0 then begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + if AEncoding in [teUnknown,teAuto] then + AEncoding := DetectTextEncoding(@ABuffer[0], ASize, ABomExists); + if AEncoding=teAnsi then + Result := AnsiString(ABuffer) + else if AEncoding = teUTF8 then begin + if ABomExists then + Result := AnsiEncode( + {$IFDEF USEYxdStr}YxdStr.Utf8Decode{$ELSE}Utf8Decode{$ENDIF}(@ABuffer[3], ASize-3)) + else + Result := AnsiEncode( + {$IFDEF USEYxdStr}YxdStr.Utf8Decode{$ELSE}Utf8Decode{$ENDIF}(@ABuffer[0], ASize)); + end + else begin + if AEncoding = teUnicode16BE then + ExchangeByteOrder(@ABuffer[0],ASize); + if ABomExists then + Result := AnsiEncode(PWideChar(@ABuffer[2]), (ASize-2) shr 1) + else + Result := AnsiEncode(PWideChar(@ABuffer[0]), ASize shr 1); + end; + end else + Result := ''; +end; + +function LoadTextU(AStream: TStream; AEncoding: TTextEncoding): AnsiString; +var + ASize: Integer; + ABuffer: TBytes; + ABomExists: Boolean; + P: PAnsiChar; +begin + ASize := AStream.Size - AStream.Position; + if ASize>0 then begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + if AEncoding in [teUnknown, teAuto] then + AEncoding:=DetectTextEncoding(@ABuffer[0],ASize,ABomExists) + else if ASize>=2 then begin + case AEncoding of + teUnicode16LE: + ABomExists:=(ABuffer[0]=$FF) and (ABuffer[1]=$FE); + teUnicode16BE: + ABomExists:=(ABuffer[1]=$FE) and (ABuffer[1]=$FF); + teUTF8: + begin + if ASize>3 then + ABomExists:=(ABuffer[0]=$EF) and (ABuffer[1]=$BB) and (ABuffer[2]=$BF) + else + ABomExists:=False; + end; + end; + end else + ABomExists:=False; + if AEncoding=teAnsi then + Result := {$IFDEF USEYxdStr}YxdStr.Utf8Encode{$ELSE}YxdJson.Utf8Encode{$ENDIF} + (AnsiDecode(@ABuffer[0], ASize)) + else if AEncoding = teUTF8 then begin + if ABomExists then begin + Dec(ASize, 3); + {$IFDEF NEXTGEN} + Result.From(@ABuffer[0], 3, ASize); + {$ELSE} + SetLength(Result, ASize); + P := @ABuffer[0]; + Inc(P, 3); + Move(P^, PAnsiChar(@Result[1])^, ASize); + {$ENDIF} + end else + Result := AnsiString(ABuffer); + end else begin + if AEncoding=teUnicode16BE then + ExchangeByteOrder(@ABuffer[0],ASize); + if ABomExists then + Result := Utf8Encode(PWideChar(@ABuffer[2]), (ASize-2) shr 1) + else + Result := Utf8Encode(PWideChar(@ABuffer[0]), ASize shr 1); + end; + end + else + Result := ''; +end; + +function LoadTextW(AStream: TStream; AEncoding: TTextEncoding): JSONStringW; +var + ASize: Integer; + ABuffer: TBytes; + ABomExists: Boolean; +begin + ASize := AStream.Size - AStream.Position; + if ASize>0 then begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + if AEncoding in [teUnknown, teAuto] then + AEncoding := DetectTextEncoding(@ABuffer[0], ASize, ABomExists) + else if ASize>=2 then begin + case AEncoding of + teUnicode16LE: + ABomExists:=(ABuffer[0]=$FF) and (ABuffer[1]=$FE); + teUnicode16BE: + ABomExists:=(ABuffer[1]=$FE) and (ABuffer[1]=$FF); + teUTF8: + begin + if ASize>3 then + ABomExists := (ABuffer[0]=$EF) and (ABuffer[1]=$BB) and (ABuffer[2]=$BF) + else + ABomExists := False; + end; + end; + end else + ABomExists:=False; + if AEncoding = teAnsi then + Result := AnsiDecode(@ABuffer[0], ASize) + else if AEncoding = teUTF8 then begin + if ABomExists then + Result := Utf8Decode(@ABuffer[3], ASize-3) + else + Result := Utf8Decode(@ABuffer[0], ASize); + end else begin + if AEncoding = teUnicode16BE then + ExchangeByteOrder(@ABuffer[0], ASize); + if ABomExists then begin + Dec(ASize, 2); + SetLength(Result, ASize shr 1); + Move(ABuffer[2], PWideChar(Result)^, ASize); + end else begin + SetLength(Result, ASize shr 1); + Move(ABuffer[0], PWideChar(Result)^, ASize); + end; + end; + end else + Result := ''; +end; +{$ENDIF} + +{$IFNDEF USEYxdStr}{$IFDEF NEXTGEN} +{ AnsiString } +procedure AnsiString.From(p: PAnsiChar; AOffset, ALen: Integer); +begin + SetLength(ALen); + Inc(P, AOffset); + Move(P^, PAnsiChar(@FValue[1])^,ALen); +end; + +function AnsiString.GetChars(AIndex: Integer): AnsiChar; +begin + if (AIndex<0) or (AIndex>=Length) then + raise Exception.CreateFmt(SOutOfIndex,[AIndex,0,Length-1]); + Result:=FValue[AIndex+1]; +end; + +class operator AnsiString.Implicit(const S: JSONStringW): AnsiString; +begin + Result := AnsiEncode(S); +end; + +class operator AnsiString.Implicit(const S: AnsiString): PAnsiChar; +begin + Result:=PansiChar(@S.FValue[1]); +end; + +function AnsiString.GetIsUtf8: Boolean; +begin + if System.Length(FValue)>0 then + Result:=(FValue[0]=1) + else + Result:=False; +end; + +function AnsiString.GetLength: Integer; +begin + //FValue[0]ͣ0-ANSI,1-UTF8ĩβַ\0 + Result := System.Length(FValue); + if Result>=2 then + Dec(Result,2) + else + Result:=0; +end; + +class operator AnsiString.Implicit(const S: AnsiString): TBytes; +var + L:Integer; +begin + L:=System.Length(S.FValue)-1; + System.SetLength(Result,L); + if L>0 then + Move(S.FValue[1],Result[0],L); +end; + +procedure AnsiString.SetChars(AIndex: Integer; const Value: AnsiChar); +begin + if (AIndex<0) or (AIndex>=Length) then + raise Exception.CreateFmt(SOutOfIndex,[AIndex,0,Length-1]); + FValue[AIndex+1]:=Value; +end; + +procedure AnsiString.SetLength(const Value: Integer); +begin + if Value<0 then begin + if System.Length(FValue)>0 then + System.SetLength(FValue,1) + else begin + System.SetLength(FValue,1); + FValue[0]:=0;//ANSI + end; + end else begin + System.SetLength(FValue,Value+2); + FValue[Value+1]:=0; + end; +end; + +class operator AnsiString.Implicit(const ABytes: TBytes): AnsiString; +var + L:Integer; +begin + L:=System.Length(ABytes); + Result.Length:=L; + if L>0 then + Move(ABytes[0],Result.FValue[1],L); +end; + +class operator AnsiString.Implicit(const S: AnsiString): JSONStringW; +begin + Result := AnsiDecode(S); +end; +{$ENDIF} {$ENDIF} + +{ JSONValue } + +procedure JSONValue.CopyValue(ASource: PJSONValue); +var + l: Integer; +begin + L := Length(ASource.FValue); + FType := ASource.FType; + SetLength(FValue, L); + if L > 0 then + Move(ASource.FValue[0], FValue[0], L); +end; + +procedure JSONValue.Free; +begin + if (FType = jdtObject) and (FObject <> nil) then + FObject.Free; +end; + +function JSONValue.GetAsBoolean: Boolean; +begin + if High(FValue) > -1 then + Result := PBoolean(@FValue[0])^ + else Result := False; +end; + +function JSONValue.GetAsByte: Byte; +begin + Result := GetAsInt64(); +end; + +function JSONValue.GetAsDateTime: TDateTime; +begin + if (FType = jdtFloat) or (FType = jdtDateTime) then + Result := GetAsFloat + else if FType = jdtString then begin + if not(ParseDateTime(PJSONChar(GetString), Result) or + ParseJsonTime(PJSONChar(GetString), Result) or ParseWebTime(PJSONChar(GetString), Result)) then + raise Exception.Create(Format(SBadConvert, ['String', 'DateTime'])) + end else if FType = jdtInteger then + Result := AsInt64 + else + raise Exception.Create(Format(SBadConvert, [JsonTypeName[Integer(FType)], 'DateTime'])); +end; + +function JSONValue.GetAsDouble: Double; +begin + Result := GetAsFloat; +end; + +function JSONValue.GetAsFloat: Extended; +begin + case FType of + jdtFloat, jdtDateTime: + begin + if Length(FValue) = 8 then + Result := PDouble(@FValue[0])^ + else if Length(FValue) >= SizeOf(Extended) then + Result := PExtended(@FValue[0])^ + else + Result := 0; + end; + jdtString: + Result := StrToFloatDef(GetString(), 0); + jdtInteger: + begin + case High(FValue) of + 3: Result := PInteger(@FValue[0])^; + 7: Result := PInt64(@FValue[0])^; + 0: Result := PShortInt(@FValue[0])^; + 1: Result := PSmallInt(@FValue[0])^; + else + Result := 0; + end; + end; + jdtBoolean: + Result := Integer(AsBoolean); + else + Result := 0; + end; +end; + +function JSONValue.GetAsInt64: Int64; +begin + case FType of + jdtInteger: + begin + case High(FValue) of + 3: Result := PInteger(@FValue[0])^; + 7: Result := PInt64(@FValue[0])^; + 0: Result := PShortInt(@FValue[0])^; + 1: Result := PSmallInt(@FValue[0])^; + else + Result := 0; + end; + end; + jdtString: + Result := StrToIntDef(GetString(), 0); + jdtFloat: + begin + if Length(FValue) = 8 then + Result := Trunc(PDouble(@FValue[0])^) + else if Length(FValue) >= SizeOf(Extended) then + Result := Trunc(PExtended(@FValue[0])^) + else + Result := 0; + end; + jdtDateTime: + Result := Trunc(AsDateTime); + jdtBoolean: + Result := Integer(AsBoolean); + else + Result := 0; + end; +end; + +function JSONValue.GetAsInteger: Integer; +begin + Result := GetAsInt64; +end; + +function JSONValue.GetAsJSONArray: JSONArray; +begin + if (FObject <> nil) and (FObject.GetIsArray) then + Result := JSONArray(FObject) + else + Result := nil; +end; + +function JSONValue.GetAsJSONObject: JSONObject; +begin + if (FObject <> nil) and (not FObject.GetIsArray) then + Result := JSONObject(FObject) + else + Result := nil; +end; + +function JSONValue.GetAsString: JSONString; +begin + Result := ToString(); +end; + +function JSONValue.GetAsVariant: Variant; +var + I: Integer; +begin + case FType of + jdtString: Result := AsString; + jdtInteger: Result := AsInt64; + jdtFloat: Result := AsFloat; + jdtBoolean: Result := AsBoolean; + jdtDateTime: Result := AsFloat; + jdtObject: + begin + if Assigned(FObject) then begin + Result := VarArrayCreate([0, FObject.Count - 1], varVariant); + for I := 0 to FObject.Count - 1 do + Result[I] := FObject.Items[I].AsVariant; + end else + Result := varEmpty; + end; + else + Result := varEmpty; + end; +end; + +function JSONValue.GetAsWord: Word; +begin + Result := GetAsInt64; +end; + +function JSONValue.GetObject: JSONBase; +begin + if FType = jdtObject then + Result := FObject + else + Result := nil; +end; + +function JSONValue.GetPath(const ADelimiter: JSONChar): JSONString; +begin + if Assigned(FObject) then + Result := FObject.GetPath(ADelimiter) + else + Result := ''; +end; + +function JSONValue.GetSize: Cardinal; +begin + Result := Length(FValue); +end; + +function JSONValue.GetString: string; +begin + {$IFDEF JSON_UNICODE} + SetString(Result, PJSONChar(FValue), System.Length(FValue) shr 1); + {$ELSE} + Result := JSONString(FValue); + SetLength(Result, System.Length(FValue)); + {$ENDIF} +end; + +procedure JSONValue.SetAsBoolean(const Value: Boolean); +begin + SetLength(FValue, SizeOf(Value)); + PBoolean(@FValue[0])^ := Value; + FType := jdtBoolean; +end; + +procedure JSONValue.SetAsByte(const Value: Byte); +begin + SetLength(FValue, SizeOf(Value)); + FValue[0] := Value; + FType := jdtInteger; +end; + +procedure JSONValue.SetAsDateTime(const Value: TDateTime); +begin + SetLength(FValue, SizeOf(Value)); + PDouble(@FValue[0])^ := Value; + FType := jdtDateTime; +end; + +procedure JSONValue.SetAsDouble(const Value: Double); +begin + SetLength(FValue, SizeOf(Value)); + PDouble(@FValue[0])^ := Value; + FType := jdtFloat; +end; + +procedure JSONValue.SetAsDWORD(const Value: Cardinal); +begin + SetLength(FValue, SizeOf(Value)); + PCardinal(@FValue[0])^ := Value; + FType := jdtInteger; +end; + +procedure JSONValue.SetAsFloat(const Value: Extended); +begin + SetLength(FValue, SizeOf(Value)); + PExtended(@FValue[0])^ := Value; + FType := jdtFloat; +end; + +procedure JSONValue.SetAsInt64(const Value: Int64); +begin + SetLength(FValue, SizeOf(Value)); + PInt64(@FValue[0])^ := Value; + FType := jdtInteger; +end; + +procedure JSONValue.SetAsInteger(const Value: Integer); +begin + SetLength(FValue, SizeOf(Value)); + PInteger(@FValue[0])^ := Value; + FType := jdtInteger; +end; + +procedure JSONValue.SetAsJSONArray(const Value: JSONArray); +begin + SetLength(FValue, 0); + FObject := Value; + FType := jdtObject; +end; + +procedure JSONValue.SetAsJSONObject(const Value: JSONObject); +begin + SetLength(FValue, 0); + FObject := Value; + FType := jdtObject; +end; + +procedure JSONValue.SetAsString(const Value: JSONString); +begin + if Length(Value) > 0 then begin + {$IFDEF JSON_UNICODE} + SetLength(FValue, (Length(Value) shl 1)); + Move(PJSONChar(Value)^, FValue[0], Length(Value) shl 1); + {$ELSE} + //SetLength(FValue, (Length(Value) + 1)); + SetLength(FValue, Length(Value)); + Move(Value[1], FValue[0], Length(Value)); + {$ENDIF} + end else + SetLength(FValue, 0); + FType := jdtString; +end; + +procedure JSONValue.SetAsVariant(const Value: Variant); + + procedure SetVariantArray(); + var + I: Integer; + P: JSONBase; + begin + if Length(FValue) <> 0 then SetLength(FValue, 0); + if (Assigned(FObject)) and (not FObject.GetIsArray) then begin + P := FObject.FParent; + FreeAndNil(FObject); + end else + P := nil; + if not Assigned(FObject) then begin + FObject := JSONArray.Create; + FObject.FParent := P; + end else + FObject.Clear; + FType := jdtObject; + FObject.FValue := @Self; + for I := VarArrayLowBound(Value, VarArrayDimCount(Value)) + to VarArrayHighBound(Value, VarArrayDimCount(Value)) do + JSONArray(FObject).add(Value[I]); + end; + +begin + case FindVarData(Value)^.VType of + varBoolean: SetAsBoolean(Value); + varByte: SetAsByte(Value); + varWord: SetAsWord(Value); + varSmallint: SetAsInteger(Value); + varInteger, + varShortInt: SetAsInteger(Value); + varLongWord: SetAsDWORD(Value); + varInt64: SetAsInt64(Value); + varSingle: SetAsDouble(Value); + varDouble: SetAsDouble(Value); + varDate: SetAsDateTime(Value); + varCurrency: SetAsFloat(Value); + varOleStr, varString: SetAsString(VarToStrDef(Value, '')); + else begin + if VarIsArray(Value) then begin + SetVariantArray(); + end else begin + SetLength(FValue, 0); + FType := jdtNull; + end; + end; + end; +end; + +procedure JSONValue.SetAsWord(const Value: Word); +begin + SetLength(FValue, SizeOf(Value)); + PWord(@FValue[0])^ := Value; + FType := jdtInteger; +end; + +{$IFDEF JSON_RTTI} +function JSONValue.ToObjectValue: TValue; +begin + Result := TYxdSerialize.writeToValue(@Self); +end; +{$ENDIF} + +function BoolToStr(const v: Boolean): JSONString; inline; +begin + if v then Result := 'true' else Result := 'false'; +end; + +function FloatToStr(const value: Extended): string; inline; +var + Buffer: array[0..63] of Char; + P: PChar; + I: Integer; +begin + I := FloatToText(Buffer, Value, fvExtended, ffGeneral, 15, 0); + P := StrScan(@Buffer[0], '.'); + if (P <> nil) then begin + if I - (P - @Buffer[0] + 1) > JsonFloatDigits then begin + I := P - @Buffer[0] + JsonFloatDigits; + while Buffer[i] = '0' do Dec(I); + SetString(Result, Buffer, I + 1) + end else + SetString(Result, Buffer, I); + end else + SetString(Result, Buffer, I); +end; + +function JSONValue.ToString(AIndent: Integer; ADoEscape: Boolean): JSONString; +begin + case FType of + jdtString: + Result := GetString(); + jdtInteger: + Result := IntToStr(AsInteger); + jdtFloat: + Result := FloatToStr(AsFloat); + jdtBoolean: + Result := BoolToStr(AsBoolean); + jdtObject: + Result := JSONBase.Encode(FObject, AIndent, ADoEscape); + jdtDateTime: + Result := ValueAsDateTime(JsonDateFormat, JsonTimeFormat, JsonDateTimeFormat); + jdtNull, jdtUnknown: + Result := 'null'; + end; +end; + +function JSONValue.ToString: JSONString; +begin + Result := ToString(0); +end; + +function JSONValue.ValueAsDateTime(const DateFormat, TimeFormat, + DateTimeFormat: JSONString): JSONString; +var + ADate: Integer; + AValue: Double; +begin + AValue := AsDateTime; + ADate := Trunc(AValue); + if SameValue(ADate, 0) then begin //DateΪ0ʱ + if SameValue(AValue, 0) then + Result := FormatDateTime(DateFormat, AValue) + else + Result := FormatDateTime(TimeFormat, AValue); + end else begin + if SameValue(AValue-ADate, 0) then + Result := FormatDateTime(DateFormat, AValue) + else + Result := FormatDateTime(DateTimeFormat, AValue); + end; +end; + +{ JSONEnumerator } + +constructor JSONEnumerator.Create(AList: JSONBase); +begin + FList := AList; + FIndex := -1; +end; + +function JSONEnumerator.GetCurrent: PJSONValue; +begin + Result := FList[FIndex]; +end; + +function JSONEnumerator.MoveNext: Boolean; +begin + if FIndex < FList.Count - 1 then begin + Inc(FIndex); + Result := True; + end else + Result := False; +end; + +{ JSONBase } + +procedure JSONBase.Assign(ANode: JSONBase); +begin + Self.parse(ANode.toString()); +end; + +class procedure JSONBase.BuildJsonString(ABuilder: TStringCatHelper; var p: PJSONChar); +var + AQuoter: JSONChar; + ps: PJSONChar; +begin + ABuilder.Position := 0; + if (p^='"') or (p^='''') then begin + AQuoter := p^; + Inc(p); + ps := p; + while p^<>#0 do begin + if (p^ = AQuoter) then begin + if ps <> p then + ABuilder.Cat(ps, p-ps); + if p[1] = AQuoter then begin + ABuilder.Cat(AQuoter); + Inc(p, 2); + ps := p; + end else begin + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + ps := p; + Break; + end; + end else if p^='\' then begin + if ps<>p then + ABuilder.Cat(ps, p-ps); + {$IFDEF JSON_UNICODE} + ABuilder.Cat(CharUnescape(p)); + {$ELSE} + CharUnescape(ABuilder, p); + {$ENDIF} + ps := p; + end else + Inc(p); + end; + if ps <> p then + ABuilder.Cat(ps, p-ps); + end else begin + while p^<>#0 do begin + if (p^=':') or (p^=']') or (p^=',') or (p^='}') then + Break + else + ABuilder.Cat(p,1); + Inc(p); + end + end; +end; + +{$IFDEF JSON_UNICODE} +class function JSONBase.CharUnescape(var p: PJSONChar): JSONChar; +{$ELSE} +class procedure JSONBase.CharUnescape(ABuilder: TStringCatHelper; var p: PJSONChar); +{$ENDIF} + + function DecodeOrd: Integer; + var + C:Integer; + begin + Result := 0; + C := 0; + while (p^<>#0) and (C<4) do begin + if IsHexChar(p^) then + Result := (Result shl 4) + HexValue(p^) + else + Break; + Inc(p); + Inc(C); + end + end; + +begin + if p^=#0 then begin + {$IFDEF JSON_UNICODE} Result := #0; {$ENDIF} + Exit; + end; + if p^ <> '\' then begin + {$IFDEF JSON_UNICODE}Result := p^;{$ELSE}ABuilder.Cat(p^);{$ENDIF} + Inc(p); + Exit; + end; + Inc(p); + case p^ of + 'b': + begin + {$IFDEF JSON_UNICODE}Result := #7;{$ELSE}ABuilder.Cat(#7);{$ENDIF} + Inc(p); + end; + 't': + begin + {$IFDEF JSON_UNICODE}Result := #9;{$ELSE}ABuilder.Cat(#9);{$ENDIF} + Inc(p); + end; + 'n': + begin + {$IFDEF JSON_UNICODE}Result := #10;{$ELSE}ABuilder.Cat(#10);{$ENDIF} + Inc(p); + end; + 'f': + begin + {$IFDEF JSON_UNICODE}Result := #12;{$ELSE}ABuilder.Cat(#12);{$ENDIF} + Inc(p); + end; + 'r': + begin + {$IFDEF JSON_UNICODE}Result := #13;{$ELSE}ABuilder.Cat(#13);{$ENDIF} + Inc(p); + end; + '\': + begin + {$IFDEF JSON_UNICODE}Result := '\';{$ELSE}ABuilder.Cat('\');{$ENDIF} + Inc(p); + end; + '''': + begin + {$IFDEF JSON_UNICODE}Result := '''';{$ELSE}ABuilder.Cat('''');{$ENDIF} + Inc(p); + end; + '"': + begin + {$IFDEF JSON_UNICODE}Result := '"';{$ELSE}ABuilder.Cat('"');{$ENDIF} + Inc(p); + end; + 'u': + begin + //\uXXXX + if IsHexChar(p[1]) and IsHexChar(p[2]) and IsHexChar(p[3]) and IsHexChar(p[4]) then begin + {$IFDEF JSON_UNICODE} + Result := JSONChar((HexValue(p[1]) shl 12) or (HexValue(p[2]) shl 8) or + (HexValue(p[3]) shl 4) or HexValue(p[4])); + {$ELSE} + ABuilder.Cat(JSONString(WideChar((HexValue(p[1]) shl 12) or (HexValue(p[2]) shl 8) or + (HexValue(p[3]) shl 4) or HexValue(p[4])))); + {$ENDIF} + Inc(p, 5); + end else + raise Exception.CreateFmt(SCharNeeded, ['0-9A-Fa-f', StrDup(p,0,4)]); + end; + '/': + begin + {$IFDEF JSON_UNICODE}Result := '/';{$ELSE}ABuilder.Cat('/');{$ENDIF} + Inc(p); + end + else begin + if StrictJson then + raise Exception.CreateFmt(SCharNeeded, ['btfrn"u''/', StrDup(p,0,4)]) + else begin + {$IFDEF JSON_UNICODE}Result := p^;{$ELSE}ABuilder.Cat(p^);{$ENDIF} + Inc(p); + end; + end; + end; +end; + +procedure JSONBase.Clear; +var + I: Integer; + Item: PJSONValue; +begin + if FItems.Count > 0 then begin + for I := 0 to FItems.Count - 1 do begin + Item := FItems.Items[i]; + if (Item <> nil) then begin + Item.Free; + Dispose(Item); + end; + end; + FItems.Clear; + end; +end; + +function JSONBase.Copy: JSONBase; +begin + if GetIsArray then + Result := JSONBase.ParseArray(ToString(0)) + else + Result := JSONBase.ParseObject(ToString(0)); +end; + +{$IFDEF UNICODE} +function JSONBase.CopyIf(const ATag: Pointer; AFilter: JSONFilterEventA): JSONBase; + + procedure NestCopy(AParentSource, AParentDest: JSONBase); + var + Accept: Boolean; + AChildSource, AChildDest: PJSONValue; + I: Integer; + begin + for I := 0 to AParentSource.Count - 1 do begin + Accept := True; + AChildSource := AParentSource[I]; + AFilter(Self, AChildSource, Accept, ATag); + if Accept then begin + if Assigned(AChildSource.FObject) and (AChildSource.FType = jdtObject) then begin + if AChildSource.FObject.GetIsArray then + NestCopy(AChildSource.FObject, AParentDest.NewChildArray(AChildSource.FName)) + else + NestCopy(AChildSource.FObject, AParentDest.NewChildObject(AChildSource.FName)) + end else begin + AChildDest := JSONObject(AParentDest).add(AChildSource.FName); + AChildDest.CopyValue(AChildSource); + end; + end; + end; + end; + +begin + if Assigned(AFilter) then begin + if GetIsArray then + Result := JSONArray.Create + else + Result := JSONObject.Create; + NestCopy(Self, Result); + end else + Result := Copy; +end; +{$ENDIF} + +function JSONBase.CopyIf(const ATag: Pointer; AFilter: JSONFilterEvent): JSONBase; + + procedure NestCopy(AParentSource, AParentDest: JSONBase); + var + Accept: Boolean; + AChildSource, AChildDest: PJSONValue; + I: Integer; + begin + for I := 0 to AParentSource.Count - 1 do begin + Accept := True; + AChildSource := AParentSource[I]; + AFilter(Self, AChildSource, Accept, ATag); + if Accept then begin + if Assigned(AChildSource.FObject) and (AChildSource.FType = jdtObject) then begin + if AChildSource.FObject.GetIsArray then + NestCopy(AChildSource.FObject, AParentDest.NewChildArray(AChildSource.FName)) + else + NestCopy(AChildSource.FObject, AParentDest.NewChildObject(AChildSource.FName)) + end else begin + AChildDest := JSONObject(AParentDest).add(AChildSource.FName); + AChildDest.CopyValue(AChildSource); + end; + end; + end; + end; + +begin + if Assigned(AFilter) then begin + if GetIsArray then + Result := JSONArray.Create + else + Result := JSONObject.Create; + NestCopy(Self, Result); + end else + Result := Copy; +end; + +constructor JSONBase.Create; +begin + FData := nil; + FParent := nil; + FValue := nil; + {$IFDEF UNICODE} + FItems := TList.Create; + {$ELSE} + FItems := JSONList.Create; + {$ENDIF} +end; + +procedure JSONBase.Decode(p: PJSONChar; len: Integer); + procedure DecodeCopy; + var + S: JSONString; + begin + S := StrDup(p, 0, len); + p := PJSONChar(S); + DecodeObject(p); + end; +begin + Clear; + if (len>0) and (p[len] <> #0) then + DecodeCopy + else + DecodeObject(p); +end; + +procedure JSONBase.Decode(const s: JSONString); +begin + Decode(PJSONChar(S), Length(S)); +end; + +procedure JSONBase.DecodeObject(var p: PJSONChar); +var + ABuilder: TStringCatHelper; + ps: PJSONChar; + ErrCode: Integer; +begin + ABuilder := TStringCatHelper.Create; + ps := p; + try + try + {$IFDEF JSON_UNICODE}SkipSpaceW(p);{$ELSE}SkipSpaceA(p);{$ENDIF} + ErrCode := ParseJsonPair(ABuilder, p); + if ErrCode <> 0 then + RaiseParseException(ErrCode, ps, p); + finally + ABuilder.Free; + end; + except on E:Exception do + raise Exception.Create(FormatParseError(EParse_Unknown, E.Message, ps, p)); + end; +end; + +{$IFDEF UNICODE} +procedure JSONBase.DeleteIf(const ATag: Pointer; ANest: Boolean; AFilter: JSONFilterEventA); + procedure DeleteChildren(AParent: JSONBase); + var + I: Integer; + Accept: Boolean; + AChild: PJSONValue; + begin + I := AParent.Count - 1; + while I >= 0 do begin + Accept := True; + AChild := AParent.Items[I]; + if ANest and (Assigned(AChild.FObject) and (AChild.FType = jdtObject)) then + DeleteChildren(AChild.FObject); + AFilter(Self, AChild, Accept, ATag); + if Accept then + AParent.Remove(I); + Dec(I); + end; + end; + +begin + if Assigned(AFilter) then + DeleteChildren(Self) + else + Clear; +end; +{$ENDIF} + +procedure JSONBase.DeleteIf(const ATag: Pointer; ANest: Boolean; AFilter: JSONFilterEvent); + procedure DeleteChildren(AParent: JSONBase); + var + I: Integer; + Accept: Boolean; + AChild: PJSONValue; + begin + I := AParent.Count - 1; + while I >= 0 do begin + Accept := True; + AChild := AParent.Items[I]; + if ANest and (Assigned(AChild.FObject) and (AChild.FType = jdtObject)) then + DeleteChildren(AChild.FObject); + AFilter(Self, AChild, Accept, ATag); + if Accept then + AParent.Remove(I); + Dec(I); + end; + end; + +begin + if Assigned(AFilter) then + DeleteChildren(Self) + else + Clear; +end; + +destructor JSONBase.Destroy; +begin + Clear; + FItems.Free; + if (FParent = nil) and (FValue <> nil) and (FValue.FType = jdtUnknown) then + Dispose(FValue); // JSONΪʱFValueΪnilʱͷFValue + inherited; +end; + +class function JSONBase.Encode(Obj: JSONBase; AIndent: Integer; ADoEscape: Boolean): JSONString; +var + ABuilder: TStringCatHelper; +begin + if Obj = nil then Exit; + ABuilder := TStringCatHelper.Create; + try + InternalEncode(Obj, ABuilder, AIndent, ADoEscape); + ABuilder.Back(1); //ɾһ + Result := ABuilder.Value; + finally + ABuilder.Free; + end; +end; + +function JSONBase.Encode(AIndent: Integer; ADoEscape: Boolean): JSONString; +begin + Encode(Self, AIndent, ADoEscape); +end; + +function JSONBase.Exist(const Key: JSONString): Boolean; +begin + Result := IndexOf(Key) > -1; +end; + +function JSONBase.FormatParseError(ACode: Integer; AMsg: JSONString; ps, + p: PJSONChar): JSONString; +var + ACol, ARow: Integer; + ALine: JSONString; +begin + if ACode<>0 then begin + p := {$IFDEF JSON_UNICODE}StrPosW{$ELSE}StrPosA{$ENDIF}(ps, p, ACol, ARow); + ALine := {$IFDEF JSON_UNICODE}DecodeLineW{$ELSE}DecodeLineA{$ENDIF}(p, False); + if Length(ALine) > 200 then + ALine := StrDup(PJSONChar(ALine), 0, 200) + '...'; + Result:=Format(SJsonParseError,[ARow, ACol, AMsg, ALine]); + end else + Result := ''; +end; + +function JSONBase.GetCount: Integer; +begin + Result := FItems.Count; +end; + +function JSONBase.GetEnumerator: JSONEnumerator; +begin + Result := JSONEnumerator.Create(Self); +end; + +function JSONBase.GetIsArray: Boolean; +begin + Result := False; +end; + +function JSONBase.GetIsJSONArray: Boolean; +begin + Result := GetIsArray; +end; + +function JSONBase.GetIsJSONObject: Boolean; +begin + Result := not GetIsArray; +end; + +function JSONBase.GetItemIndex: Integer; +var + I: Integer; +begin + Result := -1; + if Assigned(Parent) then begin + for I := 0 to Parent.GetCount - 1 do begin + if Parent[i].FObject = Self then begin + Result := I; + Break; + end; + end; + end; +end; + +function JSONBase.GetItems(Index: Integer): PJSONValue; +begin + Result := FItems.Items[index]; +end; + +function JSONBase.GetName: JSONString; +begin + if FValue = nil then + Result := '' + else + Result := FValue.FName; +end; + +function JSONBase.GetValue: JSONString; +begin + Result := Encode(Self); +end; + +function JSONBase.IndexOf(const Key: JSONString): Integer; +var + Item: PJSONValue; + AHash: Cardinal; + I, l: Integer; +begin + Result := -1; + l := Length(Key); + if l > 0 then + AHash := HashOf(PJSONChar(Key), l{$IFDEF JSON_UNICODE} shl 1{$ENDIF}) + else + AHash := 0; + for I := 0 to FItems.Count - 1 do begin + Item := FItems.Items[i]; + if Length(Item.FName) = l then begin + if JsonCaseSensitive then begin + if Item.FNameHash = 0 then + Item.FNameHash := HashOf(PJSONChar(Item.FName), l{$IFDEF JSON_UNICODE} shl 1{$ENDIF}); + if (Item.FNameHash = AHash) and (Item.FName = Key) then begin + Result := I; + Break; + end; + end else if StartWithIgnoreCase(PJSONChar(Item.FName), PJSONChar(Key)) then begin + Result := I; + Break; + end; + end; + end; +end; + +class function JSONBase.InternalEncode(Obj: JSONBase; ABuilder: TStringCatHelper; + AIndent: Integer; ADoEscape: Boolean): TStringCatHelper; +const + CharStringStart: PJSONChar = '"'; + CharStringEnd: PJSONChar = '",'; + CharNameEnd: PJSONChar = '":'; + CharArrayStart: PJSONChar = '['; + CharArrayEnd: PJSONChar = '],'; + CharObjectStart: PJSONChar = '{'; + CharObjectEnd: PJSONChar = '},'; + CharObjectEmpty: PJSONChar = '{} '; + CharNull: PJSONChar = 'null,'; + CharFalse: PJSONChar = 'false,'; + CharTrue: PJSONChar = 'true,'; + CharComma: PJSONChar = ','; + CharNum0: PJSONChar = '0'; + CharNum1: PJSONChar = '1'; + Char7: PJSONChar = '\b'; + Char9: PJSONChar = '\t'; + Char10: PJSONChar = '\n'; + Char12: PJSONChar = '\f'; + Char13: PJSONChar = '\r'; + CharQuoter: PJSONChar = '\"'; + CharBackslash: PJSONChar = '\\'; + CharCode: PJSONChar = '\u00'; + CharEscape: PJSONChar = '\u'; + + procedure CatValue(const AValue: JSONString); + var + ps: PJSONChar; + {$IFNDEF JSON_UNICODE}w: Word;{$ENDIF} + begin + ps := PJSONChar(AValue); + while ps^ <> #0 do begin + case ps^ of + #7: ABuilder.Cat(Char7, 2); + #9: ABuilder.Cat(Char9, 2); + #10: ABuilder.Cat(Char10, 2); + #12: ABuilder.Cat(Char12, 2); + #13: ABuilder.Cat(Char13, 2); + '\': ABuilder.Cat(CharBackslash, 2); + '"': ABuilder.Cat(CharQuoter, 2); + else begin + if ps^ < #$1F then begin + ABuilder.Cat(CharCode, 4); + if ps^ > #$F then + ABuilder.Cat(CharNum1, 1) + else + ABuilder.Cat(CharNum0, 1); + ABuilder.Cat(HexChar(Ord(ps^) and $0F)); + end else if (ps^ <= #$7E) or (not ADoEscape) then//Ӣַ + ABuilder.Cat(ps, 1) + else + {$IFDEF JSON_UNICODE} + ABuilder.Cat(CharEscape, 2).Cat( + HexChar((PWord(ps)^ shr 12) and $0F)).Cat( + HexChar((PWord(ps)^ shr 8) and $0F)).Cat( + HexChar((PWord(ps)^ shr 4) and $0F)).Cat( + HexChar(PWord(ps)^ and $0F)); + {$ELSE} + begin + w := PWord(AnsiDecode(ps, 2))^; + ABuilder.Cat(CharEscape, 2).Cat( + HexChar((w shr 12) and $0F)).Cat( + HexChar((w shr 8) and $0F)).Cat( + HexChar((w shr 4) and $0F)).Cat( + HexChar(w and $0F)); + Inc(ps); + end; + {$ENDIF} + end; + end; + Inc(ps); + end; + end; + + procedure StrictJsonTime(ATime:TDateTime); + const + JsonTimeStart: PJSONChar = '"/DATE('; + JsonTimeEnd: PJSONChar = ')/"'; + var + MS: Int64;//ʱϢ + begin + MS := Trunc(ATime * 86400000); + ABuilder.Cat(JsonTimeStart, 7); + ABuilder.Cat(IntToStr(MS)); + ABuilder.Cat(JsonTimeEnd, 3); + end; + + procedure DoEncode(ANode: JSONBase; ALevel:Integer); + var + I: Integer; + Item: PJSONValue; + ArrayWraped, IsArray: Boolean; + begin + if ANode.FItems.Count > 0 then begin + + ArrayWraped := False; + if ANode.GetIsArray then begin + IsArray := True; + ABuilder.Cat(CharArrayStart, 1); + end else begin + IsArray := False; + ABuilder.Cat(CharObjectStart, 1); + end; + + for I := 0 to ANode.FItems.Count - 1 do begin + Item := ANode.FItems[I]; + if Item = nil then Continue; + if (AIndent > 0) and ((not IsArray) or (Item.FType = jdtObject)) then begin + ABuilder.Cat(SLineBreak); + ABuilder.Space(AIndent * (ALevel + 1)); + end; + if Length(item.FName) > 0 then begin + ABuilder.Cat(CharStringStart, 1); + CatValue(item.FName); + ABuilder.Cat(CharNameEnd, 2); + end; + case Item.FType of + jdtObject: + begin + if Item.FObject <> nil then begin + if not Item.FObject.GetIsArray then begin + if (not IsArray) and (Length(Item.FName) = 0) then + raise Exception.CreateFmt(SObjectChildNeedName, [Item.FName, I]); + end else + ArrayWraped := True; + DoEncode(Item.FObject, ALevel+1); + end; + end; + jdtString: + begin + ABuilder.Cat(CharStringStart, 1); + CatValue(Item.AsString); + ABuilder.Cat(CharStringEnd, 2); + end; + jdtInteger: + begin + ABuilder.Cat(IntToStr(Item.AsInt64)); + ABuilder.Cat(CharComma, 1); + end; + jdtFloat: + begin + ABuilder.Cat(FloatToStr(Item.AsFloat)); + ABuilder.Cat(CharComma, 1); + end; + jdtBoolean: + begin + ABuilder.Cat(BoolToStr(Item.AsBoolean)); + ABuilder.Cat(CharComma, 1); + end; + jdtDateTime: + begin + ABuilder.Cat(CharStringStart, 1); + if StrictJson then + StrictJsonTime(Item.AsDateTime) + else + ABuilder.Cat(Item.ToString); + ABuilder.Cat(CharStringEnd, 1); + ABuilder.Cat(CharComma, 1); + end; + jdtNull, jdtUnknown: + ABuilder.Cat(CharNull, 5); + end; + end; + ABuilder.Back(1); + end else if Assigned(ANode.FParent) then begin + ABuilder.Cat(CharNull, 5); + Exit; + end else begin + ABuilder.Cat(CharObjectEmpty, 3); + Exit; + end; + + if IsArray then begin + if ArrayWraped and (AIndent > 0) then begin + ABuilder.Cat(SLineBreak); + ABuilder.Space(AIndent * ALevel); + end; + ABuilder.Cat(CharArrayEnd, 2); + end else begin + if AIndent > 0 then begin + ABuilder.Cat(SLineBreak); + ABuilder.Space(AIndent * ALevel); + end; + ABuilder.Cat(CharObjectEnd, 2); + end; + end; +begin + Result := ABuilder; + DoEncode(Obj, 0); +end; + +{$IFDEF JSON_RTTI} +function JSONBase.Invoke(AInstance: TValue): TValue; +var + AMethods: TArray; + AParams: TArray; + AMethod: TRttiMethod; + AType: TRttiType; + AContext: TRttiContext; + AParamValues: array of TValue; + I, c: Integer; + AParamItem: PJSONValue; +begin + AContext := TRttiContext.Create; + Result := TValue.Empty; + if AInstance.IsObject then + AType := AContext.GetType(AInstance.AsObject.ClassInfo) + else if AInstance.IsClass then + AType := AContext.GetType(AInstance.AsClass) + else if AInstance.Kind = tkRecord then + AType := AContext.GetType(AInstance.TypeInfo) + else + AType := AContext.GetType(AInstance.TypeInfo); + AMethods := AType.GetMethods(GetName); + c := Count; + for AMethod in AMethods do begin + AParams := AMethod.GetParameters; + if Length(AParams) = c then begin + SetLength(AParamValues, c); + for I := 0 to c - 1 do begin + AParamItem := JSONObject(Self).getItem(AParams[I].Name); + if AParamItem <> nil then + AParamValues[I] := AParamItem.ToObjectValue + else + raise Exception.CreateFmt(SParamMissed, [AParams[I].Name]); + end; + Result := AMethod.Invoke(AInstance, AParamValues); + Exit; + end; + end; + raise Exception.CreateFmt(SMethodMissed,[Name]); +end; +{$ENDIF} + +function JSONBase.ItemByPath(const APath: JSONString; const ADelimiter: JSONChar): PJSONValue; +var + AParent: JSONBase; + AName: JSONString; + p, pn, ws: PJSONChar; + AIndex: Int64; + I, L: Integer; +begin + AParent := Self; + p := PJSONChar(APath); + Result := nil; + while Assigned(AParent) and (p^ <> #0) do begin + AName := DecodeToken(p, ADelimiter, JSONChar(0), False); + if Length(AName) > 0 then begin + l := Length(AName); + AIndex := -1; + pn := PJSONChar(AName); + if (pn[l - 1] = ']') then begin // ҵ飿 + repeat + if pn[l] = '[' then begin + ws := pn + l + 1; + ParseInt(ws, AIndex); + Break; + end else + Dec(l); + until l = 0; + if l > 0 then begin + AName := StrDupX(pn, l); + I := AParent.IndexOf(AName); + if (I > -1) then begin + Result := AParent.FItems.items[I]; + if (Assigned(Result.AsJsonArray)) and (AIndex >= 0) and (AIndex < Result.FObject.Count) then begin + Result := Result.FObject.FItems[AIndex]; + AParent := Result.FObject; + end else + Break; + end else + Break; + end else + Break; + end else begin + I := AParent.IndexOf(AName); + if (I > -1) then begin + Result := AParent.FItems.Items[I]; + AParent := Result.FObject; + end else begin + Result := nil; + Break; + end; + end; + if p^ = ADelimiter then + Inc(p); + end; + end; + if p^ <> #0 then + Result := nil; +end; + +{$IFDEF USERegEx} +function JSONBase.ItemByRegex(const ARegex: JSONString; AList: JSONList; + ANest: Boolean): Integer; +var + ANode: PJSONValue; + APcre: TPerlRegEx; + + function InternalFind(AParent: JSONBase): Integer; + var + I: Integer; + begin + Result := 0; + for I := 0 to AParent.Count - 1 do begin + ANode := AParent.Items[I]; + APcre.Subject := ANode.FName; + if APcre.Match then begin + AList.Add(ANode); + Inc(Result); + end; + if ANest and (Assigned(ANode.FObject) and (ANode.FType = jdtObject)) then + Inc(Result, InternalFind(ANode.FObject)); + end; + end; +begin + APcre := TPerlRegEx.Create; + try + APcre.RegEx := ARegex; + APcre.Compile; + Result := InternalFind(Self); + finally + APcre.Free; + end; +end; +{$ENDIF} + +procedure JSONBase.LoadFromFile(const AFileName: JSONString; AEncoding: TTextEncoding); +var + AStream: TFileStream; +begin + if not FileExists(AFileName) then Exit; + AStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(AStream, AEncoding); + finally + AStream.Free; + end; +end; + +procedure JSONBase.LoadFromStream(AStream: TStream; AEncoding: TTextEncoding); +var + S: JSONString; +begin + S := {$IFDEF JSON_UNICODE}LoadTextW{$ELSE}LoadTextA{$ENDIF}(AStream, AEncoding); + if Length(S) > 1 then + Decode(PJSONChar(S), Length(S)) + else + raise Exception.Create(SBadJson); +end; + +function JSONBase.NewChildArray(const key: JSONString): JSONArray; +var + Item: PJSONValue; +begin + Result := JSONArray.Create; + Result.FParent := Self; + New(Item); + Item.FName := key; + Item.FNameHash := 0; + Item.FObject := Result; + Item.FType := jdtObject; + FItems.Add(Item); + Result.FValue := Item; +end; + +function JSONBase.NewChildObject(const key: JSONString): JSONObject; +var + Item: PJSONValue; +begin + Result := JSONObject.Create; + Result.FParent := Self; + New(Item); + Item.FName := key; + Item.FNameHash := 0; + Item.FObject := Result; + Item.FType := jdtObject; + FItems.Add(Item); + Result.FValue := Item; +end; + +function JSONBase.Next: PJSONValue; +var + I: Integer; +begin + Result := nil; + if Assigned(Parent) then begin + for I := 0 to Parent.GetCount - 1 do begin + if Parent.GetItems(i).FObject = Self then begin + if I + 1 < Parent.GetCount then + Result := Parent.GetItems(i + 1); + Break; + end; + end; + end; +end; + +function JSONBase.ParseValue(ABuilder: TStringCatHelper; + var p: PJSONChar; const FName: JSONString): Integer; +const + JsonEndChars: PJSONChar = ',}]'; +var + ANum: Extended; +begin + Result := 0; + if p^ = '"' then begin + BuildJsonString(ABuilder, p); + JSONObject(Self).put(FName, ABuilder); + end else if p^='''' then begin + if StrictJson then begin + Result := EParse_BadStringStart; + Exit; + end; + BuildJsonString(ABuilder, p); + JSONObject(Self).put(FName, ABuilder); + end else if ParseNumeric(p, ANum) then begin //֣ + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + if (p^ = #0) or {$IFDEF JSON_UNICODE}CharInW{$ELSE}CharInA{$ENDIF}(p, JsonEndChars) then begin + if SameValue(ANum, Trunc(ANum)) then + JSONObject(Self).put(FName, Trunc(ANum)) + else + JSONObject(Self).put(FName, ANum); + end else begin + Result := EParse_BadJson; + Exit; + end; + end else if StartWith(p, 'False', True) then begin //False + Inc(p,5); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + JSONObject(Self).put(FName, False); + end else if StartWith(p, 'True', True) then begin //True + Inc(p,4); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + JSONObject(Self).put(FName, True); + end else if StartWith(p, 'NULL', True) then begin //Null + Inc(p,4); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + JSONObject(Self).put(FName, NULL); // ѡй족 2014.08.01 + end else if p^ = '{' then begin + Result := NewChildObject(FName).ParseJsonPair(ABuilder, p); + end else if p^ = '[' then begin + Result := NewChildArray(FName).ParseJsonPair(ABuilder, p); + end else + Result := EParse_BadJson; +end; + +function JSONBase.ParseJsonPair(ABuilder: TStringCatHelper; var p: PJSONChar): Integer; + + procedure SkipComment; + begin + while p^ = '/' do begin + if StrictJson then begin + Result := EParse_CommentNotSupport; + Exit; + end; + if p[1] = '/' then begin + {$IFDEF JSON_UNICODE}SkipUntilW{$ELSE}SkipUntilA{$ENDIF}(p, #10); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + end else if p[1] = '*' then begin + Inc(p, 2); + while p^ <> #0 do begin + if (p[0] = '*') and (p[1] = '/') then begin + Inc(p, 2); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + Break; + end else + Inc(p); + end; + end else begin + Result := EParse_UnknownToken; + Exit; + end; + end; + end; + +begin + SkipComment; + if p^ = '{' then begin + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + while (p^<>#0) and (p^ <> '}') do begin + SkipComment; + // + if StrictJson and (p^ <> '"') then begin + Result := EParse_BadNameStart; + Exit; + end; + BuildJsonString(ABuilder, p); + if p^ <> ':' then begin + Result := EParse_BadNameEnd; + Exit; + end; + if ABuilder.Position = 0 then begin + Result := EParse_NameNotFound; + Exit; + end; + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + //ֵ + Result := ParseValue(ABuilder, p, ABuilder.Value); + if Result <> 0 then Exit; + if p^ = ',' then begin + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + end; + end; + + if p^ <> '}' then begin + Result := EParse_BadJson; + Exit; + end + + end else if p^ = '[' then begin + if (not Assigned(FParent)) or (not FParent.GetIsArray) then begin + if Length(GetName) = 0 then begin + Result := NewChildArray('unknown').ParseJsonPair(ABuilder, p); + Exit; + end; + end; + + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + while (p^<>#0) and (p^<>']') do begin + Result := ParseValue(ABuilder, p, ''); + if Result <> 0 then Exit; + if p^ = ',' then begin + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + end; + end; + + if p^ <> ']' then begin + Result := EParse_BadJson; + Exit; + end + + end else begin + Result := EParse_EndCharNeeded; + Exit; + end; + + Inc(p); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p); + Result := 0; +end; + +function JSONBase.parse(const text: JSONString; IgnoreZero: Boolean): Boolean; +var + I: Integer; + S: JSONString; + P, P1, PMax: PJSONChar; +begin + if Length(text) < 2 then + Result := False + else begin + P := PJSONChar(text); + if IgnoreZero then begin + P1 := P; + PMax := P + Length(text); + while (P1 < PMax) and (P1^ <> #0) do Inc(P1); + if PMax - P1 > 0 then begin // ַд #0 ʱŴһд + S := text; + for I := P1 - P + 1 to Length(S) do begin + if S[I] = #0 then + S[I] := #32; + end; + P := PJSONChar(S); + end; + end; + Decode(P, Length(text)); + Result := True; + end; +end; + +function JSONBase.parse(p: PJSONChar; len: Integer): Boolean; +begin + Decode(p, len); + Result := True; +end; + +class function JSONBase.parseArray(const text: JSONString; RaiseError: Boolean): JSONArray; +begin + Result := JSONArray.Create; + try + JSONArray(Result).parse(text); + except + FreeAndNil(Result); + if RaiseError then raise; + end; +end; + +class function JSONBase.parseObject(const text: JSONString; RaiseError: Boolean): JSONObject; +begin + Result := JSONObject.Create; + try + JSONObject(Result).parse(text); + except + FreeAndNil(Result); + if RaiseError then raise; + end; +end; + +function JSONBase.FindIf(const ATag: Pointer; ANest: Boolean; + AFilter: JSONFilterEvent): PJSONValue; + + function DoFind(AParent: JSONBase): PJSONValue; + var + I: Integer; + AChild: PJSONValue; + Accept: Boolean; + begin + Result := nil; + for I := 0 to AParent.Count - 1 do begin + AChild := AParent[I]; + Accept := True; + AFilter(Self, AChild, Accept, ATag); + if Accept then + Result := AChild + else if ANest and (Assigned(AChild.FObject)) then + Result := DoFind(AChild.FObject); + if Result <> nil then + Break; + end; + end; + +begin + if Assigned(AFilter) then + Result := DoFind(Self) + else + Result := nil; +end; + +{$IFDEF UNICODE} +function JSONBase.FindIf(const ATag: Pointer; ANest: Boolean; AFilter: JSONFilterEventA): PJSONValue; + + function DoFind(AParent: JSONBase): PJSONValue; + var + I: Integer; + AChild: PJSONValue; + Accept: Boolean; + begin + Result := nil; + for I := 0 to AParent.Count - 1 do begin + AChild := AParent[I]; + Accept := True; + AFilter(Self, AChild, Accept, ATag); + if Accept then + Result := AChild + else if ANest and (Assigned(AChild.FObject)) then + Result := DoFind(AChild.FObject); + if Result <> nil then + Break; + end; + end; + +begin + if Assigned(AFilter) then + Result := DoFind(Self) + else + Result := nil; +end; +{$ENDIF} + +function JSONBase.ForcePath(const APath: JSONString; const ADelimiter: JSONChar): PJSONValue; +var + AName: JSONString; + p, pn, ws: PJSONChar; + AParent: JSONBase; + I, L: Integer; + AIndex: Int64; +begin + p := PJSONChar(APath); + AParent := Self; + Result := nil; + while p^ <> #0 do begin + if (Result <> nil) and (Result.FObject = nil) then begin + Result.AsJsonObject := JSONObject.Create; + Result.FObject.FParent := AParent; + AParent := Result.FObject; + AParent.FValue := Result; + end; + AName := DecodeToken(p, ADelimiter, JSONChar(0), True); + I := AParent.IndexOf(AName); + if I < 0 then begin + pn := PJSONChar(AName); + l := Length(AName); + AIndex := -1; + if (pn[l - 1] = ']') then begin + repeat + if pn[l] = '[' then begin + ws := pn + l + 1; + if ParseInt(ws, AIndex) = 0 then + AIndex := -1; + Break; + end else + Dec(l); + until l = 0; + if l > 0 then begin + AName := StrDupX(pn, l); + I := AParent.IndexOf(AName); + if I < 0 then + AParent := AParent.NewChildArray(AName) + else begin + AParent := AParent{$IFDEF JSON_UNICODE}.FItems{$ENDIF}[I].AsJsonArray; + if not Assigned(AParent) then + raise Exception.CreateFmt(SBadJsonArray, [AName]); + end; + if AIndex >= 0 then begin + while AParent.Count <= AIndex do + JSONArray(AParent).Add(NULL); + Result := AParent[AIndex]; + if Assigned(Result.FObject) then + AParent := Result.FObject; + end else + Result := AParent.FValue; + end else + raise Exception.CreateFmt(SBadJsonName, [AName]); + end else begin + if (AParent.GetIsArray) then + AParent := AParent.NewChildObject(''); + Result := JSONObject(AParent).Add(AName); + end; + end else begin + Result := JSONObject(AParent).Items[I]; + AParent := Result.FObject; + if (p^ <> #0) and (not Assigned(AParent)) then + raise Exception.CreateFmt(SBadJsonName, [AName]); + end; + end; +end; + +{$IFDEF USEDBRTTI} +procedure JSONBase.PutDataSet(const Key: JSONString; aIn: TDataSet); +begin + TYxdSerialize.WriteDataSet(Self, key, aIn, 0, 0, True); +end; +{$ENDIF} + +{$IFDEF USEDBRTTI} +procedure JSONBase.PutDataSet(const Key: JSONString; aIn: TDataSet; + const PageIndex, PageSize: Integer; Base64Blob: Boolean); +begin + TYxdSerialize.WriteDataSet(Self, key, aIn, PageIndex, PageSize, Base64Blob); +end; +{$ENDIF} + +procedure JSONBase.putJSON(const key, value: JSONString; AType: JsonDataType); +var + p: PJSONChar; + Item: PJSONValue; + + procedure AddAsDateTime; + var + ATime: TDateTime; + begin + if ParseDateTime(p, ATime) then + Item.AsDateTime := ATime + else if ParseJsonTime(p, ATime) then + Item.AsDateTime := ATime + else + raise Exception.Create(SBadJsonTime); + end; + + procedure AddUnknown(); + var + I: Integer; + ABuilder: TStringCatHelper; + begin + ABuilder := TStringCatHelper.Create; + try + if (p^ = '{') then + i := NewChildObject(key).ParseJsonPair(ABuilder, p) + else if (p^ = '[') then begin + i := NewChildArray(key).ParseJsonPair(ABuilder, p) + end else + i := ParseValue(ABuilder, p, key); + if i <> 0 then + JSONObject(Self).put(key, value); + finally + ABuilder.Free; + end; + end; + +begin + p := PJSONChar(value); + if AType = jdtUnknown then begin + AddUnknown(); + end else begin + New(Item); + Item.FObject := nil; + Item.FNameHash := 0; + Item.FName := key; + FItems.Add(Item); + case AType of + jdtString: + Item.AsString := value; + jdtInteger: + Item.AsInteger := StrToInt(value); + jdtFloat: + item.AsFloat := StrToFloat(value); + jdtBoolean: + item.AsBoolean := StrToBool(value); + jdtDateTime: + AddAsDateTime; + jdtObject: + begin + if (p^ = '{') then + Item.AsJsonObject := JSONObject.parseObject(value) + else if (p^ = '[') then + Item.AsJsonArray := JSONArray.parseArray(value) + else + raise Exception.CreateFmt(SBadJsonObject, [Value]); + if Assigned(Item.FObject) then begin + Item.FObject.FValue := Item; + Item.FObject.FParent := Self; + end; + end; + end; + end; +end; + +procedure JSONBase.RaiseParseException(ACode: Integer; ps, p: PJSONChar); +begin + if ACode<>0 then begin + case ACode of + EParse_BadStringStart: + raise Exception.Create(FormatParseError(ACode,SBadStringStart,ps,p)); + EParse_BadJson: + raise Exception.Create(FormatParseError(ACode,SBadJson, ps,p)); + EParse_CommentNotSupport: + raise Exception.Create(FormatParseError(ACode,SCommentNotSupport, ps,p)); + EParse_UnknownToken: + raise Exception.Create(FormatParseError(ACode,SUnknownToken, ps,p)); + EParse_EndCharNeeded: + raise Exception.Create(FormatParseError(ACode,SEndCharNeeded, ps,p)); + EParse_BadNameStart: + raise Exception.Create(FormatParseError(ACode,SBadNameStart, ps,p)); + EParse_BadNameEnd: + raise Exception.Create(FormatParseError(ACode,SBadNameEnd, ps,p)); + EParse_NameNotFound: + raise Exception.Create(FormatParseError(ACode,SNameNotFound, ps,p)) + else + raise Exception.Create(FormatParseError(ACode,SUnknownError, ps,p)); + end; + end; +end; + +{$IFDEF USEDBRTTI} +function JSONBase.ToDataSet(aOut: TDataSet): Integer; +begin + Result := TYxdSerialize.ReadDataSet(Self, aOut); +end; +{$ENDIF} +{$IFDEF USERTTI} +procedure JSONBase.ToObject(aDest: TObject); +begin + TYxdSerialize.readObject(Self, aDest); +end; +{$ENDIF} +{$IFDEF JSON_RTTI} +procedure JSONBase.ToRecord(out aInstance: T); +begin + TYxdSerialize.readRecord(Self, aInstance); +end; +{$ENDIF} +{$IFDEF USERTTI} +procedure JSONBase.ToObjectValue(aDest: Pointer; aType: PTypeInfo); +begin + TYxdSerialize.readValue(Self, aDest, aType); +end; +{$ENDIF} +{$IFDEF JSON_RTTI} +procedure JSONBase.ToObjectValue(aInstance: TValue); +begin + TYxdSerialize.readValue(Self, aInstance); +end; +{$ENDIF} + +procedure JSONBase.Remove(Index: Integer); +var + item: PJSONValue; +begin + if (Index > -1) and (Index < FItems.Count) then begin + item := FItems.Items[index]; + if item <> nil then begin + item.Free; + Dispose(Item); + FItems.Delete(Index); + end; + end; +end; + +procedure JSONBase.RemoveObject(obj: JSONBase); +var + I: Integer; + item: PJSONValue; +begin + for I := 0 to FItems.Count - 1 do begin + item := FItems.Items[i]; + if (item <> nil) and (item.FObject = obj) then begin + obj.FParent := nil; + obj.FValue := nil; + FItems.Delete(i); + Dispose(Item); + end; + end; +end; + +procedure JSONBase.SaveToFile(const AFileName: JSONString; AIndent: Integer); +begin + SaveToFile(AFileName, AIndent, {$IFDEF JSON_UNICODE}teUnicode16LE{$ELSE}teAnsi{$ENDIF}, False); +end; + +procedure JSONBase.SaveToFile(const AFileName: JSONString; AIndent: Integer; AEncoding: TTextEncoding; + AWriteBOM: Boolean); +var + AStream: TMemoryStream; +begin + AStream := TMemoryStream.Create; + try + SaveToStream(AStream, AIndent, AEncoding, AWriteBOM); + AStream.SaveToFile(AFileName); + finally + AStream.Free; + end; +end; + +procedure JSONBase.SaveToStream(AStream: TStream; AIndent: Integer; AEncoding: TTextEncoding; + AWriteBOM: Boolean); +begin + if AEncoding = teUTF8 then + SaveTextU(AStream, {$IFDEF USEYxdStr}YxdStr.{$ELSE}YxdJson.{$ENDIF}Utf8Encode(toString(AIndent)), AWriteBom) + else if AEncoding = teAnsi then + SaveTextA(AStream, AnsiString(toString(AIndent))) + else if AEncoding = teUnicode16LE then + SaveTextW(AStream, toString(AIndent), AWriteBom) + else + SaveTextWBE(AStream, toString(AIndent), AWriteBom); +end; + +procedure JSONBase.SaveToStream(AStream: TStream; AIndent: Integer); +begin + SaveToStream(AStream, AIndent, {$IFDEF JSON_UNICODE}teUTF8{$ELSE}teAnsi{$ENDIF}, False); +end; + +class procedure JSONBase.SetJsonCaseSensitive(v: Boolean); +begin + JsonCaseSensitive := v; +end; + +procedure JSONBase.SetName(const Value: JSONString); +begin + if FValue = nil then begin + New(FValue); + FValue.FObject := nil; + FValue.FNameHash := 0; + FValue.FType := jdtUnknown; + end; + FValue.FName := Value; +end; + +procedure JSONBase.SetValue(const Value: JSONString); +begin + Decode(Value); +end; + +{$IFDEF JSON_UNICODE} +function JSONBase.toString: JSONString; +begin + Result := Encode(Self, 0); +end; +{$ENDIF} + +function JSONBase.toString(AIndent: Integer; ADoEscape: Boolean): JSONString; +begin + Result := Encode(Self, AIndent, ADoEscape); +end; + +function JSONBase.TryParse(p: PJSONChar; len: Integer): Boolean; + procedure DoTry(); + var + ABuilder: TStringCatHelper; + begin + ABuilder := TStringCatHelper.Create; + try + try + {$IFDEF JSON_UNICODE}SkipSpaceW(p);{$ELSE}SkipSpaceA(p);{$ENDIF} + Result := ParseJsonPair(ABuilder, p) = 0; + finally + ABuilder.Free; + end; + except on E:Exception do + Result := False; + end; + end; + + procedure DecodeCopy; + var + S: JSONString; + begin + S := StrDup(p, 0, len); + p := PJSONChar(S); + DoTry; + end; +begin + Clear; + if (len>0) and (p[len] <> #0) then + DecodeCopy + else + DoTry(); +end; + +function JSONBase.TryParse(const text: JSONString): Boolean; +begin + Result := TryParse(PJSONChar(text), Length(text)); +end; + +{$IFDEF USERTTI} +procedure JSONBase.putObject(const key: JSONString; aSource: TObject); +begin + TYxdSerialize.writeValue(Self, key, aSource{$IFNDEF JSON_UNICODE}, TYxdSerialize.GetObjectTypeInfo(aSource){$ENDIF}); +end; +{$ENDIF} + +{$IFDEF JSON_RTTI} +{$ENDIF} + +{$IFDEF JSON_RTTI} +procedure JSONBase.putRecord(const key: JSONString; const aSource: T); +begin + TYxdSerialize.writeValue(Self, key, @aSource, TypeInfo(T)); +end; +{$ENDIF} + +{$IFDEF JSON_RTTI} +{$ENDIF} + +{$IFDEF JSON_RTTI} +function JSONBase.ToObjectValue: TValue; +begin + Result := TYxdSerialize.writeToValue(Self); +end; +{$ENDIF} + +function JSONBase.GetPath: JSONString; +begin + Result := GetPath('\'); +end; + +function JSONBase.GetPath(const ADelimiter: JSONChar): JSONString; +var + AParent: JSONBase; +begin + Result := ''; + AParent := Self; + while Assigned(AParent) do begin + if AParent.FParent <> nil then begin + if AParent <> Self then begin + if AParent.FParent.GetIsArray then + Result := '[' + IntToStr(AParent.ItemIndex) + ']' + ADelimiter + Result + else + Result := AParent.FValue.FName + ADelimiter + Result + end else begin + if AParent.FParent.GetIsArray then + Result := '[' + IntToStr(AParent.ItemIndex) + ']' + else + Result := AParent.FValue.FName; + end; + end else + Break; + AParent := AParent.FParent; + end; +end; + +{$IFDEF USERTTI} +procedure JSONBase.putObjectValue(const key: JSONString; aSource: Pointer; + aType: PTypeInfo); +begin + TYxdSerialize.writeValue(Self, key, aSource, aType); +end; +{$ENDIF} +{$IFDEF JSON_RTTI} +procedure JSONBase.putObjectValue(const key: JSONString; aInstance: TValue); +begin + TYxdSerialize.writeValue(Self, key, aInstance); +end; +{$ENDIF} + +{ TStringCatHelper } +{$IFNDEF USEYxdStr} +function TStringCatHelper.Back(ALen: Integer): TStringCatHelper; +begin + Result := Self; + Dec(FDest, ALen); + if FDest < PJSONChar(FValue) then + FDest := PJSONChar(FValue); +end; + +function TStringCatHelper.BackIf(const s: PJSONChar): TStringCatHelper; +var + ps:PJSONChar; +begin + Result := Self; + ps := PJSONChar(FValue); + while FDest > ps do begin + {$IFDEF JSON_UNICODE} + if (FDest[-1] >= #$DC00) and (FDest[-1] <= #$DFFF) then begin + if CharIn(FDest-2, s) then + Dec(FDest, 2) + else + Break; + end else if CharIn(FDest-1,s) then + Dec(FDest) + else + Break; + {$ELSE} + if CharIn(FDest-1, s) then + Dec(FDest) + else + Break; + {$ENDIF} + end; +end; + +function TStringCatHelper.Cat(const s: JSONString): TStringCatHelper; +begin + Result := Cat(PJSONChar(s), Length(s)); +end; + +function TStringCatHelper.Cat(c: JSONChar): TStringCatHelper; +begin + if (FDest-FStart)=FSize then + NeedSize(-1); + FDest^ := c; + Inc(FDest); + Result := Self; +end; + +function TStringCatHelper.Cat(p: PJSONChar; + len: Integer): TStringCatHelper; +begin + Result := Self; + if len < 0 then begin + while p^ <> #0 do begin + if FDest-FStart >= FSize then + NeedSize(FSize + FBlockSize); + FDest^ := p^; + Inc(p); + Inc(FDest); + end; + end else begin + NeedSize(-len); + Move(p^, FDest^, len{$IFDEF JSON_UNICODE} shl 1{$ENDIF}); + Inc(FDest, len); + end; +end; + +function TStringCatHelper.Cat(const V: Boolean): TStringCatHelper; +begin + Result := Cat(BoolToStr(V)); +end; + +function TStringCatHelper.Cat(const V: Double): TStringCatHelper; +begin + Result := Cat(FloatToStr(V)); +end; + +function TStringCatHelper.Cat(const V: Int64): TStringCatHelper; +begin + Result := Cat(IntToStr(V)); +end; + +function TStringCatHelper.Cat(const V: Variant): TStringCatHelper; +begin + Result := Cat(VarToStr(V)); +end; + +function TStringCatHelper.Cat(const V: TGuid): TStringCatHelper; +begin + Result := Cat(GuidToString(V)); +end; + +function TStringCatHelper.Cat(const V: Currency): TStringCatHelper; +begin + Result := Cat(CurrToStr(V)); +end; + +constructor TStringCatHelper.Create(ASize: Integer); +begin + inherited Create; + FBlockSize := ASize; + NeedSize(FBlockSize); +end; + +destructor TStringCatHelper.Destroy; +begin + SetLength(FValue, 0); + inherited; +end; + +constructor TStringCatHelper.Create; +begin + inherited Create; + FBlockSize := 4096; + NeedSize(FBlockSize); +end; + +function TStringCatHelper.GetChars(AIndex: Integer): JSONChar; +begin + Result := FStart[AIndex]; +end; + +function TStringCatHelper.GetPosition: Integer; +begin + Result := FDest - PJSONChar(FValue); +end; + +function TStringCatHelper.GetValue: JSONString; +var + L: Integer; +begin + L := FDest - PJSONChar(FValue); + SetLength(Result, L); + Move(FStart^, PJSONChar(Result)^, L{$IFDEF JSON_UNICODE} shl 1{$ENDIF}); +end; + +procedure TStringCatHelper.NeedSize(ASize: Integer); +var + offset:Integer; +begin + offset := FDest-FStart; + if ASize < 0 then + ASize := offset - ASize; + if ASize > FSize then begin + FSize := ((ASize + FBlockSize) div FBlockSize) * FBlockSize; + SetLength(FValue, FSize); + FStart := PJSONChar(@FValue[0]); + FDest := FStart + offset; + end; +end; + +function TStringCatHelper.Space(count: Integer): TStringCatHelper; +begin +{$IFDEF JSON_UNICODE} + Result := Self; + if Count > 0 then begin + while Count>0 do begin + Cat(' '); + Dec(Count); + end; + end; +{$ELSE} + Result := Self; + if Count > 0 then begin + while Count>0 do begin + Cat(' '); + Dec(Count); + end; + end; +{$ENDIF} +end; + +procedure TStringCatHelper.SetPosition(const Value: Integer); +begin + if Value <= 0 then + FDest := PJSONChar(FValue) + else if Value>Length(FValue) then begin + NeedSize(Value); + FDest := PJSONChar(FValue) + Value; + end else + FDest := PJSONChar(FValue) + Value; +end; +{$ENDIF} + +{ JSONObject } + +procedure JSONObject.put(const key: JSONString; value: Byte); +begin + Add(Key).Asbyte := value; +end; + +procedure JSONObject.put(const key, value: JSONString); +begin + Add(Key).AsString := value; +end; + +procedure JSONObject.put(const key: JSONString; const value: Int64); +begin + Add(Key).AsInt64 := value; +end; + +procedure JSONObject.put(const key: JSONString; value: Integer); +begin + Add(Key).AsInteger := value; +end; + +procedure JSONObject.put(const key: JSONString; value: Word); +begin + Add(Key).AsWord := value; +end; + +procedure JSONObject.put(const key: JSONString; value: Cardinal); +begin + Add(Key).AsInt64 := value; +end; + +procedure JSONObject.put(const key: JSONString; value: JSONObject); +var + item: PJSONValue; +begin + item := Add(Key); + item.AsJsonObject := value; + if value.FParent <> nil then + value.FParent.RemoveObject(value) + else if (value.FValue <> nil) and (value.FValue.FType = jdtUnknown) then + Dispose(Value.FValue); + value.FParent := Self; + value.FValue := item; +end; + +function JSONObject.addChildArray(const key: JSONString): JSONArray; +begin + if Length(key) > 0 then + Result := NewChildArray(key) + else + Result := nil; +end; + +function JSONObject.Add(const Key: JSONString): PJSONValue; +begin + New(Result); + Result.FObject := nil; + Result.FNameHash := 0; + Result.FName := Key; + FItems.Add(Result); +end; + +function JSONObject.addChildArray(const key: JSONString; + AItems: array of const): JSONArray; +var + I: Integer; +begin + if Length(key) > 0 then begin + Result := NewChildArray(key); + for I := Low(AItems) to High(AItems) do begin + case AItems[I].VType of + vtInteger: + Result.add(AItems[I].VInteger); + vtBoolean: + Result.Add(AItems[I].VBoolean); + {$IFNDEF NEXTGEN} + vtChar: + Result.Add(JSONString(AItems[I].VChar)); + {$ENDIF !NEXTGEN} + vtExtended: + Result.Add(AItems[I].VExtended^); + {$IFNDEF NEXTGEN} + vtPChar: + Result.Add(JSONString(AItems[I].VPChar)); + vtString: + Result.Add(JSONString(AItems[I].VString^)); + vtAnsiString: + Result.Add(JSONString( + {$IFDEF UNICODE} + PAnsiString(AItems[I].VAnsiString)^ + {$ELSE} + AItems[I].VPChar + {$ENDIF UNICODE} + )); + vtWideString: + Result.Add(PWideString(AItems[I].VWideString)^); + {$ENDIF !NEXTGEN} + vtPointer: + Result.Add(IntPtr(AItems[I].VPointer)); + vtWideChar: + Result.Add(AItems[I].VWideChar); + vtPWideChar: + Result.Add(AItems[I].VPWideChar); + vtCurrency: + Result.Add(AItems[I].VCurrency^); + vtInt64: + Result.Add(AItems[I].VInt64^); + {$IFDEF UNICODE} // variants + vtUnicodeString: + Result.Add(AItems[I].VPWideChar); + {$ENDIF UNICODE} + vtVariant: + Result.Add(AItems[I].VVariant^); + vtObject: + begin + if TObject(AItems[I].VObject) is JSONObject then + Result.Add(TObject(AItems[I].VObject) as JSONObject) + else if TObject(AItems[I].VObject) is JSONArray then + Result.Add(TObject(AItems[I].VObject) as JSONArray) + else + raise Exception.Create(Format(SUnsupportArrayItem, [I])); + end + else + raise Exception.Create(Format(SUnsupportArrayItem, [I])); + end; + end; + end else + Result := nil; +end; + +function JSONObject.addChildObject(const key: JSONString): JSONObject; +begin + if Length(key) > 0 then + Result := NewChildObject(key) + else + Result := nil; +end; + +function JSONObject.Clone: JSONObject; +begin + Result := JSONObject.Create; + Result.Assign(Self); +end; + +function JSONObject.Contains(const Key: JSONString): Boolean; +begin + Result := Exist(Key); +end; + +procedure JSONObject.Delete(const key: JSONString); +begin + Remove(IndexOf(key)); +end; + +function JSONObject.getBoolean(const key: JSONString): Boolean; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsBoolean + else + Result := False; +end; + +function JSONObject.getByte(const key: JSONString): Byte; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsByte + else + Result := 0; +end; + +function JSONObject.getDateTime(const key: JSONString): TDateTime; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsDateTime + else + Result := 0; +end; + +function JSONObject.getDouble(const key: JSONString): Double; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsDouble + else + Result := 0; +end; + +function JSONObject.getDWORD(const key: JSONString): Cardinal; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsInt64 + else + Result := 0; +end; + +function JSONObject.getFloat(const key: JSONString): Extended; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsFloat + else + Result := 0; +end; + +function JSONObject.GetChildForceItem(const Path: JSONString): PJSONValue; +begin + if Length(Path) = 0 then + raise Exception.Create(SNameNotFound) + else + Result := ForcePath(Path); +end; + +function JSONObject.getInt(const key: JSONString): Integer; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsInteger + else + Result := 0; +end; + +function JSONObject.getInt64(const key: JSONString): Int64; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsInt64 + else + Result := 0; +end; + +function JSONObject.getItem(const key: JSONString): PJSONValue; +var + I: Integer; +begin + I := IndexOf(Key); + if I < 0 then + Result := nil + else + Result := FItems[I]; +end; + +function JSONObject.getJsonArray(const key: JSONString): JSONArray; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsJsonArray + else + Result := nil; +end; + +function JSONObject.getJsonObject(const key: JSONString): JSONObject; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsJsonObject + else + Result := nil; +end; + +function JSONObject.getString(const key: JSONString): JSONString; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsString + else + Result := ''; +end; + +function JSONObject.GetChildItem(const Key: JSONString): PJSONValue; +begin + Result := GetItem(Key); + if (Result = nil) and (Length(Key) > 0) then + Result := Add(Key) + else + raise Exception.Create(SNameNotFound); +end; + +function JSONObject.getVariant(const key: JSONString): Variant; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsVariant + else + Result := NULL; +end; + +function JSONObject.getWord(const key: JSONString): Word; +var + Item: PJSONValue; +begin + Item := getItem(key); + if Item <> nil then + Result := Item.AsWord + else + Result := 0; +end; + +function JSONObject.NextAsJsonObject: JSONObject; +var + P: PJSONValue; +begin + P := Next; + if p <> nil then + Result := P.AsJsonObject + else + Result := nil; +end; + +class function JSONBase.ParseValue(ABuilder: TStringCatHelper; var p: PJSONChar): Variant; +var + ANum: Extended; +begin + if (p^ = '"') or (p^='''') then begin + BuildJsonString(ABuilder, p); + Result := ABuilder.Value; + end else if ParseNumeric(p, ANum) then begin //֣ + if SameValue(ANum, Trunc(ANum)) then + Result := Trunc(ANum) + else + Result := ANum; + end else if StartWith(p, 'False', True) then begin //False + Inc(p,5); + Result := False; + end else if StartWith(p, 'True', True) then begin //True + Inc(p,4); + Result := True; + end else if StartWith(p, 'NULL', True) then begin //Null + Inc(p,4); + Result := varNull; + end else + Result := varEmpty; +end; + +{$IFDEF USERTTI} +class function JSONObject.ParseObject(const aIn: TObject): JSONObject; +begin + if not Assigned(aIn) then begin + Result := nil; + Exit; + end; + Result := JSONObject.Create; + TYxdSerialize.writeValue(Result, '', aIn{$IFNDEF JSON_UNICODE}, TYxdSerialize.GetObjectTypeInfo(aIn){$ENDIF}); +end; +{$ENDIF} + +class function JSONObject.parseObjectByName(const text, key: JSONString; + value: Variant): JSONObject; +var + ABuilder: TStringCatHelper; + p, p1: PJSONChar; + c: JSONChar; + nocmpValue: Boolean; + i, j: Integer; + + function DecodeCopy(var json: JSONObject; len: Integer): Integer; + var + S: JSONString; + begin + S := StrDup(p, 0, len); + p := PJSONChar(S); + {$IFDEF JSON_UNICODE}SkipSpaceW(p);{$ELSE}SkipSpaceA(p);{$ENDIF} + Result := json.ParseJsonPair(ABuilder, p) + end; + + function CmpValue(var p1: PJSONChar): Boolean; + begin + try + if ParseValue(ABuilder, p1) = value then begin + Result := True; + if p1^ = '}' then + Dec(p1); + end else + Result := False; + except + Result := False; + end; + end; + +begin + Result := nil; + if Length(key) = 0 then Exit; + p := PJSONChar(text); + nocmpValue := VarIsEmpty(value) or VarIsNull(value); + ABuilder := TStringCatHelper.Create; + try + while p^ <> #0 do begin + p1 := StrPos(p, PJSONChar(key)); + if (p1 = nil) then Exit; + Dec(p1); + c := p1^; + if (c = '"') or (c = '''') then begin + Inc(p1, Length(key) + 1); + if p1^ <> c then begin + p := p1 + 2; + continue; + end; + Inc(p1); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p1); + if p1^ <> ':' then Exit; + Inc(p1); + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p1); + if nocmpValue or CmpValue(p1) then begin + i := p1 - p; + p := p1; + j := 0; + if (not nocmpValue) and (p^ = '}') then begin + Dec(p); + Dec(i); + end; + while i > -1 do begin + if (p^ = '{') then begin + if j = 0 then + Break; + Dec(j); + end else if (p^ = '}') then + Inc(j); + Dec(p); + Dec(i); + end; + if i < 0 then Exit; + while (p1 <> nil) and (p1^ <> #0) do begin + if p1^ = '{' then + Inc(j) + else if (p1^ = '}') then begin + if j = 0 then + Break; + Dec(j); + end; + Inc(p1); + end; + if j <> 0 then Exit; + i := p1 - p + 2; + ABuilder.Position := 0; + Result := JSONObject.Create; + if DecodeCopy(Result, i) <> 0 then + FreeAndNil(Result); + Break; + end else + ABuilder.Position := 0; + {$IFDEF JSON_UNICODE}SkipSpaceW{$ELSE}SkipSpaceA{$ENDIF}(p1); + p := p1; + end else + p := p1 + 2; + end; + finally + ABuilder.Free; + end; +end; + +class function JSONObject.parseStringByName(const text, + key: JSONString): JSONString; +var + json: JSONObject; +begin + json := parseObjectByName(text, key, NULL); + if Assigned(json) then begin + Result := json.getString(key); + FreeAndNil(json); + end else + Result := ''; +end; + +procedure JSONObject.put(const key: JSONString; value: JSONArray); +var + item: PJSONValue; +begin + item := Add(Key); + item.AsJsonArray := value; + if value.FParent <> nil then + value.FParent.RemoveObject(value) + else if (value.FValue <> nil) and (value.FValue.FType = jdtUnknown) then + Dispose(Value.FValue); + value.FParent := Self; + value.FValue := item; +end; + +procedure JSONObject.Put(const Key: JSONString; Value: Boolean); +begin + Add(Key).AsBoolean := value; +end; + +procedure JSONObject.put(const key: JSONString; ABuilder: TStringCatHelper); +var + item: PJSONValue; + L: Integer; +begin + item := Add(Key); + item.FType := jdtString; + L := ABuilder.Position{$IFDEF JSON_UNICODE} shl 1{$ENDIF}; + SetLength(item.FValue, L); + if (L > 0) then + Move(ABuilder.Start^, Item.FValue[0], L); +end; + +procedure JSONObject.put(const key: JSONString; const value: Variant); +var + Item: PJSONValue; +begin + Item := Add(Key); + Item.AsVariant := value; + if Assigned(Item.FObject) then + Item.FObject.FParent := Self; +end; + +procedure JSONObject.put(const key: JSONString; const value: Extended); +begin + Add(Key).AsFloat := value; +end; + +procedure JSONObject.put(const key: JSONString; const value: Double); +begin + Add(Key).AsDouble := value; +end; + +procedure JSONObject.putDateTime(const key: JSONString; value: TDateTime); +begin + Add(Key).AsDateTime := value; +end; + +procedure JSONObject.SetBoolean(const Key: JSONString; const Value: Boolean); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsBoolean := Value; +end; + +procedure JSONObject.SetByte(const Key: JSONString; Value: Byte); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsByte := Value; +end; + +procedure JSONObject.SetDateTime(const Key: JSONString; const Value: TDateTime); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsDateTime := Value; +end; + +procedure JSONObject.SetDouble(const Key: JSONString; const Value: Double); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsDouble := Value; +end; + +procedure JSONObject.SetDWORD(const Key: JSONString; const Value: DWORD); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsInteger := Value; +end; + +procedure JSONObject.SetInt(const Key: JSONString; const Value: Integer); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsInteger := Value; +end; + +procedure JSONObject.SetInt64(const Key: JSONString; const Value: Int64); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsInt64 := Value; +end; + +procedure JSONObject.SetJsonArray(const Key: JSONString; const Value: JSONArray); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsJsonArray := Value; +end; + +procedure JSONObject.SetJsonObject(const Key: JSONString; + const Value: JSONObject); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsJsonObject := Value; +end; + +procedure JSONObject.SetString(const Key, Value: JSONString); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsString := Value; +end; + +procedure JSONObject.SetVariant(const Key: JSONString; const Value: Variant); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsVariant := Value; +end; + +procedure JSONObject.SetWord(const Key: JSONString; const Value: Word); +begin + if Length(Key) > 0 then + GetChildItem(Key).AsWord := Value; +end; + +procedure JSONObject.Put(const Key: JSONString; Value: array of const); +begin + AddChildArray(Key, Value); +end; + +{ JSONArray } + +procedure JSONArray.add(value: JSONObject); +var + item: PJSONValue; +begin + item := NewJsonValue(); + item.AsJsonObject := value; + if value.FParent <> nil then + value.FParent.RemoveObject(value) + else if (value.FValue <> nil) and (value.FValue.FType = jdtUnknown) then + Dispose(Value.FValue); + value.FParent := Self; + value.FValue := item; +end; + +procedure JSONArray.add(value: Byte); +begin + NewJsonValue().Asbyte := value; +end; + +procedure JSONArray.add(const value: JSONString); +begin + NewJsonValue().AsString := value; +end; + +procedure JSONArray.add(value: Cardinal); +begin + NewJsonValue().AsInt64 := value; +end; + +procedure JSONArray.add(value: Integer); +begin + NewJsonValue().AsInteger := value; +end; + +procedure JSONArray.add(value: Word); +begin + NewJsonValue().AsWord := value; +end; + +procedure JSONArray.add(const value: Int64); +begin + NewJsonValue().AsInt64 := value; +end; + +procedure JSONArray.add(const value: Variant); +var + Item: PJSONValue; +begin + Item := NewJsonValue(); + Item.AsVariant := value; + if Assigned(Item.FObject) then + Item.FObject.FParent := Self; +end; + +procedure JSONArray.add(value: JSONArray); +var + item: PJSONValue; +begin + item := NewJsonValue(); + item.AsJsonArray := value; + if value.FParent <> nil then + value.FParent.RemoveObject(value) + else if (value.FValue <> nil) and (value.FValue.FType = jdtUnknown) then + Dispose(Value.FValue); + value.FParent := Self; + value.FValue := item; +end; + +procedure JSONArray.Add(const Value: array of const); +begin + JSONObject(Self).AddChildArray('', Value); +end; + +procedure JSONArray.Add(Value: Boolean); +begin + NewJsonValue().AsBoolean := value; +end; + +function JSONArray.addChildArray: JSONArray; +begin + Result := NewChildArray(''); +end; + +function JSONArray.addChildObject: JSONObject; +begin + Result := NewChildObject(''); +end; + +function JSONArray.AddChildArray(const Index: Integer): JSONArray; +var + Item: PJSONValue; +begin + Result := JSONArray.Create; + Result.FParent := Self; + New(Item); + Item.FName := ''; + Item.FNameHash := 0; + Item.FObject := Result; + Item.FType := jdtObject; + if (Index < 0) or (Index >= FItems.Count) then + FItems.Add(Item) + else + FItems.Insert(Index, Item); + Result.FValue := Item; +end; + +function JSONArray.AddChildObject(const Index: Integer): JSONObject; +var + Item: PJSONValue; +begin + Result := JSONObject.Create; + Result.FParent := Self; + New(Item); + Item.FName := ''; + Item.FNameHash := 0; + Item.FObject := Result; + Item.FType := jdtObject; + if (Index < 0) or (Index >= FItems.Count) then + FItems.Add(Item) + else + FItems.Insert(Index, Item); + Result.FValue := Item; +end; + +procedure JSONArray.add(const value: Extended); +begin + NewJsonValue().AsFloat := value; +end; + +procedure JSONArray.add(const value: Double); +begin + NewJsonValue().AsDouble := value; +end; + +procedure JSONArray.addDateTime(value: TDateTime); +begin + NewJsonValue().AsDateTime := value; +end; + +procedure JSONArray.addJSON(const value: JSONString; AType: JsonDataType); +begin + putJSON('', value, AType); +end; + +function JSONArray.Clone: JSONArray; +begin + Result := JSONArray.Create; + Result.Assign(Self); +end; + +function JSONArray.getBoolean(Index: Integer): Boolean; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsBoolean + else + Result := False; +end; + +function JSONArray.getByte(Index: Integer): Byte; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsByte + else + Result := 0; +end; + +function JSONArray.getDateTime(Index: Integer): TDateTime; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsDateTime + else + Result := 0; +end; + +function JSONArray.getDouble(Index: Integer): Double; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsDouble + else + Result := 0; +end; + +function JSONArray.getDWORD(Index: Integer): Cardinal; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsInt64 + else + Result := 0; +end; + +function JSONArray.getFloat(Index: Integer): Extended; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsFloat + else + Result := 0; +end; + +function JSONArray.getInt(Index: Integer): Integer; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsInteger + else + Result := 0; +end; + +function JSONArray.getInt64(Index: Integer): Int64; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsInt64 + else + Result := 0; +end; + +function JSONArray.GetIsArray: Boolean; +begin + Result := True; +end; + +function JSONArray.getJsonArray(Index: Integer): JSONArray; +var + item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsJsonArray + else + Result := nil; +end; + +function JSONArray.getJsonObject(Index: Integer): JSONObject; +var + item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsJsonObject + else + Result := nil; +end; + +function JSONArray.getString(Index: Integer): JSONString; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsString + else + Result := ''; +end; + +function JSONArray.getVariant(Index: Integer): Variant; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsVariant + else + Result := varEmpty; +end; + +function JSONArray.getWord(Index: Integer): Word; +var + Item: PJSONValue; +begin + Item := FItems[index]; + if Item <> nil then + Result := Item.AsWord + else + Result := 0; +end; + +function JSONArray.NewJsonValue(): PJSONValue; +begin + New(Result); + Result.FObject := nil; + Result.FName := ''; + Result.FNameHash := 0; + FItems.Add(Result); +end; + +function JSONArray.NextAsJsonArray: JSONArray; +var + P: PJSONValue; +begin + P := Next; + if p <> nil then + Result := P.AsJsonArray + else + Result := nil; +end; + +{$IFDEF JSON_RTTI} +procedure JSONArray.PutObject(ASource: TObject); +begin + TYxdSerialize.writeValue(Self, '', aSource); +end; +{$ENDIF} + +{$IFDEF JSON_RTTI} +procedure JSONArray.PutRecord(const ASource: T); +begin + TYxdSerialize.writeValue(Self, '', @aSource, TypeInfo(T)); +end; +{$ENDIF} + +procedure JSONArray.SetBoolean(Index: Integer; const Value: Boolean); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsBoolean := Value; +end; + +procedure JSONArray.SetByte(Index: Integer; const Value: Byte); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsByte := Value; +end; + +procedure JSONArray.SetDateTime(Index: Integer; const Value: TDateTime); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsDateTime := Value; +end; + +procedure JSONArray.SetDouble(Index: Integer; const Value: Double); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsDouble := Value; +end; + +procedure JSONArray.SetDWORD(Index: Integer; const Value: DWORD); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsInteger := Value; +end; + +procedure JSONArray.SetInt(Index: Integer; const Value: Integer); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsInteger := Value; +end; + +procedure JSONArray.SetInt64(Index: Integer; const Value: Int64); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsInt64 := Value; +end; + +procedure JSONArray.SetJsonArray(Index: Integer; const Value: JSONArray); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsJsonArray := Value; +end; + +procedure JSONArray.SetJsonObject(Index: Integer; const Value: JSONObject); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsJsonObject := Value; +end; + +procedure JSONArray.SetString(Index: Integer; const Value: JSONString); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsString := Value; +end; + +procedure JSONArray.SetVariant(Index: Integer; const Value: Variant); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsVariant := Value; +end; + +procedure JSONArray.SetWord(Index: Integer; const Value: Word); +begin + if (Index < 0) or (Index >= Count) then + Add(Value) + else + FItems[index].AsWord := Value; +end; + +function InitJsonFloatPrecisionFmt(i: Integer): JSONString; +begin + SetLength(Result, I); + for i := 1 to Length(Result) do + Result[i] := '0'; +end; + +{ JSONList } + +{$IFNDEF UNICODE} +function JSONList.Get(Index: Integer): PJSONValue; +begin + Result := inherited Get(Index); +end; + +procedure JSONList.Put(Index: Integer; Item: PJSONValue); +begin + inherited Put(Index, Item); +end; +{$ENDIF} + +initialization + +finalization + +end. \ No newline at end of file diff --git a/source/YxdRtti.pas b/source/YxdRtti.pas new file mode 100644 index 0000000..155fcf2 --- /dev/null +++ b/source/YxdRtti.pas @@ -0,0 +1,1547 @@ +{*******************************************************} +{ } +{ RTTI } +{ } +{ Ȩ (C) 2013 YangYxd } +{ } +{*******************************************************} +{ + -------------------------------------------------------------------- + ˵ + -------------------------------------------------------------------- + YXDRttiоswishQJSONлswishQJson + QJsonQDACĿȨswish(QQ:109867294) + QDACٷȺ250530692 + + -------------------------------------------------------------------- + ¼¼ + -------------------------------------------------------------------- + + 2014.08.05 ver 1.0.1 + -------------------------------------------------------------------- + - ֧YxdJSONл뷴лDataSet. + + 2014.08.01 ver 1.0.0 + -------------------------------------------------------------------- + - ֧YxdJSONл뷴ллʱַ֧ʵ + ҪȴöͨRTTIɳʼ,D2007£֧TObject + publishedԡд + - XE6Delphi2007(Ѳ)֧Win32, Androidƽ̨ + - ݲ֧XMLINIĸʽлܻġ + -------------------------------------------------------------------- +} +unit YxdRtti; + +interface + +{$DEFINE USEYxdStr} // ǷʹYxdStrԪ +{$DEFINE USEIniSerialize} // ʹINIлģ +{$DEFINE USEXmlSerialize} // ʹXMLлģ +{$DEFINE USEJsonSerialize} // ʹJsonлģ + +{$DEFINE USEDataSet} // ǷʹDataSetл + +{$IF RTLVersion>=26} +{$DEFINE USE_UNICODE} +{$IFEND} + +uses + {$IFDEF USEYxdStr}YxdStr, {$ENDIF} + {$IFDEF MSWINDOWS}Windows, {$ENDIF} + {$IFDEF USE_UNICODE}Generics.Collections, Rtti, {$ENDIF} + {$IFDEF USE_UNICODE}Soap.EncdDecd, {$ELSE}Base64, {$ENDIF} + {$IFDEF USEDataSet}DB, DBClient, {$ENDIF} + {$IFDEF USEJsonSerialize}YxdJson, {$ENDIF} + SysUtils, Classes, Variants, TypInfo; + +type + /// + /// л + /// + TSerializeType = (afXML,{XMLʽ} afIni,{iniļ} afJson {jsonʽ}); + + {$IFDEF USE_UNICODE} + TValueArray = array of TValue; + {$ENDIF} + +type + TYxdSerialize = class + protected + class procedure LoadCollection(AIn: JSONBase; ACollection: TCollection); + class function ArrayItemTypeName(ATypeName: JSONString): JSONString; + class function ArrayItemType(ArrType: PTypeInfo): PTypeInfo; + public + class procedure ReadValue(AIn: JSONBase; ADest: Pointer; aType: {$IFDEF USE_UNICODE}PTypeInfo{$ELSE}PTypeInfo{$ENDIF}); overload; + class procedure ReadObject(AIn: JSONBase; ADest: TObject); + class procedure WriteValue(AOut: JSONBase; const Key: JSONString; ASource: Pointer; AType: PTypeInfo); overload; + {$IFDEF USEDataSet} + /// + /// ָJSONתDataSet + /// JSON + /// ĿDataSetݼ + /// سɹص-1ʾ + /// + class function ReadDataSet(AIn: JSONBase; ADest: TDataSet): Integer; + /// + /// лDataSetΪJson + /// ָJson + /// KeyΪգAoutһKeyӶ + /// лDataSetݼ + /// ӵڼҳʼлPageSize > 0 ʱЧ + /// ҳʱÿҳ + /// + class procedure WriteDataSet(AOut: JSONBase; const Key: JSONString; ADataSet: TDataSet; + const PageIndex, PageSize: Integer; Base64Blob: Boolean = True); + {$ENDIF} + {$IFDEF USE_UNICODE} + class procedure ReadValue(AIn: JSONBase; AInstance: TValue); overload; + class procedure ReadRecord(AIn: JSONBase; out AInstance: T); + class function WriteToValue(AIn: PJSONValue): TValue; overload; + class function WriteToValue(AIn: JSONBase): TValue; overload; + class procedure WriteValue(AOut: JSONBase; const Key: JSONString; AInstance: TValue); overload; + {$ELSE} + class function GetObjectTypeInfo(AObj: TObject): PTypeInfo; + {$ENDIF} + end; + +implementation + +{$IFDEF USEDataSet} +const + CSBlobs: JSONString = '[blobs]<'; //Ҫ޸, Ϊ8ٱȶ + CSBlobBase64: JSONString = '[BS]'; //ʹBase64Blobʱʶǰ׺ +{$ENDIF} + +resourcestring + SUnsupportPropertyType = 'ֵ֧.'; + SMissRttiTypeDefine = '޷ҵ %s RTTIϢԽӦ͵(array[0..1] of ByteΪTByteArr=array[0..1]ȻTByteArr)'; + SArrayTypeMissed = 'δ֪Ԫ.'; + SErrorJsonType = 'Json.'; + +{ TYxdSerialize } + +class function TYxdSerialize.ArrayItemType(ArrType: PTypeInfo): PTypeInfo; +var + ATypeData: PTypeData; +begin + Result := nil; + if (ArrType <> nil) and (ArrType.Kind in [tkArray,tkDynArray]) then begin + ATypeData := GetTypeData(ArrType); + if (ATypeData <> nil) then + Result := ATypeData.elType2^; + if Result = nil then begin + if ATypeData.BaseType^ = TypeInfo(Byte) then + Result := TypeInfo(Byte); + end; + end; +end; + +class function TYxdSerialize.ArrayItemTypeName(ATypeName: JSONString): JSONString; +var + p, ps: PJSONChar; + ACount: Integer; +begin + p := PJSONChar(ATypeName); + if StartWith(p, 'TArray<', true) then begin + Inc(p, 7); + ps := p; + ACount := 1; + while ACount >0 do begin + if p^ = '>' then + Dec(ACount) + else if p^ = '<' then + Inc(ACount); + Inc(p); + end; + Result := StrDupX(ps, p-ps-1); + end else + Result:=''; +end; + +{$IFNDEF USE_UNICODE} +class function TYxdSerialize.GetObjectTypeInfo(AObj: TObject): PTypeInfo; +begin + if Assigned(AObj) then + Result := AObj.ClassInfo + else + Result := nil; +end; +{$ENDIF} + +class procedure TYxdSerialize.LoadCollection(aIn: JSONBase; ACollection: TCollection); +var + I: Integer; + {$IFNDEF USE_UNICODE} + Item: TCollectionItem; + {$ENDIF} +begin + if not Assigned(aIn) then Exit; + for I := 0 to aIn.Count - 1 do begin + {$IFDEF USE_UNICODE} + readValue(aIn, ACollection.Add); + {$ELSE} + Item := ACollection.Add; + readValue(aIn, Item, GetObjectTypeInfo(Item)); + {$ENDIF} + end; +end; + + +{$IFDEF USEDataSet} +{$IFDEF USE_UNICODE} +type TPointerStream = class(TCustomMemoryStream); +{$ENDIF} +class function TYxdSerialize.ReadDataSet(AIn: JSONBase; ADest: TDataSet): Integer; +var + BlobStream: TStream; + {$IFDEF USE_UNICODE} BSStream: TPointerStream;{$ENDIF} + + function IsBlob(p: Pointer; HighL: Integer): Boolean; + begin + {$IFDEF USE_UNICODE} + Result := (HighL >= 18) + and (PInt64(p)^ = $6F006C0062005B) + and (PInt64(IntPtr(p)+8)^ = $3C005D00730062) + and (PWord(IntPtr(p)+HighL-1)^ = $3E); + {$ELSE} + Result := (HighL >= 9) + and (PInt64(IntPtr(p))^ = $3C5D73626F6C625B) + and (PByte(IntPtr(p)+HighL)^ = $3E); + {$ENDIF} + end; + + function GetBlodValue(Field: TField; Item: PJSONValue; var Buf: TBytes): Integer; + var + I: Integer; + p: {$IFDEF USE_UNICODE}PByte{$ELSE}PAnsiChar{$ENDIF}; + {$IFNDEF USE_UNICODE}BStmp: JSONString;{$ELSE}BStmp: TMemoryStream;{$ENDIF} + begin + Result := 0; + I := High(Item.FValue); + if I > -1 then begin + p := @Item.FValue[0]; + {$IFDEF USE_UNICODE} + if IsBlob(p, I) then begin + Inc(p, 16); + if (I >= 22) and (PInt64(p)^ = $5D00530042005B) then begin + Inc(p, 8); + if not Assigned(BSStream) then + BSStream := TPointerStream.Create; + BSStream.SetPointer(p, I-17-8); + BSStream.Position := 0; + BStmp := TMemoryStream.Create; + try + DecodeStream(BSStream, BStmp); + if Assigned(BlobStream) then + BlobStream.Free; + BlobStream := ADest.CreateBlobStream(Field, bmWrite); + BlobStream.Write(BSTmp.Memory^, BSTmp.Size); + BlobStream.Free; + BlobStream := nil; + finally + BStmp.Free; + end; + Result := 2; + end else begin + {$IFDEF USEYxdStr}YxdStr{$ELSE}YxdJson{$ENDIF}.HexToBin(Pointer(p), (I-17) shr 1, Buf); + Result := 1; + end; + {$ELSE} + if IsBlob(p, I) then begin + Inc(p, 8); + if (I >= 13) and (PDWORD(p)^ = $5D53425B) then begin + Inc(p, 4); + BStmp := Base64Decode(p^, I-8-4); + if Assigned(BlobStream) then + BlobStream.Free; + BlobStream := ADest.CreateBlobStream(Field, bmWrite); + BlobStream.Size := 0; + if Length(BSTmp) > 0 then + BlobStream.WriteBuffer(BSTmp[1], Length(BStmp)); + BlobStream.Free; + BlobStream := nil; + Result := 2; + end else begin + {$IFDEF USEYxdStr}YxdStr{$ELSE}YxdJson{$ENDIF}.HexToBin(p, High(Item.FValue)-8, Buf); + Result := 1; + end; + {$ENDIF} + end; + end; + end; + + procedure AddObjectMeta(Item: PJSONValue); + begin + case Item.FType of + jdtString: + begin + if (Item.Size > 0) and IsBlob(@Item.FValue[0], High(Item.FValue)) then + ADest.FieldDefs.Add(Item.FName, ftBlob, 20) + else + ADest.FieldDefs.Add(Item.FName, ftString, 30); + end; + jdtInteger: + ADest.FieldDefs.Add(Item.FName, ftInteger); + jdtFloat: + ADest.FieldDefs.Add(Item.FName, ftFloat); + jdtBoolean: + ADest.FieldDefs.Add(Item.FName, ftBoolean); + jdtDateTime: + ADest.FieldDefs.Add(Item.FName, ftDateTime); + jdtNull: ; + else + ADest.FieldDefs.Add(Item.FName, ftVariant); + end; + end; + + procedure AddItem(Field: TField; DataType: TFieldType; Item: PJSONValue); + var + Buf: TBytes; + begin + if Item.FType = jdtNull then begin + Field.Value := NULL; + Exit; + end; + case DataType of + ftDate, ftTime, ftDateTime, ftTimeStamp{$IFDEF USE_UNICODE}, ftTimeStampOffset{$ENDIF}: + Field.Value := Item.AsDateTime; + ftBlob, ftGraphic, ftMemo, ftTypedBinary: + begin + case GetBlodValue(Field, Item, Buf) of + 0: Field.Value := Item.GetString; + 1: + begin + if Assigned(BlobStream) then + BlobStream.Free; + BlobStream := ADest.CreateBlobStream(Field, bmWrite); + BlobStream.Position := 0; + BlobStream.WriteBuffer(Buf[0], Length(Buf)); + BlobStream.Free; + BlobStream := nil; + end; + end; + end + else case Item.FType of + jdtBoolean: + Field.Value := Item.AsBoolean; + jdtInteger: + Field.Value := Item.AsInteger; + jdtFloat: + Field.Value := Item.AsFloat; + jdtDateTime: + Field.Value := Item.AsDateTime; + jdtString: + begin + case GetBlodValue(Field, Item, Buf) of + 0: Field.Value := Item.GetString; + 1: Field.Value := Buf; + end; + end; + end; + end; + end; + +var + FldName: string; + Meta, MetaItem, Data: JSONArray; + Item, ItemChild: PJSONValue; + ItemObject: JSONBase; + Field: TField; + I: Integer; +begin + Result := -1; + if (not Assigned(aDest)) or (not Assigned(AIn)) then Exit; + ADest.DisableControls; + ADest.FieldDefs.Clear; + ADest.Close; + + if (AIn.IsJSONArray) then begin + Meta := nil; + Data := JSONArray(aIn); + end else begin + Meta := JSONObject(AIn).GetJsonArray('meta'); + Data := JSONObject(AIn).GetJsonArray('data'); + if not Assigned(Data) then + Data := JSONObject(AIn).GetJsonArray('rows'); + end; + + Result := 0; + BlobStream := nil; + {$IFDEF USE_UNICODE}BSStream := nil;{$ENDIF} + if (not Assigned(Meta)) and (not Assigned(Data)) then Exit; + try + if (not Assigned(Meta)) then begin // ûMetaݣֶлȡ + ItemObject := Data.GetJsonObject(0); + if not Assigned(ItemObject) then + Exit; + for Item in ItemObject do begin + if Item.FType = jdtNull then begin + if Length(Item.FName) > 0 then // һ¼в + for i := 1 to Data.Count - 1 do begin + ItemObject := Data.GetJsonObject(I); + if (ItemObject = nil) then Continue; + ItemChild := JSONObject(ItemObject).GetItem(Item.FName); + if (ItemChild = nil) or (ItemChild.FType = jdtNull) then Continue; + AddObjectMeta(ItemChild); + Break; + end; + end else + AddObjectMeta(Item); + end; + end else begin + for I := 0 to Meta.Count - 1 do begin + MetaItem := Meta[I].AsJsonArray; + if MetaItem = nil then Continue; + ADest.FieldDefs.Add( + MetaItem.Items[0].GetString, + TFieldType(MetaItem.Items[1].AsInteger), + MetaItem.Items[2].AsInteger, + MetaItem.Items[3].AsBoolean); + end; + end; + + if not ADest.Active then begin + if ADest is TClientDataSet then + TClientDataSet(ADest).CreateDataSet + else + ADest.Open; + end; + + try + for Item in Data do begin + ItemObject := Item.GetObject; + if ItemObject = nil then Continue; + // ģʽ + if ItemObject.IsJSONArray then begin + ADest.Append; + for I := 0 to ItemObject.Count - 1 do begin + FldName := ADest.Fields[i].FieldName; + AddItem(ADest.Fields[i], ADest.FieldDefs.Items[i].DataType, ItemObject.Items[i]); + end; + ADest.Post; + end else begin // ģʽ + ADest.Append; + for ItemChild in ItemObject do begin + Field := ADest.FindField(ItemChild.FName); + if not Assigned(Field) then + Continue; + FldName := ItemChild.FName; + AddItem(Field, ADest.FieldDefs.Items[Field.Index].DataType, ItemChild); + end; + ADest.Post; + end; + end; + except + raise Exception.CreateFmt('jsonֶ(%s)ֵݼ쳣', [FldName]); + end; + finally + if ADest.Active then + ADest.First; + ADest.EnableControls; + if Assigned(BlobStream) then + BlobStream.Free; + {$IFDEF USE_UNICODE} + if Assigned(BSStream) then + BSStream.Free; + {$ENDIF} + end; + Result := ADest.RecordCount; +end; +{$ENDIF} + +class procedure TYxdSerialize.readObject(aIn: JSONBase; aDest: TObject); +begin + if not Assigned(aDest) then Exit; + {$IFDEF USE_UNICODE} + readValue(aIn, aDest); + {$ELSE} + readValue(aIn, aDest, GetObjectTypeInfo(aDest)); + {$ENDIF} +end; + +{$IFDEF USE_UNICODE} +class procedure TYxdSerialize.readRecord(aIn: JSONBase; out aInstance: T); +begin + readValue(aIn, @aInstance, TypeInfo(T)); +end; +{$ENDIF} + +{$IFDEF USE_UNICODE} +class procedure TYxdSerialize.readValue(aIn: JSONBase; aInstance: TValue); +begin + if aInstance.IsEmpty then + Exit; + if aInstance.Kind = tkRecord then + readValue(aIn, aInstance.GetReferenceToRawData, aInstance.TypeInfo) + else if aInstance.Kind = tkClass then + readValue(aIn, aInstance.AsObject, aInstance.TypeInfo); +end; +{$ENDIF} + +class procedure TYxdSerialize.readValue(aIn: JSONBase; aDest: Pointer; + aType: PTypeInfo); + + procedure LoadClass(AObj: TObject; AChild: PJSONValue); + begin + if AObj is TStrings then + (AObj as TStrings).Text := AChild.AsString + else if AObj is TCollection then + LoadCollection(AChild.AsJsonArray, AObj as TCollection) + else if AObj <> nil then + readValue(AChild.AsJsonObject, AObj{$IFNDEF USE_UNICODE}, GetObjectTypeInfo(AObj){$ENDIF}); + end; + + {$IFDEF USE_UNICODE} + procedure ToRecord; + var + AContext: TRttiContext; + AFieldItem: TRttiField; + AFields: TArray; + ARttiType: TRttiType; + ABaseAddr: Pointer; + AChild: PJSONValue; + J: Integer; + begin + AContext := TRttiContext.Create; + ARttiType := AContext.GetType(AType); + ABaseAddr := ADest; + AFields := ARttiType.GetFields; + for J := Low(AFields) to High(AFields) do begin + AFieldItem := AFields[J]; + if AFieldItem.FieldType <> nil then begin + if aIn.IsJSONArray then + AChild := JSONArray(aIn).Items[J] + else + AChild := JSONObject(aIn).getItem(AFieldItem.Name); + if AChild <> nil then begin + case AFieldItem.FieldType.TypeKind of + tkInteger: + AFieldItem.SetValue(ABaseAddr, AChild.AsInteger); + {$IFNDEF NEXTGEN} + tkString: + PShortString(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := ShortString(AChild.AsString); + {$ENDIF !NEXTGEN} + tkUString{$IFNDEF NEXTGEN},tkLString,tkWString{$ENDIF !NEXTGEN}: + AFieldItem.SetValue(ABaseAddr, AChild.AsString); + tkEnumeration: + begin + if GetTypeData(AFieldItem.FieldType.Handle)^.BaseType^ = TypeInfo(Boolean) then + AFieldItem.SetValue(ABaseAddr, AChild.AsBoolean) + else begin + case GetTypeData(AFieldItem.FieldType.Handle).OrdType of + otSByte: + begin + if AChild.FType = jdtInteger then + PShortint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PShortint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := GetEnumValue(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otUByte: + begin + if AChild.FType = jdtInteger then + PByte(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PByte(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := GetEnumValue(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otSWord: + begin + if AChild.FType = jdtInteger then + PSmallint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PSmallint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := GetEnumValue(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otUWord: + begin + if AChild.FType = jdtInteger then + PWord(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PWord(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := GetEnumValue(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otSLong: + begin + if AChild.FType = jdtInteger then + PInteger(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PInteger(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := GetEnumValue(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otULong: + begin + if AChild.FType = jdtInteger then + PCardinal(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PCardinal(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := GetEnumValue(AFieldItem.FieldType.Handle, AChild.AsString); + end; + end; + end; + end; + tkSet: + begin + case GetTypeData(AFieldItem.FieldType.Handle).OrdType of + otSByte: + begin + if AChild.FType = jdtInteger then + PShortint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PShortint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := StringToSet(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otUByte: + begin + if AChild.FType = jdtInteger then + PByte(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PByte(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := StringToSet(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otSWord: + begin + if AChild.FType = jdtInteger then + PSmallint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PSmallint(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := StringToSet(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otUWord: + begin + if AChild.FType = jdtInteger then + PWord(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PWord(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := StringToSet(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otSLong: + begin + if AChild.FType = jdtInteger then + PInteger(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PInteger(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := StringToSet(AFieldItem.FieldType.Handle, AChild.AsString); + end; + otULong: + begin + if AChild.FType = jdtInteger then + PCardinal(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsInteger + else + PCardinal(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := StringToSet(AFieldItem.FieldType.Handle, AChild.AsString); + end; + end; + end; + tkChar, tkWChar: + AFieldItem.SetValue(ABaseAddr, AChild.AsString); + tkFloat: + if (AFieldItem.FieldType.Handle = TypeInfo(TDateTime)) or + (AFieldItem.FieldType.Handle = TypeInfo(TTime)) or + (AFieldItem.FieldType.Handle = TypeInfo(TDate)) + then + AFieldItem.SetValue(ABaseAddr, AChild.AsDateTime) + else + AFieldItem.SetValue(ABaseAddr, AChild.AsFloat); + tkInt64: + AFieldItem.SetValue(ABaseAddr, AChild.AsInt64); + tkVariant: + PVariant(IntPtr(ABaseAddr)+AFieldItem.Offset)^ := AChild.AsVariant; + tkArray, tkDynArray: + readValue(AChild.AsJsonArray, Pointer(IntPtr(ABaseAddr)+AFieldItem.Offset),AFieldItem.FieldType.Handle); + tkClass: + LoadClass(AFieldItem.GetValue(ABaseAddr).AsObject, AChild); + tkRecord: + readValue(AChild.AsJsonObject, Pointer(IntPtr(ABaseAddr)+AFieldItem.Offset),AFieldItem.FieldType.Handle); + end; + end; + end; + end; + end; + {$ENDIF} + + procedure ToObject; + var + AProp: PPropInfo; + AObj, AChildObj: TObject; + AChild: PJSONValue; + J: Integer; + begin + AObj := aDest; + for J := 0 to aIn.Count - 1 do begin + AChild := aIn.Items[J]; + AProp := GetPropInfo(AObj, AChild.FName); + if AProp <> nil then begin + case AProp.PropType^.Kind of + tkClass: + begin + AChildObj:=Pointer(GetOrdProp(AObj,AProp)); + if AChildObj is TStrings then + (AChildObj as TStrings).Text:=AChild.AsString + else if AChildObj is TCollection then + LoadCollection(AChild.AsJsonObject, AChildObj as TCollection) + else + readValue(AChild.AsJsonObject, AChildObj{$IFNDEF USE_UNICODE}, GetObjectTypeInfo(AChildObj){$ENDIF}); + end; + tkRecord, tkArray, tkDynArray://tkArray,tkDynArray͵û,tkRecord + readValue(AChild.AsJsonObject, Pointer(GetOrdProp(AObj, AProp)), AProp.PropType^); + tkInteger: + SetOrdProp(AObj, AProp, AChild.AsInteger); + tkChar,tkString,tkWChar, tkLString, tkWString{$IFDEF USE_UNICODE}, tkUString{$ENDIF}: + SetStrProp(AObj, AProp, AChild.AsString); + tkEnumeration: + begin + if GetTypeData(AProp.PropType^)^.BaseType^ = TypeInfo(Boolean) then + SetOrdProp(AObj, AProp, Integer(AChild.AsBoolean)) + else if AChild.FType = jdtInteger then + SetOrdProp(AObj, AProp, AChild.AsInteger) + else + SetEnumProp(AObj, AProp, AChild.AsString); + end; + tkSet: + begin + if AChild.FType = jdtInteger then + SetOrdProp(AObj, AProp, AChild.AsInteger) + else + SetSetProp(AObj, AProp, AChild.AsString); + end; + tkVariant: + SetVariantProp(AObj, AProp, AChild.AsVariant); + tkInt64: + SetInt64Prop(AObj, AProp, AChild.AsInt64); + end; + end; + end; + end; + + procedure SetDynArrayLen(arr:Pointer; AType:PTypeInfo; ALen:NativeInt); + var + pmem: Pointer; + begin + pmem := PPointer(arr)^; + DynArraySetLength(pmem, AType, 1, @ALen); + PPointer(arr)^ := pmem; + end; + + {$IFDEF USE_UNICODE} + procedure ToArray; + var + AContext: TRttiContext; + ASubType: TRttiType; + S: JSONString; + pd, pi: PByte; + ASubTypeInfo: PTypeInfo; + AChild: PJSONValue; + I, AOffset: Integer; + begin + AContext := TRttiContext.Create; + {$IF RTLVersion>25} + S := ArrayItemTypeName(AType.NameFld.ToString); + {$ELSE} + S := ArrayItemTypeName(string(AType.Name)); + {$IFEND} + ASubType := AContext.FindType(S); + ASubTypeInfo := ASubType.Handle; + if ASubType <> nil then begin + SetDynArrayLen(ADest, AType, aIn.Count); + pd := PPointer(ADest)^; + for I := 0 to aIn.Count - 1 do begin + AOffset := I * GetTypeData(AType).elSize; + pi := Pointer(IntPtr(pd)+AOffset); + AChild := aIn.Items[I]; + case ASubType.TypeKind of + tkInteger: + begin + case GetTypeData(ASubTypeInfo).OrdType of + otSByte: + PShortint(pi)^ := AChild.AsInteger; + otUByte: + pi^ := AChild.AsInteger; + otSWord: + PSmallint(pi)^ := AChild.AsInteger; + otUWord: + PWord(pi)^ := AChild.AsInteger; + otSLong: + PInteger(pi)^ := AChild.AsInteger; + otULong: + PCardinal(pi)^ := AChild.AsInteger; + end; + end; + {$IFNDEF NEXTGEN} + tkChar: + pi^ := Ord(PAnsiChar(AnsiString(AChild.AsString))[0]); + {$ENDIF !NEXTGEN} + tkEnumeration: + begin + if GetTypeData(ASubTypeInfo)^.BaseType^ = TypeInfo(Boolean) then + PBoolean(pi)^ := AChild.AsBoolean + else + begin + case GetTypeData(ASubTypeInfo)^.OrdType of + otSByte: + begin + if AChild.FType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := GetEnumValue(ASubTypeInfo, AChild.AsString); + end; + otUByte: + begin + if AChild.FType = jdtInteger then + pi^ := AChild.AsInteger + else + pi^ := GetEnumValue(ASubTypeInfo, AChild.AsString); + end; + otSWord: + begin + if AChild.FType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := GetEnumValue(ASubTypeInfo, AChild.AsString); + end; + otUWord: + begin + if AChild.FType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := GetEnumValue(ASubTypeInfo, AChild.AsString); + end; + otSLong: + begin + if AChild.FType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := GetEnumValue(ASubTypeInfo, AChild.AsString); + end; + otULong: + begin + if AChild.FType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := GetEnumValue(ASubTypeInfo, AChild.AsString); + end; + end; + end; + end; + tkFloat: + case GetTypeData(ASubTypeInfo)^.FloatType of + ftSingle: + PSingle(pi)^ := AChild.AsFloat; + ftDouble: + PDouble(pi)^ := AChild.AsFloat; + ftExtended: + PExtended(pi)^ := AChild.AsFloat; + ftComp: + PComp(pi)^ := AChild.AsFloat; + ftCurr: + PCurrency(pi)^ := AChild.AsFloat; + end; + {$IFNDEF NEXTGEN} + tkString: + PShortString(pi)^:=ShortString(AChild.AsString); + {$ENDIF !NEXTGEN} + tkSet: + begin + case GetTypeData(ASubTypeInfo)^.OrdType of + otSByte: + begin + if AChild.FType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + otUByte: + begin + if AChild.FType = jdtInteger then + pi^ := AChild.AsInteger + else + pi^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + otSWord: + begin + if AChild.FType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + otUWord: + begin + if AChild.FType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + otSLong: + begin + if AChild.FType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + otULong: + begin + if AChild.FType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := StringToSet(ASubTypeInfo, AChild.AsString); + end; + end; + end; + tkClass: + LoadClass(PPointer(pi)^, AChild); + tkWChar: + PWideChar(pi)^ := PWideChar(AChild.AsString)[0]; + {$IFNDEF NEXTGEN} + tkLString: + PAnsiString(pi)^ := AnsiString(AChild.AsString); + tkWString: + PWideString(pi)^ := AChild.AsString; + {$ENDIF} + tkVariant: + PVariant(pi)^ := AChild.AsVariant; + tkArray,tkDynArray: + readValue(AChild.AsJsonObject, pi, ASubTypeInfo); + tkRecord: + readValue(AChild.AsJsonObject, pi, ASubTypeInfo); + tkInt64: + PInt64(pi)^ := AChild.AsInt64; + tkUString: + PUnicodeString(pi)^ := AChild.AsString; + end; + end; + end else + raise Exception.Create(SArrayTypeMissed); + end; + {$ENDIF} + + {$IFDEF USE_UNICODE} + function GetFixedArrayItemType:PTypeInfo; + var + pType: PPTypeInfo; + begin + pType := GetTypeData(AType)^.ArrayData.ElType; + if pType = nil then + Result := nil + else + Result := pType^; + end; + + procedure ToFixedArray; + var + pi: Pointer; + ASubType: PTypeInfo; + AChild: PJSONValue; + I, C, ASize: Integer; + begin + C := GetTypeData(AType).ArrayData.ElCount; + ASubType := GetFixedArrayItemType; + if ASubType = nil then Exit; + ASize:=GetTypeData(ASubType).elSize; + for I := 0 to C-1 do begin + pi := Pointer(IntPtr(ADest)+ASize*I); + AChild := aIn.Items[I]; + case ASubType.Kind of + tkInteger: + begin + case GetTypeData(ASubType).OrdType of + otSByte: + PShortint(pi)^ := AChild.AsInteger; + otUByte: + PByte(pi)^ := AChild.AsInteger; + otSWord: + PSmallint(pi)^ := AChild.AsInteger; + otUWord: + PWord(pi)^ := AChild.AsInteger; + otSLong: + PInteger(pi)^ := AChild.AsInteger; + otULong: + PCardinal(pi)^ := AChild.AsInteger; + end; + end; + {$IFNDEF NEXTGEN} + tkChar: + PByte(pi)^ := Ord(PAnsiChar(AnsiString(AChild.AsString))[0]); + {$ENDIF !NEXTGEN} + tkEnumeration: + begin + if GetTypeData(ASubType)^.BaseType^ = TypeInfo(Boolean) then + PBoolean(pi)^ := AChild.AsBoolean + else begin + case GetTypeData(ASubType)^.OrdType of + otSByte: + begin + if AChild.FType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otUByte: + begin + if AChild.FType = jdtInteger then + PByte(pi)^ := AChild.AsInteger + else + PByte(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otSWord: + begin + if AChild.FType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otUWord: + begin + if AChild.FType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otSLong: + begin + if AChild.FType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + otULong: + begin + if AChild.FType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := GetEnumValue(ASubType, AChild.AsString); + end; + end; + end; + end; + tkFloat: + case GetTypeData(ASubType)^.FloatType of + ftSingle: + PSingle(pi)^ := AChild.AsFloat; + ftDouble: + PDouble(pi)^ := AChild.AsFloat; + ftExtended: + PExtended(pi)^ := AChild.AsFloat; + ftComp: + PComp(pi)^ := AChild.AsFloat; + ftCurr: + PCurrency(pi)^ := AChild.AsFloat; + end; + {$IFNDEF NEXTGEN} + tkString: + PShortString(pi)^ := ShortString(AChild.AsString); + {$ENDIF !NEXTGEN} + tkSet: + begin + case GetTypeData(ASubType)^.OrdType of + otSByte: + begin + if AChild.FType = jdtInteger then + PShortint(pi)^ := AChild.AsInteger + else + PShortint(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otUByte: + begin + if AChild.FType = jdtInteger then + PByte(pi)^ := AChild.AsInteger + else + PByte(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otSWord: + begin + if AChild.FType = jdtInteger then + PSmallint(pi)^ := AChild.AsInteger + else + PSmallint(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otUWord: + begin + if AChild.FType = jdtInteger then + PWord(pi)^ := AChild.AsInteger + else + PWord(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otSLong: + begin + if AChild.FType = jdtInteger then + PInteger(pi)^ := AChild.AsInteger + else + PInteger(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + otULong: + begin + if AChild.FType = jdtInteger then + PCardinal(pi)^ := AChild.AsInteger + else + PCardinal(pi)^ := StringToSet(ASubType, AChild.AsString); + end; + end; + end; + tkClass: + LoadClass(PPointer(pi)^, AChild); + tkWChar: + PWideChar(pi)^ := PWideChar(AChild.AsString)[0]; + {$IFNDEF NEXTGEN} + tkLString: + PAnsiString(pi)^ := AnsiString(AChild.AsString); + tkWString: + PWideString(pi)^ := AChild.AsString; + {$ENDIF} + tkVariant: + PVariant(pi)^ := AChild.AsVariant; + tkArray, tkDynArray: + readValue(AChild.AsJsonObject, pi, ASubType); + tkRecord: + readValue(AChild.AsJsonObject, pi, ASubType); + tkInt64: + PInt64(pi)^ := AChild.AsInt64; + tkUString: + PUnicodeString(pi)^ := AChild.AsString; + end; + end; + end; + {$ENDIF} +begin + if (aDest <> nil) and (Assigned(aIn)) then begin + {$IFDEF USE_UNICODE} + if aType.Kind = tkRecord then + ToRecord + else if aType.Kind = tkClass then + ToObject + else if aType.Kind = tkDynArray then + ToArray + else if aType.Kind = tkArray then + ToFixedArray + {$ELSE} + if aType.Kind = tkClass then + ToObject + {$ENDIF} + else + raise Exception.Create(SUnsupportPropertyType); + end; +end; + +{$IFDEF USE_UNICODE} +class function TYxdSerialize.writeToValue(aIn: PJSONValue): TValue; +begin + case aIn.FType of + jdtString: + Result := aIn.AsString; + jdtInteger: + Result := aIn.AsInt64; + jdtFloat: + Result := aIn.AsFloat; + jdtDateTime: + Result := aIn.AsDateTime; + jdtBoolean: + Result := aIn.AsBoolean; + jdtObject: + Result := writeToValue(aIn.AsJsonObject); + else + Result := TValue.Empty; + end; +end; +{$ENDIF} + +{$IFDEF USE_UNICODE} +class function TYxdSerialize.writeToValue(aIn: JSONBase): TValue; +var + AValues: array of TValue; + I: Integer; +begin + if not Assigned(aIn) then Exit; + SetLength(AValues, aIn.Count); + for I := 0 to aIn.Count - 1 do + AValues[I] := writeToValue(aIn.Items[I]); + Result := TValue.FromArray(TypeInfo(TValueArray), AValues); +end; +{$ENDIF} + +{$IFDEF USEDataSet} +class procedure TYxdSerialize.WriteDataSet(AOut: JSONBase; const Key: JSONString; + ADataSet: TDataSet; const PageIndex, PageSize: Integer; + Base64Blob: Boolean); +var + BlobStream: TMemoryStream; + + procedure AddDataSetMeta(MetaItem: JSONArray; Field: TField); + begin + MetaItem.Add(Field.FieldName); + if Field.DataType = ftAutoInc then + MetaItem.Add(Ord(ftLargeint)) + else + MetaItem.Add(Ord(Field.DataType)); + MetaItem.Add(Field.Size); + MetaItem.Add(Field.Required); + MetaItem.Add(Field.DisplayLabel); + end; + + procedure AddDataSetRow(DS: TDataSet; Item: JSONArray); + var + Field: TField; + begin + for Field in DS.Fields do begin + // жֶǷҪ + if Field.IsNull then + Item.Add(null) + else begin + case Field.DataType of + ftBoolean: + Item.Add(Field.AsBoolean); + ftDate, ftTime, ftDateTime, ftTimeStamp{$IFDEF USE_UNICODE}, ftTimeStampOffset{$ENDIF}: + Item.AddDateTime(Field.AsDateTime); + ftInteger, ftWord, ftSmallint{$IFDEF USE_UNICODE}, ftShortint{$ENDIF}: + Item.Add(Field.AsInteger); + ftLargeint, ftAutoInc: + Item.Add({$IFDEF USE_UNICODE}Field.AsLargeInt{$ELSE}Field.AsInteger{$ENDIF}); + ftFloat, ftBCD: // ftSingle + Item.Add(Field.AsFloat); + ftCurrency: + Item.Add(Field.AsCurrency); + ftString, ftWideString, ftGuid: + Item.Add(Field.AsString); + ftBlob, ftGraphic, ftMemo, ftTypedBinary: + begin + if not Assigned(BlobStream) then + BlobStream := TMemoryStream.Create + else + BlobStream.Position := 0; + TBlobField(Field).SaveToStream(BlobStream); + {$IFDEF USE_UNICODE} + if Base64Blob then begin + Item.Add(CSBlobs + CSBlobBase64 + JSONString(EncodeBase64(BlobStream.Memory, BlobStream.Position)) + '>'); + end else + Item.Add(CSBlobs + {$IFDEF USEYxdStr}YxdStr{$ELSE}YxdJson{$ENDIF}.BinToHex(BlobStream.Memory, BlobStream.Position) + '>'); + {$ELSE} + if Base64Blob then + Item.Add(CSBlobs + CSBlobBase64 + Base64Encode(BlobStream.Memory^, BlobStream.Position) + '>') + else begin + Item.Add(CSBlobs + {$IFDEF USEYxdStr}YxdStr{$ELSE}YxdJson{$ENDIF}.BinToHex(BlobStream.Memory, BlobStream.Position) + '>'); + end; + {$ENDIF} + end; + else + Item.Add(Field.AsString); + end; + end; + end; + end; + + procedure AddDataSet(DS: TDataSet); + var + Data: JSONArray; + Field: TField; + MoveIndex, StepIndex: Integer; + begin + Data := JSONObject(aOut).AddChildArray('meta'); + for Field in DS.Fields do + AddDataSetMeta(Data.AddChildArray(), Field); + + BlobStream := nil; + DS.DisableControls; + try + Data := JSONObject(aOut).AddChildArray('data'); + DS.First; + // ҳƶ¼ + if (PageIndex > 0) and (PageSize > 0) then begin + MoveIndex := (PageIndex - 1) * PageSize; + DS.MoveBy(MoveIndex); + end; + StepIndex := 0; + while not DS.Eof do begin + AddDataSetRow(DS, Data.AddChildArray); + if (PageSize > 0) then begin + Inc(StepIndex); + if StepIndex >= PageSize then + Break; + end; + DS.Next; + end; + finally + DS.EnableControls; + if Assigned(BlobStream) then + BlobStream.Free; + end; + end; + +begin + if aOut.IsJSONArray then + aOut := JSONArray(aOut).AddChildObject() + else if (Length(Key) > 0) then + aOut := JSONObject(aOut).addChildObject(key); + AddDataSet(ADataSet); +end; +{$ENDIF} + +class procedure TYxdSerialize.writeValue(aOut: JSONBase; const key: JSONString; aSource: Pointer; + aType: PTypeInfo); +{$IFDEF USE_UNICODE}var AValue: TValue;{$ENDIF} + + procedure AddCollection(AParent:JSONBase; ACollection:TCollection); + var + J: Integer; + begin + for J := 0 to ACollection.Count-1 do + writeValue(AParent, '', ACollection.Items[J]{$IFNDEF USE_UNICODE}, GetObjectTypeInfo(ACollection.Items[J]){$ENDIF}); + end; + + {$IFDEF USE_UNICODE} + //XE6System.rttiTValuetkSetʹBug + function SetAsOrd(AValue:TValue): Int64; + var + ATemp: Integer; + begin + AValue.ExtractRawData(@ATemp); + case GetTypeData(AValue.TypeInfo).OrdType of + otSByte: + Result := PShortint(@ATemp)^; + otUByte: + Result := PByte(@ATemp)^; + otSWord: + Result := PSmallint(@ATemp)^; + otUWord: + Result := PWord(@ATemp)^; + otSLong: + Result := PInteger(@ATemp)^; + otULong: + Result := PCardinal(@ATemp)^; + else + Result := 0 + end; + end; + {$ENDIF} + + {$IFDEF USE_UNICODE} + procedure SaveClass(AObj: TObject; AFieldItem: TRttiField); + begin + if (AObj is TStrings) then + JSONObject(aOut).put(AFieldItem.Name, TStrings(AObj).Text) + else if AObj is TCollection then + AddCollection(JSONObject(aOut).addChildArray(AFieldItem.Name), AObj as TCollection) + else //͵Ķ󲻱 + writeValue(aOut, AFieldItem.Name, AObj, AFieldItem.FieldType.Handle); + end; + {$ENDIF} + + {$IFDEF USE_UNICODE} + procedure AddRecord; + var + AFieldItem: TRttiField; + AContext: TRttiContext; + AFields: TArray; + ARttiType: TRttiType; + II, J: Integer; + begin + AContext := TRttiContext.Create; + ARttiType := AContext.GetType(AType); + AFields := ARttiType.GetFields; + //Ǵӽṹ壬¼ԱǶֻ¼乫ԣ⴦TStringsTCollection + for J := Low(AFields) to High(AFields) do begin + AFieldItem := AFields[J]; + if AFieldItem.FieldType <> nil then begin + case AFieldItem.FieldType.TypeKind of + tkInteger: + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).AsInteger); + {$IFNDEF NEXTGEN}tkString,tkLString,tkWString,{$ENDIF !NEXTGEN}tkUString: + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).AsString); + tkEnumeration: + begin + if GetTypeData(AFieldItem.FieldType.Handle).BaseType^ = TypeInfo(Boolean) then + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).AsBoolean) + else if JsonRttiEnumAsInt then + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).AsOrdinal) + else + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).ToString); + end; + tkSet: + begin + if JsonRttiEnumAsInt then + JSONObject(aOut).put(AFieldItem.Name, SetAsOrd(AFieldItem.GetValue(ASource))) + else + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).ToString); + end; + tkChar,tkWChar: + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).ToString); + tkFloat: + begin + if (AFieldItem.FieldType.Handle = TypeInfo(TDateTime)) or + (AFieldItem.FieldType.Handle = TypeInfo(TTime)) or + (AFieldItem.FieldType.Handle = TypeInfo(TDate)) + then + JSONObject(aOut).putDateTime(AFieldItem.Name, AFieldItem.GetValue(ASource).AsExtended) + else + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).AsExtended); + end; + tkInt64: + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).AsInt64); + tkVariant: + JSONObject(aOut).put(AFieldItem.Name, AFieldItem.GetValue(ASource).AsVariant); + tkArray, tkDynArray: + with JSONObject(aOut).addChildArray(AFieldItem.Name) do begin + AValue := AFieldItem.GetValue(ASource); + for II := 0 to AValue.GetArrayLength - 1 do + putObjectValue('', AValue.GetArrayElement(II)); + end; + tkClass: + SaveClass(AFieldItem.GetValue(ASource).AsObject, AFieldItem); + tkRecord: + writeValue(aOut, AFieldItem.Name, + Pointer(IntPtr(ASource) + AFieldItem.Offset), AFieldItem.FieldType.Handle); + end; + end else + raise Exception.CreateFmt(SMissRttiTypeDefine,[AFieldItem.Name]); + end; + end; + {$ENDIF} + + procedure AddObject; + var + AName: JSONString; + APropList: PPropList; + ACount: Integer; + AObj, AChildObj: TObject; + J: Integer; + begin + AObj := ASource; + ACount := GetPropList(AType,APropList); + try + for J := 0 to ACount - 1 do begin + if not ((APropList[J].PropType^.Kind in [tkMethod, tkInterface{$IFDEF USE_UNICODE}, tkClassRef, tkPointer, tkProcedure{$ENDIF}]) or + IsDefaultPropertyValue(AObj, APropList[J], nil)) then + begin + {$IF RTLVersion>25} + AName := APropList[J].NameFld.ToString; + {$ELSE} + AName := String(APropList[J].Name); + {$IFEND} + case APropList[J].PropType^.Kind of + tkClass: + begin + AChildObj := Pointer(GetOrdProp(AObj, APropList[J])); + if AChildObj is TStrings then + JSONObject(aOut).put(AName, (AChildObj as TStrings).Text) + else if AChildObj is TCollection then + AddCollection(JSONObject(aOut).addChildArray(AName), AChildObj as TCollection) + else + writeValue(aOut, AName, AChildObj{$IFNDEF USE_UNICODE}, GetObjectTypeInfo(AChildObj){$ENDIF}); + end; + tkInteger: + JSONObject(aOut).put(AName, GetOrdProp(AObj,APropList[J])); + tkChar,tkString,tkWChar, tkLString, tkWString{$IFDEF USE_UNICODE}, tkUString{$ENDIF}: + JSONObject(aOut).put(AName, GetStrProp(AObj,APropList[J])); + tkEnumeration: + begin + if GetTypeData(APropList[J]^.PropType^)^.BaseType^ = TypeInfo(Boolean) then + JSONObject(aOut).put(AName, GetOrdProp(AObj,APropList[J])<>0) + else if JsonRttiEnumAsInt then + JSONObject(aOut).put(AName, GetOrdProp(AObj,APropList[J])) + else + JSONObject(aOut).put(AName, GetEnumProp(AObj,APropList[J])); + end; + tkSet: + begin + if JsonRttiEnumAsInt then + JSONObject(aOut).put(AName, GetOrdProp(AObj, APropList[J])) + else + JSONObject(aOut).put(AName, GetSetProp(AObj,APropList[J],True)); + end; + tkVariant: + JSONObject(aOut).put(AName, GetPropValue(AObj,APropList[J])); + tkInt64: + JSONObject(aOut).put(AName, GetInt64Prop(AObj,APropList[J])); + tkRecord, tkArray, tkDynArray://¼顢̬ϵͳҲ棬Ҳûṩ̫õĽӿ + raise Exception.Create(SUnsupportPropertyType); + end; + end; + end; + finally + FreeMem(APropList); + end; + end; + + {$IFDEF USE_UNICODE} + procedure AddArray; + var + I: Integer; + begin + TValue.Make(ASource, AType, AValue); + for I := 0 to AValue.GetArrayLength - 1 do + writeValue(aOut, '', AValue.GetArrayElement(I)); + end; + {$ENDIF} +begin + if not Assigned(ASource) then Exit; + case AType.Kind of + {$IFDEF USE_UNICODE} + tkRecord: + begin + if aOut.IsJSONArray then + aOut := JSONArray(aOut).AddChildObject() + else + aOut := JSONObject(aOut).addChildObject(key); + AddRecord; + end; + {$ENDIF} + tkClass: + begin + if TObject(ASource) is TStrings then + JSONObject(aOut).put(key, TStrings(ASource).Text) + else if TObject(ASource) is TCollection then + AddCollection(aOut, TCollection(ASource)) + {$IFDEF USEDataSet} + else if TObject(ASource) is TDataSet then + WriteDataSet(aOut, Key, TDataSet(ASource), 0, -1) + {$ENDIF} + else begin + if aOut.IsJSONArray then + aOut := JSONArray(aOut).AddChildObject() + else if (Length(Key) > 0) then + aOut := JSONObject(aOut).addChildObject(key); + AddObject; + end; + end; + {$IFDEF USE_UNICODE} + tkDynArray: + begin + if aOut.IsJSONArray then + aOut := JSONArray(aOut).addChildArray() + else + aOut := JSONObject(aOut).addChildArray(key); + AddArray; + end; + {$ENDIF} + end; +end; + +{$IFDEF USE_UNICODE} +class procedure TYxdSerialize.writeValue(aOut: JSONBase; const key: JSONString; aInstance: TValue); +var + I,C:Integer; +begin + if not Assigned(aOut) then Exit; + case aInstance.Kind of + tkClass: + writeValue(aOut, key, aInstance.AsObject, aInstance.TypeInfo); + tkRecord: + writeValue(aOut, key, aInstance.GetReferenceToRawData, aInstance.TypeInfo); + tkArray, tkDynArray: + begin + if not aOut.IsJSONArray then + aOut := JSONObject(aOut).addChildArray(key) + else + aOut.Clear; + C := aInstance.GetArrayLength; + for I := 0 to C-1 do + writeValue(aOut, '', AInstance.GetArrayElement(I)); + end; + tkInteger, tkInt64: + JSONObject(aOut).put(key, AInstance.AsInt64); + tkChar, tkString,tkWChar, tkLString, tkWString, tkUString: + JSONObject(aOut).put(key, aInstance.ToString); + tkEnumeration: + begin + if GetTypeData(AInstance.TypeInfo)^.BaseType^ = TypeInfo(Boolean) then + JSONObject(aOut).put(key, aInstance.AsBoolean) + else if JsonRttiEnumAsInt then + JSONObject(aOut).put(key, aInstance.AsOrdinal) + else + JSONObject(aOut).put(key, aInstance.ToString) + end; + tkSet: + JSONObject(aOut).put(key, aInstance.ToString); + tkVariant: + JSONObject(aOut).put(key, aInstance.AsVariant) + end; +end; +{$ENDIF} + +end. +r \ No newline at end of file diff --git a/source/YxdStr.pas b/source/YxdStr.pas new file mode 100644 index 0000000..b181ec9 --- /dev/null +++ b/source/YxdStr.pas @@ -0,0 +1,3211 @@ +{*******************************************************} +{ } +{ YxdInclude û } +{ } +{ Ȩ (C) 2013 YangYxd } +{ } +{*******************************************************} + +unit YxdStr; + +interface + +// ǷʹURL +{$DEFINE USE_URLFUNC} +// Ƿʹַת +{$DEFINE USE_STRENCODEFUNC} + +//Delphi XE +{$IF (RTLVersion>=26)} +{$DEFINE USE_UNICODE} +{$IFEND} + +//ǷʹInline +{$DEFINE INLINE} + +{$IF (RTLVersion>=26) and (not Defined(NEXTGEN))} +{$DEFINE ANSISTRINGS} +{$IFEND} + +uses + {$IFNDEF UNICODE}Windows, {$ELSE} {$IFDEF MSWINDOWS}Windows, {$ENDIF}{$ENDIF} + {$IFDEF ANSISTRINGS}AnsiStrings, {$ENDIF} + {$IFDEF POSIX}Posix.String_, {$ENDIF} + {$IFDEF USE_URLFUNC}StrUtils, Math, {$ENDIF} + SysUtils, SysConst, Classes, Variants; + +type + {$IFDEF NEXTGEN} + AnsiChar = Byte; + PAnsiChar = ^AnsiChar; + WideString = UnicodeString; + AnsiString = record + private + FValue:TBytes; + function GetChars(AIndex: Integer): AnsiChar; + procedure SetChars(AIndex: Integer; const Value: AnsiChar); + function GetLength:Integer; + procedure SetLength(const Value: Integer); + function GetIsUtf8: Boolean; + public + class operator Implicit(const S:WideString):AnsiString; + class operator Implicit(const S:AnsiString):PAnsiChar; + class operator Implicit(const S:AnsiString):TBytes; + class operator Implicit(const ABytes:TBytes):AnsiString; + class operator Implicit(const S:AnsiString):WideString; + //class operator Implicit(const S:PAnsiChar):AnsiString; + //ַȽ + procedure From(p:PAnsiChar;AOffset,ALen:Integer); + property Chars[AIndex:Integer]:AnsiChar read GetChars write SetChars;default; + property Length:Integer read GetLength write SetLength; + property IsUtf8:Boolean read GetIsUtf8; + end; + {$ENDIF} + + {$if CompilerVersion < 23} + NativeUInt = Cardinal; + IntPtr = NativeInt; + {$ifend} + StringA = AnsiString; + {$IFDEF UNICODE} + StringW = UnicodeString; + TIntArray = TArray; + {$ELSE} + StringW = WideString; + TIntArray = array of Integer; + {$ENDIF} + CharA = AnsiChar; + CharW = WideChar; + PCharA = PAnsiChar; + PCharW = PWideChar; + +type + TTextEncoding = (teUnknown, {δ֪ı} teAuto,{Զ} teAnsi, { Ansi } + teUnicode16LE, { Unicode LE } teUnicode16BE, { Unicode BE } + teUTF8 { UTF8 } ); + +type + TStringCatHelper = class + private + FValue: array of char; + FStart, FDest: PChar; + FBlockSize: Integer; + FSize: Integer; + function GetValue: string; + function GetPosition: Integer; + function GetChars(AIndex:Integer): Char; + procedure SetPosition(const Value: Integer); + procedure NeedSize(ASize:Integer); + public + constructor Create; overload; + constructor Create(ASize: Integer); overload; + destructor Destroy; override; + function Cat(p: PChar; len: Integer): TStringCatHelper; overload; + function Cat(const s: string): TStringCatHelper; overload; + function Cat(c: Char): TStringCatHelper; overload; + function Cat(const V:Int64): TStringCatHelper;overload; + function Cat(const V:Double): TStringCatHelper;overload; + function Cat(const V:Boolean): TStringCatHelper;overload; + function Cat(const V:Currency): TStringCatHelper;overload; + function Cat(const V:TGuid): TStringCatHelper;overload; + function Cat(const V:Variant): TStringCatHelper;overload; + function Cat(const V:TStream): TStringCatHelper;overload; + function Space(count: Integer): TStringCatHelper; + function Back(ALen: Integer): TStringCatHelper; + function BackIf(const s: PChar): TStringCatHelper; + procedure Reset; + property Value: string read GetValue; + property Chars[Index: Integer]: Char read GetChars; + property Start: PChar read FStart; + property Current: PChar read FDest; + property Position: Integer read GetPosition write SetPosition; + end; + +type + TStringArrayItem = packed record + P: PChar; + Len: Integer; + end; + PStringArrayItem = ^TStringArrayItem; + TStringArrayData = array of TStringArrayItem; + +type + TStringArray = class; + TOnFilterEvent = function (Sender: TStringArray; const P: PChar; + const Len: Integer): Boolean; + + /// + /// ַ飬ַָ + /// + TStringArray = class(TObject) + private + FData: string; + FList: array of TStringArrayItem; + FCount, FCapacity: Integer; + FDelimiter: Char; + FTag: Integer; + FOnFilter: TOnFilterEvent; + procedure Grow; + procedure CheckIndex(const Index: Integer); + function GetItem(const Index: Integer): string; overload; + procedure SetDelimitedText(const Value: string); + procedure SetText(const Value: string); + protected + procedure SetCapacity(NewCapacity: Integer); virtual; + public + procedure Clear; + function Add(const P: PChar; const Len: Integer): Integer; + procedure SetDelimitedData(const Value: Pointer; const Len: Integer); + procedure GetString(const Index: Integer; var Data: string); + function GetText(const ADelimiter: string = #13#10): string; + function GetFloat(const Index: Integer): Double; + function GetValue(const Index: Integer): PStringArrayItem; + function GetItemValue(const Index: Integer): PStringArrayItem; + property Delimiter: Char read FDelimiter write FDelimiter; + property DelimitedText: string write SetDelimitedText; + property Count: Integer read FCount; + property Capacity: Integer read FCapacity; + property Items[const Index: Integer]: string read GetItem; default; + property Text: string read FData write SetText; + property Tag: Integer read FTag write FTag; + property OnFilter: TOnFilterEvent read FOnFilter write FOnFilter; + end; + +type + /// + /// ַ飬ַָ + /// + TStringArrayS = class(TObject) + private + FData: string; + FList: array of string; + FCount, FCapacity: Integer; + FDelimiter: Char; + procedure Grow; + function GetItem(const Index: Integer): string; + procedure SetItem(const Index: Integer; const Value: string); + procedure SetDelimitedText(const Value: string); + protected + procedure SetCapacity(NewCapacity: Integer); virtual; + public + function Add(const S: string): Integer; overload; virtual; + function Add(const P: PChar; const Len: Integer): Integer; overload; virtual; + procedure Clear; + property Delimiter: Char read FDelimiter write FDelimiter; + property DelimitedText: string write SetDelimitedText; + property Count: Integer read FCount; + property Capacity: Integer read FCapacity; + property Items[const Index: Integer]: string read GetItem write SetItem; default; + end; + +// -------------------------------------------------------------------------- +// ַת +// -------------------------------------------------------------------------- + +function StrDupX(const s: PChar; ACount:Integer): String; +function StrDupXA(const s: PCharA; ACount:Integer): StringA; +function StrDupXW(const s: PCharW; ACount:Integer): StringW; +function StrDup(const S: PChar; AOffset: Integer; const ACount: Integer): String; +procedure ExchangeByteOrder(p: PCharA; l: Integer); overload; +function ExchangeByteOrder(V: Smallint): Smallint; overload; inline; +function ExchangeByteOrder(V: Word): Word; overload; inline; +function ExchangeByteOrder(V: Integer): Integer; overload; inline; +function ExchangeByteOrder(V: Cardinal): Cardinal; overload; inline; +function ExchangeByteOrder(V: Int64): Int64; overload; inline; +function ExchangeByteOrder(V: Single): Single; overload; inline; +function ExchangeByteOrder(V: Double): Double; overload; inline; +// תɴдַ +function CharUpper(c: Char): Char; inline; +function CharUpperA(c: AnsiChar): AnsiChar; +function CharUpperW(c: WideChar): WideChar; +// ڴɨ +function MemScan(S: Pointer; len_s: Integer; sub: Pointer; len_sub: Integer): Pointer; +// ֽλȽ +function BinaryCmp(const p1, p2: Pointer; len: Integer): Integer; +// ַң UTF8ֱʹAnsi汾ɣ +function StrScanW(const Str: PCharW; Chr: CharW): PCharW; +function StrStr(src, sub: string): Integer; overload; inline; +function StrStr(src, sub: PChar): PChar; overload; inline; +function StrStrA(src, sub: PAnsiChar): PAnsiChar; +function StrStrW(src, sub: PWideChar): PWideChar; +function StrIStr(src, sub: string): Integer; overload; +function StrIStr(src, sub: PChar): PChar; overload; +function StrIStrA(src, sub: PAnsiChar): PAnsiChar; +function StrIStrW(src, sub: PWideChar): PWideChar; +function PosStr(sub, src: AnsiString; Offset: Integer = 0): Integer; overload; inline; +function PosStr(sub: PAnsiChar; src: PAnsiChar; Offset: Integer = 0): Integer; overload; inline; +function PosStr(sub: PAnsiChar; subLen: Integer; src: PAnsiChar; Offset: Integer): Integer; overload; inline; +function PosStr(sub: PAnsiChar; subLen: Integer; src: PAnsiChar; srcLen: Integer; Offset: Integer): Integer; overload; +// ַҵ +function RPosStr(sub, src: AnsiString; Offset: Integer = 0): Integer; overload; inline; +function RPosStr(sub: PAnsiChar; src: PAnsiChar; Offset: Integer = 0): Integer; overload; inline; +function RPosStr(sub: PAnsiChar; subLen: Integer; src: PAnsiChar; srcLen: Integer; Offset: Integer): Integer; overload; +// һ #0 Ϊ־Wideַ +function WideStrLen(S: PWideChar): Integer; inline; +// һ #0 Ϊ־Ansiַ +function AnsiStrLen(s: PAnsiChar): Integer; inline; +// һַȡADelimΪ־ӷADeleteΪTrueʱɾԴַݣDelim) +function Fetch(var AInput: string; const ADelim: string = ' '; + const ADelete: Boolean = True): string; inline; +// һתΪ16ַij +function LengthAsDWordToHex(const Value: Cardinal): Integer; +{$IFDEF USE_URLFUNC} +// URL +function UrlEncode(const AUrl: StringA): StringA; overload; +function UrlEncode(const AUrl: StringW): StringW; overload; +function UrlEncodeA(const AStr: PCharA; OutBuf: PCharA): Integer; overload; +function UrlEncodeW(const AStr: PCharW; OutBuf: PCharW): Integer; overload; +// URL +function UrlDecode(const Src: PCharA; OutBuf: PCharA; RaiseError: Boolean = True): Integer; overload; +function UrlDecode(const AStr: StringA; RaiseError: Boolean = True): StringA; overload; +function UTFStrToUnicode(UTFStr: StringA): StringW; +{$ENDIF} +// ַȡ +function LeftStr(const AText: AnsiString; const ACount: Integer): AnsiString; overload; inline; +function LeftStr(const AText: WideString; const ACount: Integer): WideString; overload; inline; +function RightStr(const AText: AnsiString; const ACount: Integer): AnsiString; overload; inline; +function RightStr(const AText: WideString; const ACount: Integer): WideString; overload; inline; +function MidStr(const AText: AnsiString; const AStart, ACount: Integer): AnsiString; overload; inline; +function MidStr(const AText: WideString; const AStart, ACount: Integer): WideString; overload; inline; +//ת +function IsHexChar(c: Char): Boolean; inline; +function HexValue(c: Char): Integer; +function HexChar(v: Byte): Char; +//ַǷָб +function CharIn(const c, list: PChar; ACharLen:PInteger = nil): Boolean; inline; +{$IFNDEF NEXTGEN} +function CharInA(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +function CharInU(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +{$ENDIF} +function CharInW(c, list: PWideChar; ACharLen: PInteger = nil): Boolean; +//ַ +function CharSizeA(c: PAnsiChar): Integer; +function CharSizeU(c: PAnsiChar): Integer; +function CharSizeW(c: PWideChar): Integer; +//ַ +function DetectTextEncoding(const p: Pointer; L: Integer; var b: Boolean): TTextEncoding; +{$IFDEF USE_STRENCODEFUNC} +function AnsiEncode(p:PWideChar; l:Integer): AnsiString; overload; +function AnsiEncode(const p: StringW): AnsiString; overload; +{$IFNDEF MSWINDOWS} +function AnsiDecode(const S: AnsiString): StringW; overload; +{$ENDIF} +function AnsiDecode(p: PAnsiChar; l:Integer): StringW; overload; +function Utf8Encode(const p: StringW): AnsiString; overload; +function Utf8Encode(p: PWideChar; l: Integer): AnsiString; overload; +{$IFNDEF MSWINDOWS} +function Utf8Decode(const S: AnsiString): StringW; overload; +{$ENDIF} +function Utf8Decode(p: PAnsiChar; l: Integer): StringW; overload; +// ַ +function LoadTextA(AStream: TStream; AEncoding: TTextEncoding=teUnknown): StringA; overload; +function LoadTextU(AStream: TStream; AEncoding: TTextEncoding=teUnknown): StringA; overload; +function LoadTextW(AStream: TStream; AEncoding: TTextEncoding=teUnknown): StringW; overload; +{$ENDIF} +function BinToHex(p: Pointer; l: Integer): string; overload; +function BinToHex(const ABytes:TBytes): string; overload; +procedure HexToBin(p: Pointer; l: Integer; var AResult: TBytes); overload; +function HexToBin(const S: String): TBytes; overload; +procedure HexToBin(const S: String; var AResult: TBytes); overload; + +//ַкţеʼַ +function StrPosA(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +function StrPosU(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +function StrPosW(start, current: PWideChar; var ACol, ARow:Integer): PWideChar; +//ȡһ +function DecodeLineA(var p:PAnsiChar; ASkipEmpty:Boolean=True): StringA; +function DecodeLineW(var p:PWideChar; ASkipEmpty:Boolean=True): StringW; +//հַ Ansi룬#9#10#13#161#161UCS룬#9#10#13#$3000 +function SkipSpaceA(var p: PAnsiChar): Integer; +function SkipSpaceU(var p: PAnsiChar): Integer; +function SkipSpaceW(var p: PWideChar): Integer; +//һ,#10Ϊнβ +function SkipLineA(var p: PAnsiChar): Integer; +function SkipLineU(var p: PAnsiChar): Integer; +function SkipLineW(var p: PWideChar): Integer; +//Ƿǿհַ +function IsSpaceA(const c:PAnsiChar; ASpaceSize:PInteger=nil): Boolean; +function IsSpaceU(const c:PAnsiChar; ASpaceSize:PInteger=nil): Boolean; +function IsSpaceW(const c:PWideChar; ASpaceSize:PInteger=nil): Boolean; +//ֱַָ +{$IFNDEF NEXTGEN} +function SkipUntilA(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar = {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF}): Integer; +function SkipUntilU(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar = {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF}): Integer; +{$ENDIF} +function SkipUntilW(var p: PWideChar; AExpects: PWideChar; AQuoter: WideChar = #0): Integer; +//жǷַָʼ +function StartWith(s, startby: PChar; AIgnoreCase: Boolean): Boolean; +function StartWithIgnoreCase(s, startby: PChar): Boolean; +//ı +procedure SaveTextA(AStream: TStream; const S: StringA); +procedure SaveTextU(AStream: TStream; const S: StringA; AWriteBom: Boolean = True); +procedure SaveTextW(AStream: TStream; const S: StringW; AWriteBom: Boolean = True); +procedure SaveTextWBE(AStream: TStream; const S: StringW; AWriteBom: Boolean = True); + +var + // ϵͳ ACP + SysACP: Integer; +{$IFDEF USE_STRENCODEFUNC} + // Javaʽ룬#$0ַΪ#$C080 + JavaFormatUtf8: Boolean = True; +{$ENDIF} + +implementation + +resourcestring + SOutOfIndex = 'Խ磬ֵ %d [%d..%d]ķΧڡ'; + SBadUnicodeChar = 'ЧUnicodeַ:%d'; +{$IFDEF USE_URLFUNC} + sErrorDecodingURLText = 'Error decoding URL style (%%XX) encoded string at position %d'; + sInvalidURLEncodedChar = 'Invalid URL encoded character (%s) at position %d'; +{$ENDIF} + +{$IFDEF MSWINDOWS} +type + TMSVCStrStr = function(s1, s2: PAnsiChar): PAnsiChar; cdecl; + TMSVCStrStrW = function(s1, s2: PWideChar): PWideChar; cdecl; + TMSVCMemCmp = function(s1, s2: Pointer; len: Integer): Integer; cdecl; +var + hMsvcrtl: HMODULE; + VCStrStr: TMSVCStrStr; + VCStrStrW: TMSVCStrStrW; + VCMemCmp: TMSVCMemCmp; +{$ENDIF} + +function WideStrLen(S: PWideChar): Integer; inline; +begin + Result := 0; + if S <> nil then + while S^ <> #0 do begin + Inc(Result); + Inc(S); + end; +end; + +function AnsiStrLen(s: PAnsiChar): Integer; inline; +begin + Result := 0; + if s <> nil then + {$IFDEF POSIX} + while S^ <> 0 do begin + {$ELSE} + while S^ <> #0 do begin + {$ENDIF} + Inc(Result); + Inc(s); + end; +end; + +function Fetch(var AInput: string; const ADelim: string = ' '; + const ADelete: Boolean = True): string; +var + LPos: Integer; +begin + if ADelim = #0 then + LPos := Pos(ADelim, AInput) + else + LPos := Pos(ADelim, AInput); + if LPos = 0 then begin + Result := AInput; + if ADelete then AInput := ''; + end else begin + Result := Copy(AInput, 1, LPos - 1); + if ADelete then + AInput := Copy(AInput, LPos + Length(ADelim), MaxInt); + end; +end; + +procedure ExchangeByteOrder(p: PCharA; l: Integer); +var + pe: PCharA; + c: CharA; +begin + pe := p; + Inc(pe, l); + while IntPtr(p) < IntPtr(pe) do begin + c := p^; + p^ := PCharA(IntPtr(p) + 1)^; + PCharA(IntPtr(p) + 1)^ := c; + Inc(p, 2); + end; +end; + +function ExchangeByteOrder(V: Smallint): Smallint; +var + pv: array [0 .. 1] of Byte absolute V; + pd: array [0 .. 1] of Byte absolute Result; +begin + pd[0] := pv[1]; + pd[1] := pv[0]; +end; + +function ExchangeByteOrder(V: Word): Word; +var + pv: array [0 .. 1] of Byte absolute V; + pd: array [0 .. 1] of Byte absolute Result; +begin + pd[0] := pv[1]; + pd[1] := pv[0]; +end; + +function ExchangeByteOrder(V: Integer): Integer; +var + pv: array [0 .. 3] of Byte absolute V; + pd: array [0 .. 3] of Byte absolute Result; +begin + pd[0] := pv[3]; + pd[1] := pv[2]; + pd[2] := pv[1]; + pd[3] := pv[0]; +end; + +function ExchangeByteOrder(V: Cardinal): Cardinal; +var + pv: array [0 .. 3] of Byte absolute V; + pd: array [0 .. 3] of Byte absolute Result; +begin + pd[0] := pv[3]; + pd[1] := pv[2]; + pd[2] := pv[1]; + pd[3] := pv[0]; +end; + +function ExchangeByteOrder(V: Int64): Int64; +var + pv: array [0 .. 7] of Byte absolute V; + pd: array [0 .. 7] of Byte absolute Result; +begin + pd[0] := pv[7]; + pd[1] := pv[6]; + pd[2] := pv[5]; + pd[3] := pv[4]; + pd[4] := pv[3]; + pd[5] := pv[2]; + pd[6] := pv[1]; + pd[7] := pv[0]; +end; + +function ExchangeByteOrder(V: Single): Single; +var + pv: array [0 .. 3] of Byte absolute V; + pd: array [0 .. 3] of Byte absolute Result; +begin + pd[0] := pv[3]; + pd[1] := pv[2]; + pd[2] := pv[1]; + pd[3] := pv[0]; +end; + +function ExchangeByteOrder(V: Double): Double; +var + pv: array [0 .. 7] of Byte absolute V; + pd: array [0 .. 7] of Byte absolute Result; +begin + pd[0] := pv[7]; + pd[1] := pv[6]; + pd[2] := pv[5]; + pd[3] := pv[4]; + pd[4] := pv[3]; + pd[5] := pv[2]; + pd[6] := pv[1]; + pd[7] := pv[0]; +end; + +function StrDupX(const s: PChar; ACount:Integer): String; +begin + SetLength(Result, ACount); + Move(s^, PChar(Result)^, ACount{$IFDEF UNICODE} shl 1{$ENDIF}); +end; + +function StrDupXA(const s: PCharA; ACount:Integer): StringA; +begin + {$IFDEF NEXTGEN} + Result.From(s, 0, ACount); + {$ELSE} + SetLength(Result, ACount); + Move(s^, PCharA(Result)^, ACount); + {$ENDIF} +end; + +function StrDupXW(const s: PCharW; ACount:Integer): StringW; +begin + SetLength(Result, ACount); + Move(s^, PCharW(Result)^, ACount shl 1); +end; + +function StrDup(const S: PChar; AOffset: Integer; const ACount: Integer): String; +var + C, ACharSize: Integer; + p, pds, pd: PChar; +begin + C := 0; + p := S + AOffset; + SetLength(Result, 4096); + pd := PChar(Result); + pds := pd; + while (p^ <> #0) and (C < ACount) do begin + ACharSize := {$IFDEF UNICODE} CharSizeW(p); {$ELSE} CharSizeA(p); {$ENDIF} + AOffset := pd - pds; + if AOffset + ACharSize = Length(Result) then begin + SetLength(Result, Length(Result){$IFDEF UNICODE} shl 1{$ENDIF}); + pds := PChar(Result); + pd := pds + AOffset; + end; + Inc(C); + pd^ := p^; + if ACharSize = 2 then + pd[1] := p[1]; + Inc(pd, ACharSize); + Inc(p, ACharSize); + end; + SetLength(Result, pd-pds); +end; + +function CharUpper(c: Char): Char; +begin + {$IFDEF UNICODE} + Result := CharUpperW(c); + {$ELSE} + Result := CharUpperA(c); + {$ENDIF}; +end; + +function CharUpperA(c: AnsiChar): AnsiChar; +begin + {$IFNDEF NEXTGEN} + if (c>=#$61) and (c<=#$7A) then + {$ELSE} + if (c>=$61) and (c<=$7A) then + {$ENDIF} + Result := AnsiChar(Ord(c)-$20) + else + Result := c; +end; + +function CharUpperW(c: WideChar): WideChar; +begin + if (c>=#$61) and (c<=#$7A) then + Result := WideChar(PWord(@c)^-$20) + else + Result := c; +end; + +function StrScanW(const Str: PCharW; Chr: CharW): PCharW; +begin + Result := Str; + while Result^ <> Chr do begin + if Result^ = #0 then begin + Result := nil; + Exit; + end; + Inc(Result); + end; +end; + +function MemScan(S: Pointer; len_s: Integer; sub: Pointer; len_sub: Integer): Pointer; +var + pb_s, pb_sub, pc_sub, pc_s: PByte; + remain: Integer; +begin + if len_s > len_sub then begin + pb_s := S; + pb_sub := sub; + Result := nil; + while len_s >= len_sub do begin + if pb_s^ = pb_sub^ then begin + remain := len_sub - 1; + pc_sub := pb_sub; + pc_s := pb_s; + Inc(pc_s); + Inc(pc_sub); + if BinaryCmp(pc_s, pc_sub, remain) = 0 then begin + Result := pb_s; + Break; + end; + end; + Inc(pb_s); + end; + end else if len_s = len_sub then begin + if CompareMem(S, sub, len_s) then + Result := S + else + Result := nil; + end else + Result := nil; +end; + +function BinaryCmp(const p1, p2: Pointer; len: Integer): Integer; + function CompareByByte: Integer; + var + b1, b2: PByte; + begin + if (len <= 0) or (p1 = p2) then + Result := 0 + else begin + b1 := p1; + b2 := p2; + Result := 0; + while len > 0 do begin + if b1^ <> b2^ then begin + Result := b1^ - b2^; + Exit; + end; + Inc(b1); + Inc(b2); + end; + end; + end; +begin + {$IFDEF MSWINDOWS} + if Assigned(VCMemCmp) then + Result := VCMemCmp(p1, p2, len) + else + Result := CompareByByte; + {$ELSE} + Result := memcmp(p1, p2, len); + {$ENDIF} +end; + +function StrStr(src, sub: string): Integer; +var + p1, p2: PChar; +begin + p1 := PChar(src); + p2 := PChar(sub); + {$IFDEF UNICODE} + p2 := StrStrW(p1, p2); + {$ELSE} + p2 := StrStrA(p1, p2); + {$ENDIF}; + if p2 <> nil then + Result := p2 - p1 + else + Result := -1; +end; + +function StrIStr(src, sub: string): Integer; +var + p1, p2: PChar; +begin + p1 := PChar(src); + p2 := PChar(sub); + {$IFDEF UNICODE} + p2 := StrIStrW(p1, p2); + {$ELSE} + p2 := StrIStrA(p1, p2); + {$ENDIF}; + if p2 <> nil then + Result := p2 - p1 + else + Result := -1; +end; + +function StrStr(src, sub: PChar): PChar; +begin + {$IFDEF UNICODE} + Result := StrStrW(src, sub); + {$ELSE} + Result := StrStrA(src, sub); + {$ENDIF}; +end; + +function DoStrStrASearch(s1, ps2: PAnsiChar): PAnsiChar; inline; +var + ps1: PAnsiChar; +begin + ps1 := s1; + Inc(ps1); + Inc(ps2); + while ps2^ <> {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if ps1^ = ps2^ then begin + Inc(ps1); + Inc(ps2); + end else + Break; + end; + if ps2^ = {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} then + Result := s1 + else + Result := nil; +end; + +function StrStrA(src, sub: PAnsiChar): PAnsiChar; +begin + {$IFDEF MSWINDOWS} + if Assigned(VCStrStr) then begin + Result := VCStrStr(src, sub); + Exit; + end; + {$ENDIF} + Result := nil; + if (src <> nil) and (sub <> nil) then begin + while src^ <> {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if src^ = sub^ then begin + Result := DoStrStrASearch(src, sub); + if Result <> nil then + Exit; + end; + Inc(src); + end; + end; +end; + +function StrStrW(src, sub: PWideChar): PWideChar; +var + I: Integer; +begin + {$IFDEF MSWINDOWS} + if Assigned(VCStrStrW) then begin + Result := VCStrStrW(src, sub); + Exit; + end; + {$ENDIF} + if (sub = nil) or (sub^ = #0) then + Result := src + else begin + Result := nil; + while src^ <> #0 do begin + if src^ = sub^ then begin + I := 1; + while sub[I] <> #0 do begin + if src[I] = sub[I] then + Inc(I) + else + Break; + end; + if sub[I] = #0 then begin + Result := src; + Break; + end; + end; + Inc(src); + end; + end; +end; + +function StrIStr(src, sub: PChar): PChar; +begin + {$IFDEF UNICODE} + Result := StrIStrW(src, sub); + {$ELSE} + Result := StrIStrA(src, sub); + {$ENDIF}; +end; + +function DoStrStrAISearch(s1, ps2: PAnsiChar): PAnsiChar; inline; +var + ps1: PAnsiChar; +begin + ps1 := s1; + Inc(ps1); + Inc(ps2); + while ps2^ <> {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if CharUpperA(ps1^) = CharUpperA(ps2^) then begin + Inc(ps1); + Inc(ps2); + end else + Break; + end; + if ps2^ = {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} then + Result := s1 + else + Result := nil; +end; + +function StrIStrA(src, sub: PAnsiChar): PAnsiChar; +begin + Result := nil; + if (src <> nil) and (sub <> nil) then begin + while src^ <> {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if CharUpperA(src^) = CharUpperA(sub^) then begin + Result := DoStrStrAISearch(src, sub); + if Result <> nil then + Exit; + end; + Inc(src); + end; + end; +end; + +function StrIStrW(src, sub: PWideChar): PWideChar; +var + I: Integer; + ws2: StringW; +begin + Result := nil; + if (src = nil) or (sub = nil) then + Exit; + ws2 := UpperCase(sub); + sub := PWideChar(ws2); + while src^ <> #0 do begin + if CharUpperW(src^) = sub^ then begin + I := 1; + while sub[I] <> #0 do begin + if CharUpperW(src[I]) = sub[I] then + Inc(I) + else + Break; + end; + if sub[I] = #0 then begin + Result := src; + Break; + end; + end; + Inc(src); + end; +end; + +function PosStr(sub, src: AnsiString; Offset: Integer = 0): Integer; +begin + Result := PosStr(PAnsiChar(sub), Length(sub), PAnsiChar(src), Length(src), Offset); + if Result <> -1 then + Inc(Result); +end; + +function PosStr(sub: PAnsiChar; src: PAnsiChar; Offset: Integer): Integer; +begin + Result := PosStr(sub, AnsiStrLen(sub), src, AnsiStrLen(src), Offset); +end; + +function PosStr(sub: PAnsiChar; subLen: Integer; src: PAnsiChar; Offset: Integer): Integer; +begin + Result := PosStr(sub, subLen, src, AnsiStrLen(src), Offset); +end; + +function PosStr(sub: PAnsiChar; subLen: Integer; src: PAnsiChar; srcLen: Integer; Offset: Integer): Integer; +var + p: PAnsiChar; + j: Integer; +begin + Result := -1; + if (sub = nil) or (src = nil) then + Exit; + if (Offset > 0) then Dec(srcLen, Offset); + if (subLen <= srcLen) and (subLen > 0) then begin + p := src; + Inc(p, Offset); + Dec(subLen); + Dec(srcLen, subLen); + while srcLen > 0 do begin + if PByte(p)^ = PByte(sub)^ then begin + if subLen > 0 then begin + for j := 1 to subLen do + {$IFDEF NEXTGEN} + if PAnsiChar(IntPtr(p)+j) <> PAnsiChar(IntPtr(sub)+j) then Break; + {$ELSE} + if p[j] <> sub[j] then Break; + {$ENDIF} + end else + j := 1; + if j > subLen then begin + {$IFDEF NEXTGEN} + Result := IntPtr(p) - IntPtr(src); + {$ELSE} + Result := p - src; + {$ENDIF} + Exit; + end; + end; + Inc(p); + Dec(srcLen); + end; + end; +end; + +function RPosStr(sub, src: AnsiString; Offset: Integer = 0): Integer; +begin + Result := RPosStr(PAnsiChar(sub), Length(sub), PAnsiChar(src), Length(src), Offset); + if Result <> -1 then + Inc(Result); +end; + +function RPosStr(sub: PAnsiChar; src: PAnsiChar; Offset: Integer = 0): Integer; +begin + Result := RPosStr(sub, AnsiStrLen(sub), src, AnsiStrLen(src), Offset); +end; + +function RPosStr(sub: PAnsiChar; subLen: Integer; src: PAnsiChar; srcLen: Integer; Offset: Integer): Integer; +var + p: PAnsiChar; + j: Integer; +begin + Result := -1; + if (sub = nil) or (src = nil) then + Exit; + p := src; + Inc(p, srcLen); + if (Offset > 0) then begin + Dec(p, Offset + 1); + Dec(srcLen, Offset); + end else + Dec(p); + if (subLen <= srcLen) and (subLen > 0) then begin + while srcLen > 0 do begin + if PByte(p)^ = PByte(sub)^ then begin + if (subLen > 1) then begin + for j := 1 to subLen do + {$IFDEF NEXTGEN} + if PAnsiChar(IntPtr(p)+j) <> PAnsiChar(IntPtr(sub)+j) then Break; + {$ELSE} + if p[j] <> sub[j] then Break; + {$ENDIF} + end else + j := 1; + if j = subLen then begin + {$IFDEF NEXTGEN} + Result := IntPtr(p) - IntPtr(src) + 1; + {$ELSE} + Result := p - src + 1; + {$ENDIF} + Exit; + end; + end; + Dec(p); + Dec(srcLen); + end; + end; +end; + +function LengthAsDWordToHex(const Value: Cardinal): Integer; +begin + if Value < $10 then + Result := 1 + else if Value < $100 then + Result := 2 + else if Value < $1000 then + Result := 3 + else if Value < $10000 then + Result := 4 + else if Value < $100000 then + Result := 5 + else if Value < $1000000 then + Result := 6 + else if Value < $10000000 then + Result := 7 + else + Result := 8; +end; + +{$IFDEF USE_URLFUNC} +function UrlDecode(const Src: PCharA; OutBuf: PCharA; RaiseError: Boolean): Integer; +const + H_BYTE:array[0..255] of Smallint = + ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,$00,$10,$20,$30,$40,$50,$60,$70,$80,$90,-1,-1,-1,-1,-1,-1 + ,-1,$A0,$B0,$C0,$D0,$E0,$F0,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ); + + L_BYTE: array[0..255] of Smallint = + ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,-1,-1,-1,-1,-1,-1 + ,-1,$0A,$0B,$0C,$0D,$0E,$0F,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ); +var + Sp, Rp: PAnsichar; + HB, LB: SmallInt; +begin + Result := -1; + if (Src = nil) or (OutBuf = nil) then + Exit; + Sp := Src; + Rp := OutBuf; + LB := -1; + while Sp^ <> #0 do begin + case Sp^ of + '+': Rp^ := #32; + '%': begin + // Look for an escaped % (%%) or % encoded character + Inc(Sp); + if Sp^ = '%' then + Rp^ := '%' + else begin + HB := H_BYTE[Byte(Sp^)]; + if HB <> 0 then + LB := L_BYTE[Byte((Sp+1)^)]; + if (HB <> -1) and (LB <> -1) then begin + Rp^ := AnsiChar(HB + LB); + Inc(Sp); + end else begin + if RaiseError then + raise Exception.Create(Format(sErrorDecodingURLText, [Sp - Src])) + else + Exit; + end; + end; + end; + else + Rp^ := Sp^; + end; + Inc(Rp); + Inc(Sp); + end; + Result := Rp - OutBuf; +end; + +function UrlDecode(const AStr: StringA; RaiseError: Boolean): StringA; +var + I: Integer; +begin + if Length(AStr) > 0 then begin + SetLength(Result, Length(AStr)); + I := UrlDecode(Pointer(AStr), Pointer(Result), RaiseError); + if I = -1 then + Result := '' + else if I <> Length(Result) then + SetLength(Result, I); + end else + Result := ''; +end; + +function UrlEncodeA(const AStr: PCharA; OutBuf: PCharA): Integer; +const + HTTP_CONVERT: array[0..255] of PCharA = ( + ' %00#','%01#','%02#','%03#','%04#','%05#','%06#','%07#' + ,'%08#','%09#','%0A#','%0B#','%0C#','%0D#','%0E#','%0F#' + ,'%10#','%11#','%12#','%13#','%14#','%15#','%16#','%17#' + ,'%18#','%19#','%1A#','%1B#','%1C#','%1D#','%1E#','%1F#' + ,'%20#','' ,'%22#','%23#','' ,'%25#','%26#','' + ,'' ,'' ,'' ,'%2B#','%2C#','' ,'' ,'%2F#' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'%3A#','%3B#','%3C#','%3D#','%3E#','%3F#' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'%5B#','%5C#','%5D#','%5E#','' + ,'%60#','' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'%7B#','%7C#','%7D#','%7E#','%7F#' + ,'%80#','%81#','%82#','%83#','%84#','%85#','%86#','%87#' + ,'%88#','%89#','%8A#','%8B#','%8C#','%8D#','%8E#','%8F#' + ,'%90#','%91#','%92#','%93#','%94#','%95#','%96#','%97#' + ,'%98#','%99#','%9A#','%9B#','%9C#','%9D#','%9E#','%9F#' + ,'%A0#','%A1#','%A2#','%A3#','%A4#','%A5#','%A6#','%A7#' + ,'%A8#','%A9#','%AA#','%AB#','%AC#','%AD#','%AE#','%AF#' + ,'%B0#','%B1#','%B2#','%B3#','%B4#','%B5#','%B6#','%B7#' + ,'%B8#','%B9#','%BA#','%BB#','%BC#','%BD#','%BE#','%BF#' + ,'%C0#','%C1#','%C2#','%C3#','%C4#','%C5#','%C6#','%C7#' + ,'%C8#','%C9#','%CA#','%CB#','%CC#','%CD#','%CE#','%CF#' + ,'%D0#','%D1#','%D2#','%D3#','%D4#','%D5#','%D6#','%D7#' + ,'%D8#','%D9#','%DA#','%DB#','%DC#','%DD#','%DE#','%DF#' + ,'%E0#','%E1#','%E2#','%E3#','%E4#','%E5#','%E6#','%E7#' + ,'%E8#','%E9#','%EA#','%EB#','%EC#','%ED#','%EE#','%EF#' + ,'%F0#','%F1#','%F2#','%F3#','%F4#','%F5#','%F6#','%F7#' + ,'%F8#','%F9#','%FA#','%FB#','%FC#','%FD#','%FE#','%FF#' + ); +var + Sp, Rp, P: PCharA; +begin + Sp := AStr; + Rp := OutBuf; + while Sp^ <> #0 do begin + if Sp^ = ' ' then + Rp^ := '+' + else begin + P := HTTP_CONVERT[Ord(Sp^)]; + if P^ = #0 then + Rp^ := Sp^ + else begin + PInteger(Rp)^ := PInteger(P)^; + Inc(Rp, 2); + end; + end; + Inc(Rp); + Inc(Sp); + end; + Result := Rp - OutBuf; +end; + +function UrlEncodeW(const AStr: PCharW; OutBuf: PCharW): Integer; +const + HTTP_CONVERT: array[0..255] of PCharW = ( + ' %00#','%01#','%02#','%03#','%04#','%05#','%06#','%07#' + ,'%08#','%09#','%0A#','%0B#','%0C#','%0D#','%0E#','%0F#' + ,'%10#','%11#','%12#','%13#','%14#','%15#','%16#','%17#' + ,'%18#','%19#','%1A#','%1B#','%1C#','%1D#','%1E#','%1F#' + ,'%20#','' ,'%22#','%23#','' ,'%25#','%26#','' + ,'' ,'' ,'' ,'%2B#','%2C#','' ,'' ,'%2F#' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'%3A#','%3B#','%3C#','%3D#','%3E#','%3F#' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'%5B#','%5C#','%5D#','%5E#','' + ,'%60#','' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'' ,'' ,'' ,'' ,'' + ,'' ,'' ,'' ,'%7B#','%7C#','%7D#','%7E#','%7F#' + ,'%80#','%81#','%82#','%83#','%84#','%85#','%86#','%87#' + ,'%88#','%89#','%8A#','%8B#','%8C#','%8D#','%8E#','%8F#' + ,'%90#','%91#','%92#','%93#','%94#','%95#','%96#','%97#' + ,'%98#','%99#','%9A#','%9B#','%9C#','%9D#','%9E#','%9F#' + ,'%A0#','%A1#','%A2#','%A3#','%A4#','%A5#','%A6#','%A7#' + ,'%A8#','%A9#','%AA#','%AB#','%AC#','%AD#','%AE#','%AF#' + ,'%B0#','%B1#','%B2#','%B3#','%B4#','%B5#','%B6#','%B7#' + ,'%B8#','%B9#','%BA#','%BB#','%BC#','%BD#','%BE#','%BF#' + ,'%C0#','%C1#','%C2#','%C3#','%C4#','%C5#','%C6#','%C7#' + ,'%C8#','%C9#','%CA#','%CB#','%CC#','%CD#','%CE#','%CF#' + ,'%D0#','%D1#','%D2#','%D3#','%D4#','%D5#','%D6#','%D7#' + ,'%D8#','%D9#','%DA#','%DB#','%DC#','%DD#','%DE#','%DF#' + ,'%E0#','%E1#','%E2#','%E3#','%E4#','%E5#','%E6#','%E7#' + ,'%E8#','%E9#','%EA#','%EB#','%EC#','%ED#','%EE#','%EF#' + ,'%F0#','%F1#','%F2#','%F3#','%F4#','%F5#','%F6#','%F7#' + ,'%F8#','%F9#','%FA#','%FB#','%FC#','%FD#','%FE#','%FF#' + ); +var + Sp, Rp, P: PCharW; + Buf: array [0..4] of Byte; + I, J: Integer; +begin + Sp := AStr; + Rp := OutBuf; + while Sp^ <> #0 do begin + if Sp^ = ' ' then + Rp^ := '+' + else begin + if Ord(Sp^) > $FF then begin + I := WideCharToMultiByte(CP_ACP, 0, Sp, 1, @Buf[0], 4, nil, nil); + for J := 0 to I - 1 do begin + P := HTTP_CONVERT[Buf[J]]; + PInt64(Rp)^ := PInt64(P)^; + Inc(Rp, 3); + end; + Inc(Sp); + Continue; + end else begin + P := HTTP_CONVERT[Ord(Sp^)]; + if P^ = #0 then + Rp^ := Sp^ + else begin + PInt64(Rp)^ := PInt64(P)^; + Inc(Rp, 2); + end; + end; + end; + Inc(Rp); + Inc(Sp); + end; + Result := Rp - OutBuf; +end; + +function UrlEncode(const AUrl: StringA): StringA; +var + I: Integer; +begin + if Length(AUrl) > 0 then begin + SetLength(Result, Length(AUrl) * 3); + I := UrlEncodeA(PAnsiChar(AUrl), @Result[1]); + if Length(Result) <> I then + SetLength(Result, I); + end else + Result := ''; +end; + +function UrlEncode(const AUrl: StringW): StringW; +var + I: Integer; +begin + if Length(AUrl) > 0 then begin + SetLength(Result, Length(AUrl) * 3); + I := UrlEncodeW(PWideChar(AUrl), @Result[1]); + if Length(Result) <> I then + SetLength(Result, I); + end else + Result := ''; +end; +{$ENDIF} + +{$IFDEF USE_URLFUNC} +function XDigit(Ch : AnsiChar) : Integer; +begin + {$IFDEF NEXTGEN} + if (Ch >= Ord('0')) and (Ch <= Ord('9')) then + {$ELSE} + if (Ch >= '0') and (Ch <= '9') then + {$ENDIF} + Result := Ord(Ch) - Ord('0') + else + Result := (Ord(Ch) and 15) + 9; +end; + +function UTFStrToUnicode(UTFStr: StringA): StringW; +var + I:Integer; + Index:Integer; + HexStr:String; + LowerCaseUTFStr:String; + WChar:WideChar; + WCharWord:Word; + AChar:AnsiChar; +begin + ////\u60a8\u7684\u9a8c\u8bc1\u7801\u9519\u8bef + Result:=''; + LowerCaseUTFStr := LowerCase(string(UTFStr)); + Index:=PosEx('\u',LowerCaseUTFStr,1); + while Index>0 do + begin + HexStr:=Copy(LowerCaseUTFStr,Index+2,4); + WCharWord:=0; + //HexStr=60a8 + for I := 1 to Length(HexStr) do + begin + AChar:=AnsiChar(HexStr[I]); + WCharWord:=WCharWord+XDigit(AChar)*Ceil(Power(16,4-I)); + end; + WChar:=WideChar(WCharWord); + //WChar= + Result:=Result+WChar; + Index:=PosEx('\u',LowerCaseUTFStr,Index+6); + end; +end; +{$ENDIF} + +{$IFNDEF NEXTGEN} +procedure CalcCharLengthA(var Lens: TIntArray; list: PAnsiChar); +var + i, l: Integer; +begin + i := 0; + System.SetLength(Lens, Length(List)); + while i< Length(List) do begin + l := CharSizeA(@list[i]); + lens[i] := l; + Inc(i, l); + end; +end; +{$ENDIF} + +{$IFNDEF NEXTGEN} +procedure CalcCharLengthU(var Lens: TIntArray; list: PAnsiChar); +var + i, l: Integer; +begin + i := 0; + System.SetLength(Lens, Length(List)); + while i< Length(List) do begin + l := CharSizeU(@list[i]); + lens[i] := l; + Inc(i, l); + end; +end; +{$ENDIF} + +// ַǷָб +function CharIn(const c, list: PChar; ACharLen:PInteger = nil): Boolean; +begin +{$IFDEF UNICODE} + Result := CharInW(c, list, ACharLen); +{$ELSE} + Result := CharInA(c, list, ACharLen); +{$ENDIF} +end; + +{$IFNDEF NEXTGEN} +function CharInA(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +var + i: Integer; + lens: TIntArray; +begin + Result := False; + CalcCharLengthA(lens, list); + i := 0; + while i < Length(list) do begin + if CompareMem(c, @list[i], lens[i]) then begin + if ACharLen <> nil then + ACharLen^:=lens[i]; + Result := True; + Break; + end else + Inc(i, lens[i]); + end; +end; +{$ENDIF} + +function CharInW(c, list: PWideChar; ACharLen: PInteger = nil): Boolean; +var + p: PWideChar; +begin + Result:=False; + p := list; + while p^ <> #0 do begin + if p^ = c^ then begin + if (p[0]>=#$DB00) and (p[0]<=#$DBFF) then begin + if p[1]=c[1] then begin + Result := True; + if ACharLen <> nil then + ACharLen^ := 2; + Break; + end; + end else begin + Result := True; + if ACharLen <> nil then + ACharLen^ := 1; + Break; + end; + end; + Inc(p); + end; +end; + +{$IFNDEF NEXTGEN} +function CharInU(c, list: PAnsiChar; ACharLen: PInteger = nil): Boolean; +var + i: Integer; + lens: TIntArray; +begin + Result := False; + CalcCharLengthU(lens, list); + i := 0; + while i < Length(list) do begin + if CompareMem(c, @list[i], lens[i]) then begin + if ACharLen <> nil then + ACharLen^ := lens[i]; + Result := True; + Break; + end else + Inc(i, lens[i]); + end; +end; +{$ENDIF} + +//㵱ǰַij +// GB18030,GBKGB2312 +// ֽڣֵ00x7F +// ˫ֽڣһֽڵֵ0x810xFEڶֽڵֵ0x400xFE0x7F +// ֽڣһֽڵֵ0x810xFEڶֽڵֵ0x300x39ֽڴ0x810xFEĸֽڴ0x300x39 +function CharSizeA(c: PAnsiChar): Integer; +begin + if SysACP = 936 then begin + Result:=1; + {$IFDEF NEXTGEN} + if (c^>=$81) and (c^<=$FE) then begin + Inc(c); + if (c^>=$40) and (c^<=$FE) and (c^<>$7F) then + Result:=2 + else if (c^>=$30) and (c^<=$39) then begin + Inc(c); + if (c^>=$81) and (c^<=$FE) then begin + Inc(c); + if (c^>=$30) and (c^<=$39) then + Result:=4; + end; + end; + end; + {$ELSE} + if (c^>=#$81) and (c^<=#$FE) then begin + Inc(c); + if (c^>=#$40) and (c^<=#$FE) and (c^<>#$7F) then + Result:=2 + else if (c^>=#$30) and (c^<=#$39) then begin + Inc(c); + if (c^>=#$81) and (c^<=#$FE) then begin + Inc(c); + if (c^>=#$30) and (c^<=#$39) then + Result:=4; + end; + end; + end; + {$ENDIF} + end else + {$IFDEF UNICODE} + {$IFDEF NEXTGEN} + if TEncoding.ANSI.CodePage = CP_UTF8 then + Result := CharSizeU(c) + else if (c^<128) or (TEncoding.ANSI.CodePage=437) then + Result:=1 + else + Result:=2; + {$ELSE} + {$IF RTLVersion>26} + Result := AnsiStrings.StrCharLength(PAnsiChar(c)); + {$ELSE} + Result := Sysutils.StrCharLength(PAnsiChar(c)); + {$IFEND} + {$ENDIF} + {$ELSE} + Result := StrCharLength(PAnsiChar(c)); + {$ENDIF} +end; + +function CharSizeU(c: PAnsiChar): Integer; +begin + if (Ord(c^) and $80) = 0 then + Result := 1 + else begin + if (Ord(c^) and $FC) = $FC then //4000000+ + Result := 6 + else if (Ord(c^) and $F8)=$F8 then//200000-3FFFFFF + Result := 5 + else if (Ord(c^) and $F0)=$F0 then//10000-1FFFFF + Result := 4 + else if (Ord(c^) and $E0)=$E0 then//800-FFFF + Result := 3 + else if (Ord(c^) and $C0)=$C0 then//80-7FF + Result := 2 + else + Result := 1; + end +end; + +function CharSizeW(c: PWideChar): Integer; +begin + if (c[0]>=#$DB00) and (c[0]<=#$DBFF) and (c[1] >= #$DC00) and (c[1] <= #$DFFF) then + Result := 2 + else + Result := 1; +end; + +function DetectTextEncoding(const p: Pointer; L: Integer; var b: Boolean): TTextEncoding; +const + NoUtf8Char: array [0 .. 3] of Byte = ($C1, $AA, $CD, $A8); // ANSIͨ +var + pAnsi: PByte; + pWide: PWideChar; + I, AUtf8CharSize: Integer; + + function IsUtf8Order(var ACharSize:Integer):Boolean; + var + I: Integer; + ps: PByte; + const + Utf8Masks:array [0..4] of Byte=($C0, $E0, $F0, $F8, $FC); + begin + ps := pAnsi; + ACharSize := CharSizeU(PAnsiChar(ps)); + Result := False; + if ACharSize > 1 then begin + I := ACharSize-2; + if ((Utf8Masks[I] and ps^) = Utf8Masks[I]) then begin + Inc(ps); + Result:=True; + for I := 1 to ACharSize-1 do begin + if (ps^ and $80)<>$80 then begin + Result:=False; + Break; + end; + Inc(ps); + end; + end; + end; + end; + +begin + Result := teAnsi; + b := false; + if L >= 2 then begin + pAnsi := PByte(p); + pWide := PWideChar(p); + b := True; + if pWide^ = #$FEFF then + Result := teUnicode16LE + else if pWide^ = #$FFFE then + Result := teUnicode16BE + else if L >= 3 then begin + if (pAnsi^ = $EF) and (PByte(IntPtr(pAnsi) + 1)^ = $BB) and + (PByte(IntPtr(pAnsi) + 2)^ = $BF) then // UTF-8 + Result := teUTF8 + else begin// ַǷзUFT-8ַ11... + b := false; + Result := teUnknown;//ļΪUTF8룬ȻǷвUTF-8 + I := 0; + Dec(L, 2); + while I<=L do begin + if (pAnsi^ and $80) <> 0 then begin // λΪ1 + if (l - I >= 4) then begin + if CompareMem(pAnsi, @NoUtf8Char[0], 4) then begin + // ͨԵUTF-8ж + Inc(pAnsi, 4); + Inc(I, 4); + continue; + end; + end; + if IsUtf8Order(AUtf8CharSize) then begin + if AUtf8CharSize>2 then begin//ִ2ֽڳȵUTF8У99%UTF-8ˣж + Result := teUTF8; + Break; + end; + Inc(pAnsi,AUtf8CharSize); + Inc(I, AUtf8CharSize); + end else begin + Result:=teAnsi; + Break; + end; + end else begin + if pAnsi^=0 then begin //00 xx (xx<128) λǰBE + if PByte(IntPtr(pAnsi)+1)^<128 then begin + Result := teUnicode16BE; + Break; + end; + end else if PByte(IntPtr(pAnsi)+1)^=0 then begin//xx 00 λǰLE + Result:=teUnicode16LE; + Break; + end; + Inc(pAnsi); + Inc(I); + end; + end; + if Result = teUnknown then + Result := teAnsi; + end; + end; + end; +end; + +{$IFDEF USE_STRENCODEFUNC} +function AnsiEncode(p:PWideChar; l:Integer): AnsiString; +var + ps: PWideChar; + {$IFDEF MSWINDOWS} + len: Integer; + {$ENDIF} +begin + if l<=0 then begin + ps:=p; + while ps^<>#0 do Inc(ps); + l:=ps-p; + end; + if l>0 then begin + {$IFDEF MSWINDOWS} + len := WideCharToMultiByte(CP_ACP,0,p,l,nil,0,nil,nil); + SetLength(Result, len); + WideCharToMultiByte(CP_ACP,0,p,l,PAnsiChar(Result), len, nil, nil); + {$ELSE} + Result.Length:=l shl 1; + Result.FValue[0]:=0; + Move(p^,PAnsiChar(Result)^,l shl 1); + Result:=TEncoding.Convert(TEncoding.Unicode,TEncoding.ANSI,Result.FValue,1,l shl 1); + {$ENDIF} + end else + Result := ''; +end; +{$ENDIF} + +function LeftStr(const AText: AnsiString; const ACount: Integer): AnsiString; +begin + Result := Copy(AText, 1, ACount); +end; + +function LeftStr(const AText: WideString; const ACount: Integer): WideString; +begin + Result := Copy(AText, 1, ACount); +end; + +function RightStr(const AText: AnsiString; const ACount: Integer): AnsiString; +begin + Result := Copy(AText, Length(AText) + 1 - ACount, ACount); +end; + +function RightStr(const AText: WideString; const ACount: Integer): WideString; +begin + Result := Copy(AText, Length(AText) + 1 - ACount, ACount); +end; + +function MidStr(const AText: AnsiString; const AStart, ACount: Integer): AnsiString; +begin + Result := Copy(AText, AStart, ACount); +end; + +function MidStr(const AText: WideString; const AStart, ACount: Integer): WideString; +begin + Result := Copy(AText, AStart, ACount); +end; + +{$IFDEF USE_STRENCODEFUNC} +function AnsiEncode(const p: StringW):AnsiString; +begin + Result := AnsiEncode(PWideChar(p), Length(p)); +end; +{$ENDIF} + +{$IFDEF USE_STRENCODEFUNC} {$IFNDEF MSWINDOWS} +function AnsiDecode(const S: AnsiString): StringW; +begin + if S.IsUtf8 then + Result := Utf8Decode(S) + else + Result := TEncoding.ANSI.GetString(S.FValue, 1, S.Length); +end; +{$ENDIF} {$ENDIF} + +{$IFDEF USE_STRENCODEFUNC} +function AnsiDecode(p: PAnsiChar; l:Integer): StringW; +var + ps: PAnsiChar; +{$IFNDEF MSWINDOWS} + ABytes:TBytes; +{$ENDIF} +begin + if l<=0 then begin + ps := p; + while ps^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do Inc(ps); + l:=IntPtr(ps)-IntPtr(p); + end; + if l>0 then begin + {$IFDEF MSWINDOWS} + System.SetLength(Result, MultiByteToWideChar(CP_ACP,0,PAnsiChar(p),l,nil,0)); + MultiByteToWideChar(CP_ACP, 0, PAnsiChar(p),l,PWideChar(Result),Length(Result)); + {$ELSE} + System.SetLength(ABytes, l); + Move(p^, PByte(@ABytes[0])^, l); + Result := TEncoding.ANSI.GetString(ABytes); + {$ENDIF} + end else + System.SetLength(Result,0); +end; +{$ENDIF} + +{$IFDEF USE_STRENCODEFUNC} +function Utf8Encode(const p: StringW): AnsiString; +begin + Result := Utf8Encode(PWideChar(p), Length(p)); +end; + +function Utf8Encode(p:PWideChar; l:Integer): AnsiString; +var + ps:PWideChar; + pd,pds:PAnsiChar; + c:Cardinal; +begin + if p=nil then + Result := '' + else begin + if l<=0 then begin + ps:=p; + while ps^<>#0 do + Inc(ps); + l:=ps-p; + end; + {$IFDEF NEXTGEN} + Result.Length:=l*6; + {$ELSE} + SetLength(Result, l*6);//UTF8ÿַ6ֽڳ,һԷ㹻Ŀռ + {$ENDIF} + if l>0 then begin + Result[1] := {$IFDEF NEXTGEN}1{$ELSE}#1{$ENDIF}; + ps:=p; + pd:=PAnsiChar(Result); + pds:=pd; + while l>0 do begin + c:=Cardinal(ps^); + Inc(ps); + if (c>=$D800) and (c<=$DFFF) then begin//Unicode չַ + c:=(c-$D800); + if (ps^>=#$DC00) and (ps^<=#$DFFF) then begin + c:=$10000+((c shl 10) + (Cardinal(ps^)-$DC00)); + Inc(ps); + Dec(l); + end else + raise Exception.Create(Format(SBadUnicodeChar,[IntPtr(ps^)])); + end; + Dec(l); + if c=$0 then begin + if JavaFormatUtf8 then begin//Javaʽ룬#$0ַΪ#$C080 + pd^:={$IFDEF NEXTGEN}$C0{$ELSE}#$C0{$ENDIF}; + Inc(pd); + pd^:={$IFDEF NEXTGEN}$80{$ELSE}#$80{$ENDIF}; + Inc(pd); + end else begin + pd^:=AnsiChar(c); + Inc(pd); + end; + end else if c<=$7F then begin //1B + pd^:=AnsiChar(c); + Inc(pd); + end else if c<=$7FF then begin//$80-$7FF,2B + pd^:=AnsiChar($C0 or (c shr 6)); + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F)); + Inc(pd); + end else if c<=$FFFF then begin //$8000 - $FFFF,3B + pd^:=AnsiChar($E0 or (c shr 12)); + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F)); + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F)); + Inc(pd); + end else if c<=$1FFFFF then begin //$01 0000-$1F FFFF,4B + pd^:=AnsiChar($F0 or (c shr 18));//1111 0xxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 12) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F));//10 xxxxxx + Inc(pd); + end else if c<=$3FFFFFF then begin//$20 0000 - $3FF FFFF,5B + pd^:=AnsiChar($F8 or (c shr 24));//1111 10xx + Inc(pd); + pd^:=AnsiChar($F0 or ((c shr 18) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 12) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F));//10 xxxxxx + Inc(pd); + end else if c<=$7FFFFFFF then begin //$0400 0000-$7FFF FFFF,6B + pd^:=AnsiChar($FC or (c shr 30));//1111 11xx + Inc(pd); + pd^:=AnsiChar($F8 or ((c shr 24) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($F0 or ((c shr 18) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 12) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or ((c shr 6) and $3F));//10 xxxxxx + Inc(pd); + pd^:=AnsiChar($80 or (c and $3F));//10 xxxxxx + Inc(pd); + end; + end; + pd^:={$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF}; + {$IFDEF NEXTGEN} + Result.Length := IntPtr(pd)-IntPtr(pds); + {$ELSE} + SetLength(Result, IntPtr(pd)-IntPtr(pds)); + {$ENDIF} + end; + end; +end; +{$ENDIF} + +{$IFDEF USE_STRENCODEFUNC} {$IFNDEF MSWINDOWS} +function Utf8Decode(const S: AnsiString): StringW; overload; +begin + if S.IsUtf8 then + Result := Utf8Decode(PAnsiChar(S), S.Length) + else + Result := AnsiDecode(S); +end; +{$ENDIF} {$ENDIF} + +{$IFDEF USE_STRENCODEFUNC} +function Utf8Decode(p: PAnsiChar; l: Integer): StringW; +var + ps,pe: PByte; + pd,pds: PWord; + c: Cardinal; +begin + if l<=0 then begin + ps:=PByte(p); + while ps^<>0 do Inc(ps); + l := Integer(ps) - Integer(p); + end; + ps := PByte(p); + pe := ps; + Inc(pe, l); + System.SetLength(Result, l); + pd := PWord(PWideChar(Result)); + pds := pd; + while Integer(ps)0 then begin + if (ps^ and $FC)=$FC then begin //4000000+ + c:=(ps^ and $03) shl 30; + Inc(ps); + c:=c or ((ps^ and $3F) shl 24); + Inc(ps); + c:=c or ((ps^ and $3F) shl 18); + Inc(ps); + c:=c or ((ps^ and $3F) shl 12); + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + c:=c-$10000; + pd^:=$D800+((c shr 10) and $3FF); + Inc(pd); + pd^:=$DC00+(c and $3FF); + Inc(pd); + end else if (ps^ and $F8)=$F8 then begin //200000-3FFFFFF + c:=(ps^ and $07) shl 24; + Inc(ps); + c:=c or ((ps^ and $3F) shl 18); + Inc(ps); + c:=c or ((ps^ and $3F) shl 12); + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + c:=c-$10000; + pd^:=$D800+((c shr 10) and $3FF); + Inc(pd); + pd^:=$DC00+(c and $3FF); + Inc(pd); + end else if (ps^ and $F0)=$F0 then begin //10000-1FFFFF + c:=(ps^ and $0F) shr 18; + Inc(ps); + c:=c or ((ps^ and $3F) shl 12); + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + c:=c-$10000; + pd^:=$D800+((c shr 10) and $3FF); + Inc(pd); + pd^:=$DC00+(c and $3FF); + Inc(pd); + end else if (ps^ and $E0)=$E0 then begin //800-FFFF + c:=(ps^ and $1F) shl 12; + Inc(ps); + c:=c or ((ps^ and $3F) shl 6); + Inc(ps); + c:=c or (ps^ and $3F); + Inc(ps); + pd^:=c; + Inc(pd); + end else if (ps^ and $C0)=$C0 then begin //80-7FF + pd^:=(ps^ and $3F) shl 6; + Inc(ps); + pd^:=pd^ or (ps^ and $3F); + Inc(pd); + Inc(ps); + end else + raise Exception.Create(Format('ЧUTF8ַ:%d',[Integer(ps^)])); + end else begin + pd^ := ps^; + Inc(ps); + Inc(pd); + end; + end; + System.SetLength(Result, (Integer(pd)-Integer(pds)) shr 1); +end; +{$ENDIF} + +{$IFDEF USE_STRENCODEFUNC} +function LoadTextA(AStream: TStream; AEncoding: TTextEncoding): StringA; +var + ASize: Integer; + ABuffer: TBytes; + ABomExists: Boolean; +begin + ASize := AStream.Size - AStream.Position; + if ASize > 0 then begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + if AEncoding in [teUnknown,teAuto] then + AEncoding := DetectTextEncoding(@ABuffer[0], ASize, ABomExists); + if AEncoding=teAnsi then + Result := AnsiString(ABuffer) + else if AEncoding = teUTF8 then begin + if ABomExists then + Result := AnsiEncode(Utf8Decode(@ABuffer[3], ASize-3)) + else + Result := AnsiEncode(Utf8Decode(@ABuffer[0], ASize)); + end + else begin + if AEncoding = teUnicode16BE then + ExchangeByteOrder(@ABuffer[0],ASize); + if ABomExists then + Result := AnsiEncode(PWideChar(@ABuffer[2]), (ASize-2) shr 1) + else + Result := AnsiEncode(PWideChar(@ABuffer[0]), ASize shr 1); + end; + end else + Result := ''; +end; + +function LoadTextU(AStream: TStream; AEncoding: TTextEncoding): StringA; +var + ASize: Integer; + ABuffer: TBytes; + ABomExists: Boolean; + P: PAnsiChar; +begin + ASize := AStream.Size - AStream.Position; + if ASize>0 then begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + if AEncoding in [teUnknown, teAuto] then + AEncoding:=DetectTextEncoding(@ABuffer[0],ASize,ABomExists) + else if ASize>=2 then begin + case AEncoding of + teUnicode16LE: + ABomExists:=(ABuffer[0]=$FF) and (ABuffer[1]=$FE); + teUnicode16BE: + ABomExists:=(ABuffer[1]=$FE) and (ABuffer[1]=$FF); + teUTF8: + begin + if ASize>3 then + ABomExists:=(ABuffer[0]=$EF) and (ABuffer[1]=$BB) and (ABuffer[2]=$BF) + else + ABomExists:=False; + end; + end; + end else + ABomExists:=False; + if AEncoding=teAnsi then + Result := YxdStr.Utf8Encode(AnsiDecode(@ABuffer[0], ASize)) + else if AEncoding = teUTF8 then begin + if ABomExists then begin + Dec(ASize, 3); + {$IFDEF NEXTGEN} + Result.From(@ABuffer[0], 3, ASize); + {$ELSE} + SetLength(Result, ASize); + P := @ABuffer[0]; + Inc(P, 3); + Move(P^, PAnsiChar(@Result[1])^, ASize); + {$ENDIF} + end else + Result := AnsiString(ABuffer); + end else begin + if AEncoding=teUnicode16BE then + ExchangeByteOrder(@ABuffer[0],ASize); + if ABomExists then + Result := Utf8Encode(PWideChar(@ABuffer[2]), (ASize-2) shr 1) + else + Result := Utf8Encode(PWideChar(@ABuffer[0]), ASize shr 1); + end; + end + else + Result := ''; +end; + +function LoadTextW(AStream: TStream; AEncoding: TTextEncoding): StringW; +var + ASize: Integer; + ABuffer: TBytes; + ABomExists: Boolean; +begin + ASize := AStream.Size - AStream.Position; + if ASize>0 then begin + SetLength(ABuffer, ASize); + AStream.ReadBuffer((@ABuffer[0])^, ASize); + ABomExists := False; + // Ƿָ룬ǿƼBOMͷڱָ + if (ABuffer[0]=$FF) and (ABuffer[1]=$FE) then begin + ABomExists := True; + AEncoding := teUnicode16LE; + end else if (ABuffer[1]=$FE) and (ABuffer[1]=$FF) then begin + ABomExists := True; + AEncoding := teUnicode16BE; + end else if (ASize > 3) and (ABuffer[0]=$EF) and (ABuffer[1]=$BB) and (ABuffer[2]=$BF) then begin + ABomExists := True; + AEncoding := teUTF8; + end else if AEncoding in [teUnknown, teAuto] then + AEncoding := DetectTextEncoding(@ABuffer[0], ASize, ABomExists); + + if AEncoding = teAnsi then + Result := AnsiDecode(@ABuffer[0], ASize) + else if AEncoding = teUTF8 then begin + if ABomExists then + Result := Utf8Decode(@ABuffer[3], ASize-3) + else + Result := Utf8Decode(@ABuffer[0], ASize); + end else begin + if AEncoding = teUnicode16BE then + ExchangeByteOrder(@ABuffer[0], ASize); + if ABomExists then begin + Dec(ASize, 2); + SetLength(Result, ASize shr 1); + Move(ABuffer[2], PWideChar(Result)^, ASize); + end else begin + SetLength(Result, ASize shr 1); + Move(ABuffer[0], PWideChar(Result)^, ASize); + end; + end; + end else + Result := ''; +end; +{$ENDIF} + +{$IFDEF NEXTGEN} +{ AnsiString } +procedure AnsiString.From(p: PAnsiChar; AOffset, ALen: Integer); +begin + SetLength(ALen); + Inc(P, AOffset); + Move(P^, PAnsiChar(@FValue[1])^,ALen); +end; + +function AnsiString.GetChars(AIndex: Integer): AnsiChar; +begin + if (AIndex<0) or (AIndex >= Length) then + raise Exception.CreateFmt(SOutOfIndex, [AIndex, 0, Length - 1]); + Result:=FValue[AIndex+1]; +end; + +class operator AnsiString.Implicit(const S: WideString): AnsiString; +begin + Result := AnsiEncode(S); +end; + +class operator AnsiString.Implicit(const S: AnsiString): PAnsiChar; +begin + Result:=PansiChar(@S.FValue[1]); +end; + +function AnsiString.GetIsUtf8: Boolean; +begin + if System.Length(FValue)>0 then + Result:=(FValue[0]=1) + else + Result:=False; +end; + +function AnsiString.GetLength: Integer; +begin + //FValue[0]ͣ0-ANSI,1-UTF8ĩβַ\0 + Result := System.Length(FValue); + if Result>=2 then + Dec(Result,2) + else + Result:=0; +end; + +class operator AnsiString.Implicit(const S: AnsiString): TBytes; +var + L:Integer; +begin + L:=System.Length(S.FValue)-1; + System.SetLength(Result,L); + if L>0 then + Move(S.FValue[1],Result[0],L); +end; + +procedure AnsiString.SetChars(AIndex: Integer; const Value: AnsiChar); +begin + if (AIndex<0) or (AIndex>=Length) then + raise Exception.CreateFmt(SOutOfIndex,[AIndex,0,Length-1]); + FValue[AIndex+1]:=Value; +end; + +procedure AnsiString.SetLength(const Value: Integer); +begin + if Value<0 then begin + if System.Length(FValue)>0 then + System.SetLength(FValue,1) + else begin + System.SetLength(FValue,1); + FValue[0]:=0;//ANSI + end; + end else begin + System.SetLength(FValue,Value+2); + FValue[Value+1]:=0; + end; +end; + +class operator AnsiString.Implicit(const ABytes: TBytes): AnsiString; +var + L:Integer; +begin + L:=System.Length(ABytes); + Result.Length:=L; + if L>0 then + Move(ABytes[0],Result.FValue[1],L); +end; + +class operator AnsiString.Implicit(const S: AnsiString): WideString; +begin + Result := AnsiDecode(S); +end; +{$ENDIF} + +{ TStringCatHelper } + +function TStringCatHelper.Back(ALen: Integer): TStringCatHelper; +begin + Result := Self; + Dec(FDest, ALen); + if FDest < PChar(FValue) then + FDest := PChar(FValue); +end; + +function TStringCatHelper.BackIf(const s: PChar): TStringCatHelper; +var + ps: PChar; +begin + Result := Self; + ps := PChar(FValue); + while FDest > ps do begin + {$IFDEF UNICODE} + if (FDest[-1] >= #$DC00) and (FDest[-1] <= #$DFFF) then begin + if CharIn(FDest-2, s) then + Dec(FDest, 2) + else + Break; + end else if CharIn(FDest-1,s) then + Dec(FDest) + else + Break; + {$ELSE} + if CharIn(FDest-1, s) then + Dec(FDest) + else + Break; + {$ENDIF} + end; +end; + +function TStringCatHelper.Cat(const s: string): TStringCatHelper; +begin + Result := Cat(PChar(s), Length(s)); +end; + +function TStringCatHelper.Cat(c: Char): TStringCatHelper; +begin + if (FDest-FStart)=FSize then + NeedSize(-1); + FDest^ := c; + Inc(FDest); + Result := Self; +end; + +function TStringCatHelper.Cat(p: PChar; + len: Integer): TStringCatHelper; +begin + Result := Self; + if len < 0 then begin + while p^ <> #0 do begin + if FDest-FStart >= FSize then + NeedSize(FSize + FBlockSize); + FDest^ := p^; + Inc(p); + Inc(FDest); + end; + end else begin + NeedSize(-len); + Move(p^, FDest^, len{$IFDEF UNICODE} shl 1{$ENDIF}); + Inc(FDest, len); + end; +end; + +function TStringCatHelper.Cat(const V: Boolean): TStringCatHelper; +begin + Result := Cat(BoolToStr(V)); +end; + +function TStringCatHelper.Cat(const V: Double): TStringCatHelper; +begin + Result := Cat(FloatToStr(V)); +end; + +function TStringCatHelper.Cat(const V: Int64): TStringCatHelper; +begin + Result := Cat(IntToStr(V)); +end; + +function TStringCatHelper.Cat(const V: Variant): TStringCatHelper; +begin + Result := Cat(VarToStr(V)); +end; + +function TStringCatHelper.Cat(const V: TGuid): TStringCatHelper; +begin + Result := Cat(GuidToString(V)); +end; + +function TStringCatHelper.Cat(const V: Currency): TStringCatHelper; +begin + Result := Cat(CurrToStr(V)); +end; + +constructor TStringCatHelper.Create(ASize: Integer); +begin + inherited Create; + FBlockSize := ASize; + NeedSize(FBlockSize); +end; + +destructor TStringCatHelper.Destroy; +begin + SetLength(FValue, 0); + inherited; +end; + +constructor TStringCatHelper.Create; +begin + inherited Create; + FBlockSize := 4096; + NeedSize(FBlockSize); +end; + +function TStringCatHelper.GetChars(AIndex: Integer): Char; +begin + Result := FStart[AIndex]; +end; + +function TStringCatHelper.GetPosition: Integer; +begin + Result := FDest - PChar(FValue); +end; + +function TStringCatHelper.GetValue: string; +var + L: Integer; +begin + L := FDest - PChar(FValue); + SetLength(Result, L); + Move(FStart^, PChar(Result)^, L{$IFDEF UNICODE} shl 1{$ENDIF}); +end; + +procedure TStringCatHelper.NeedSize(ASize: Integer); +var + offset:Integer; +begin + offset := FDest-FStart; + if ASize < 0 then + ASize := offset - ASize; + if ASize > FSize then begin + FSize := ((ASize + FBlockSize) div FBlockSize) * FBlockSize; + SetLength(FValue, FSize); + FStart := PChar(@FValue[0]); + FDest := FStart + offset; + end; +end; + +procedure TStringCatHelper.Reset; +begin + FDest := FStart; +end; + +function TStringCatHelper.Space(count: Integer): TStringCatHelper; +begin +{$IFDEF UNICODE} + Result := Self; + if Count > 0 then begin + while Count>0 do begin + Cat(' '); + Dec(Count); + end; + end; +{$ELSE} + Result := Self; + if Count > 0 then begin + while Count>0 do begin + Cat(' '); + Dec(Count); + end; + end; +{$ENDIF} +end; + +procedure TStringCatHelper.SetPosition(const Value: Integer); +begin + if Value <= 0 then + FDest := PChar(FValue) + else if Value>Length(FValue) then begin + NeedSize(Value); + FDest := PChar(FValue) + Value; + end else + FDest := PChar(FValue) + Value; +end; + +function TStringCatHelper.Cat(const V: TStream): TStringCatHelper; +const + BufSize = 4096; +var + I: Integer; + Buf: array [0..BufSize-1] of Byte; +begin + Result := Self; + if Assigned(V) then begin + I := Length(Buf); + while I = BufSize do begin + I := V.Read(Buf, BufSize); + if I > 0 then + Cat(@Buf[0], I); + end; + end; +end; + +{ TStringArray } + +function TStringArray.Add(const P: PChar; const Len: Integer): Integer; +begin + if (not Assigned(FOnFilter)) or (FOnFilter(Self, P, Len)) then begin + Result := FCount; + if Result = FCapacity then + Grow; + FList[Result].P := P; + FList[Result].Len := Len; + Inc(FCount); + end else + Result := -1; +end; + +procedure TStringArray.CheckIndex(const Index: Integer); +begin + if (Index < 0) or (Index >= FCount) then + raise Exception.Create(Format(SOutOfIndex,[Index, 0, FCount - 1])); +end; + +procedure TStringArray.Clear; +begin + FCount := 0; +end; + +const + Convert: array[0..127] of Integer = + ( + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ); + +function PCharToFloat(const S: PChar; Len: Integer): Double; +var + I, K, V, M: Integer; +begin + Result := 0; + K := 0; + M := 10; + for i := 0 to len - 1 do begin + V := Convert[Ord(s[i])]; + if (s[i] = '.') and (k = 0) then Inc(k); + if (V < 0) then begin + if (k > 1) then begin + Result := 0; + Exit; + end; + end else begin + if k = 0 then + Result := (result * 10) + V + else begin + Result := Result + V / M; + M := M * 10; + end; + end; + end; +end; + +function TStringArray.GetFloat(const Index: Integer): Double; +var + P: PStringArrayItem; +begin + P := @FList[index]; + if P.P = nil then + Result := 0 + else + Result := PCharToFloat(P.P, P.Len); +end; + +function TStringArray.GetItem(const Index: Integer): string; +begin + CheckIndex(Index); + GetString(Index, Result); +end; + +function TStringArray.GetItemValue(const Index: Integer): PStringArrayItem; +begin + Result := @FList[index]; +end; + +procedure TStringArray.Grow; +var + Delta: Integer; +begin + if FCapacity > 64 then Delta := FCapacity div 4 else + if FCapacity > 8 then Delta := 16 else + Delta := 4; + SetCapacity(FCapacity + Delta); +end; + +function TStringArray.GetValue(const Index: Integer): PStringArrayItem; +begin + CheckIndex(Index); + Result := @FList[index]; +end; + +procedure TStringArray.GetString(const Index: Integer; var Data: string); +var + P: PStringArrayItem; +begin + P := @FList[index]; + if P.P = nil then + Data := '' + else + SetString(Data, P.P, P.Len); +end; + +function TStringArray.GetText(const ADelimiter: string): string; +var + S: TStringCatHelper; + I: Integer; +begin + if Length(FList) > 0 then begin + S := TStringCatHelper.Create; + for I := 0 to FCount - 1 do begin + S.Cat(FList[I].P, FList[I].Len); + if I < FCount - 1 then + S.Cat(ADelimiter); + end; + Result := S.Value; + S.Free; + end else + Result := ''; +end; + +procedure TStringArray.SetCapacity(NewCapacity: Integer); +begin + SetLength(FList, NewCapacity); + FCapacity := NewCapacity; +end; + +procedure TStringArray.SetDelimitedData(const Value: Pointer; const Len: Integer); +var + P, P1, PMax: PChar; + C: Char; +begin + if Value = nil then Exit; + FCount := 0; + FData := ''; + P := Value; + C := FDelimiter; + P1 := P; + PMax := P + Len; + while True do begin + if P = PMax then begin + Add(P1, P - P1); + Break; + end else if P^ = C then begin + Add(P1, P - P1); + Inc(P); + P1 := P; + end else + Inc(P); + end; +end; + +procedure TStringArray.SetDelimitedText(const Value: string); +var + P, P1, PMax: PChar; + C: Char; +begin + FCount := 0; + FData := Value; + P := Pointer(Value); + if P = nil then Exit; + C := FDelimiter; + P1 := P; + PMax := P + Length(Value); + while True do begin + if P = PMax then begin + Add(P1, P - P1); + Break; + end else if P^ = C then begin + Add(P1, P - P1); + Inc(P); + P1 := P; + end else + Inc(P); + end; +end; + +procedure TStringArray.SetText(const Value: string); +var + P, P1, PMax: PChar; +begin + FCount := 0; + FData := Value; + P := Pointer(Value); + if P = nil then Exit; + P1 := P; + PMax := P + Length(Value); + while True do begin + if P = PMax then begin + Add(P1, P - P1); + Break; + end else if (P^ = #13) then begin + Add(P1, P - P1); + Inc(P); + if (P^ = #10) then + Inc(P); + P1 := P; + end else if (P^ = #10) then begin + Add(P1, P - P1); + Inc(P); + if (P^ = #13) then + Inc(P); + P1 := P; + end else + Inc(P); + end; +end; + +{ TStringArrayS } + +function TStringArrayS.Add(const S: string): Integer; +begin + Result := FCount; + if Result = FCapacity then + Grow; + FList[Result] := S; + Inc(FCount); +end; + +function TStringArrayS.Add(const P: PChar; const Len: Integer): Integer; +begin + Result := FCount; + if Result = FCapacity then + Grow; + SetString(FList[Result], P, Len); + Inc(FCount); +end; + +procedure TStringArrayS.Clear; +begin + FCount := 0; +end; + +function TStringArrayS.GetItem(const Index: Integer): string; +begin + if (Index < 0) or (Index >= FCount) then + raise Exception.Create(Format(SOutOfIndex,[Index, 0, FCount - 1])); + Result := FList[index]; +end; + +procedure TStringArrayS.Grow; +var + Delta: Integer; +begin + if FCapacity > 64 then Delta := FCapacity div 4 else + if FCapacity > 8 then Delta := 16 else + Delta := 4; + SetCapacity(FCapacity + Delta); +end; + +procedure TStringArrayS.SetCapacity(NewCapacity: Integer); +begin + SetLength(FList, NewCapacity); + FCapacity := NewCapacity; +end; + +procedure TStringArrayS.SetDelimitedText(const Value: string); +var + P, P1: PChar; + C: Char; +begin + FCount := 0; + FData := Value; + C := FDelimiter; + P := PChar(Value); + P1 := P; + while True do begin + if P^ = #0 then begin + Add(P1, P - P1); + Break; + end else if P^ = C then begin + Add(P1, P - P1); + Inc(P); + P1 := P; + end else + Inc(P); + end; +end; + +procedure TStringArrayS.SetItem(const Index: Integer; const Value: string); +begin + FList[Index] := Value; +end; + +function IsHexChar(c: Char): Boolean; inline; +begin + Result:=((c>='0') and (c<='9')) or + ((c>='a') and (c<='f')) or + ((c>='A') and (c<='F')); +end; + +function HexValue(c: Char): Integer; +begin + if (c>='0') and (c<='9') then + Result := Ord(c) - Ord('0') + else if (c>='a') and (c<='f') then + Result := 10+ Ord(c)-Ord('a') + else + Result := 10+ Ord(c)-Ord('A'); +end; + +function HexChar(v: Byte): Char; +begin + if v<10 then + Result := Char(v + Ord('0')) + else + Result := Char(v-10 + Ord('A')); +end; + +function BinToHex(p:Pointer;l:Integer): String; +const + B2HConvert: array[0..15] of Char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); +var + pd: PChar; + pb: PByte; +begin + SetLength(Result, l shl 1); + pd := PChar(Result); + pb := p; + while l>0 do begin + pd^ := B2HConvert[pb^ shr 4]; + Inc(pd); + pd^ := B2HConvert[pb^ and $0F]; + Inc(pd); + Inc(pb); + Dec(l); + end; +end; + +function BinToHex(const ABytes:TBytes): String; +begin + Result:=BinToHex(@ABytes[0], Length(ABytes)); +end; + +procedure HexToBin(p: pointer; l: Integer; var AResult: TBytes); +var + ps: PChar; + pd: PByte; +begin + SetLength(AResult, l shr 1); + ps := p; + pd := @AResult[0]; + while ps - p < l do begin + if IsHexChar(ps[0]) and IsHexChar(ps[1]) then begin + pd^:=(HexValue(ps[0]) shl 4) + HexValue(ps[1]); + Inc(pd); + Inc(ps, 2); + end else begin + SetLength(AResult, 0); + Exit; + end; + end; +end; + +function HexToBin(const S: String): TBytes; +begin + HexToBin(PChar(S), System.Length(S), Result); +end; + +procedure HexToBin(const S: String; var AResult: TBytes); +begin + HexToBin(PChar(S), System.Length(S), AResult); +end; + +function StrPosA(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +begin + ACol := 1; + ARow := 1; + Result := start; + while IntPtr(start) < IntPtr(current) do begin + if start^={$IFDEF NEXTGEN}10{$ELSE}#10{$ENDIF} then begin + Inc(ARow); + ACol := 1; + Inc(start); + Result := start; + end else begin + Inc(start, CharSizeA(start)); + Inc(ACol); + end; + end; +end; + +function StrPosU(start, current: PAnsiChar; var ACol, ARow:Integer): PAnsiChar; +begin + ACol := 1; + ARow := 1; + Result := start; + while IntPtr(start){$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if (PWORD(p)^ = $0D0A) or (PWORD(p)^ = $0A0D) then + i := 2 + else if (p^ = {$IFDEF NEXTGEN}13{$ELSE}#13{$ENDIF}) then + i := 1 + else + i := 0; + if i > 0 then begin + if ps = p then begin + if ASkipEmpty then begin + Inc(p, i); + ps := p; + end else begin + Result := ''; + Exit; + end; + end else begin + {$IFDEF NEXTGEN} + Result.Length := IntPtr(p)-IntPtr(ps); + {$ELSE} + SetLength(Result, p-ps); + {$ENDIF} + Move(ps^, PAnsiChar(Result)^, IntPtr(p)-IntPtr(ps)); + Inc(p, i); + Exit; + end; + end else + Inc(p); + end; + if ps = p then + Result := '' + else begin + {$IFDEF NEXTGEN} + Result.Length := IntPtr(p)-IntPtr(ps); + {$ELSE} + SetLength(Result, p-ps); + {$ENDIF} + Move(ps^, PAnsiChar(Result)^, IntPtr(p)-IntPtr(ps)); + end; +end; + +function DecodeLineW(var p: PWideChar; ASkipEmpty: Boolean): StringW; +var + ps: PWideChar; + i: Integer; +begin + ps := p; + while p^<>#0 do begin + if (PCardinal(p)^ = $000D000A) or (PCardinal(p)^ = $000A000D) then + i := 2 + else if (p^ = #13) then + i := 1 + else + i := 0; + if i > 0 then begin + if ps = p then begin + if ASkipEmpty then begin + Inc(p, i); + ps := p; + end else begin + Result := ''; + Exit; + end; + end else begin + SetLength(Result, p-ps); + Move(ps^, PWideChar(Result)^, p-ps); + Inc(p, i); + Exit; + end; + end else + Inc(p); + end; + if ps = p then + Result := '' + else begin + SetLength(Result, p-ps); + Move(ps^, PWideChar(Result)^, p-ps); + end; +end; + +function IsSpaceA(const c: PAnsiChar; ASpaceSize: PInteger): Boolean; +begin + {$IFDEF NEXTGEN} + if c^ in [9, 10, 13, 32] then begin + {$ELSE} + if c^ in [#9, #10, #13, #32] then begin + {$ENDIF} + Result := True; + if ASpaceSize <> nil then + ASpaceSize^ := 1; + end else if PWORD(c)^ = $A1A1 then begin + Result := True; + if ASpaceSize <> nil then + ASpaceSize^ := 2; + end else + Result:=False; +end; + +function IsSpaceW(const c: PWideChar; ASpaceSize: PInteger): Boolean; +begin + Result := (c^=#9) or (c^=#10) or (c^=#13) or (c^=#32) or (c^=#$3000); + if Result and (ASpaceSize <> nil) then + ASpaceSize^ := 1; +end; + +//ȫǿո$3000UTF-8227,128,128 +function IsSpaceU(const c: PAnsiChar; ASpaceSize: PInteger): Boolean; +begin + {$IFDEF NEXTGEN} + if c^ in [9, 10, 13, 32] then begin + {$ELSE} + if c^ in [#9, #10, #13, #32] then begin + {$ENDIF} + Result := True; + if (ASpaceSize <> nil) then + ASpaceSize^ := 1; + end else if (c^={$IFDEF NEXTGEN}227{$ELSE}#227{$ENDIF}) and (PWORD(IntPtr(c)+1)^ = $8080) then begin + Result := True; + if (ASpaceSize <> nil) then + ASpaceSize^ := 3; + end else + Result:=False; +end; + +function SkipSpaceA(var p: PAnsiChar): Integer; +var + ps: PAnsiChar; + L: Integer; +begin + ps := p; + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if IsSpaceA(p, @L) then + Inc(p, L) + else + Break; + end; + Result:= IntPtr(p) - IntPtr(ps); +end; + +function SkipSpaceU(var p: PAnsiChar): Integer; +var + ps: PAnsiChar; + L: Integer; +begin + ps := p; + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if IsSpaceU(p, @L) then + Inc(p, L) + else + Break; + end; + Result:= IntPtr(p) - IntPtr(ps); +end; + +function SkipSpaceW(var p: PWideChar): Integer; +var + ps: PWideChar; + L:Integer; +begin + ps := p; + while p^<>#0 do begin + if IsSpaceW(p, @L) then + Inc(p, L) + else + Break; + end; + Result := p - ps; +end; + +function SkipLineA(var p: PAnsiChar): Integer; +var + ps: PAnsiChar; +begin + ps := p; + while p^ <> {$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if (PWORD(p)^ = $0D0A) or (PWORD(p)^ = $0A0D) then begin + Inc(p, 2); + Break; + end else if (p^ = {$IFDEF NEXTGEN}13{$ELSE}#13{$ENDIF}) then begin + Inc(p); + Break; + end else + Inc(p); + end; + Result := IntPtr(p) - IntPtr(ps); +end; + +function SkipLineU(var p: PAnsiChar): Integer; +begin + Result := SkipLineA(p); +end; + +function SkipLineW(var p: PWideChar): Integer; +var + ps: PWideChar; +begin + ps := p; + while p^ <> #0 do begin + if (PCardinal(p)^ = $000D000A) or (PCardinal(p)^ = $000A000D) then begin + Inc(p, 2); + Break; + end else if (p^ = #13) then begin + Inc(p); + Break; + end else + Inc(p); + end; + Result := p - ps; +end; + +{$IFNDEF NEXTGEN} +function SkipUntilA(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar): Integer; +var + ps: PAnsiChar; +begin + ps := p; + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if (p^ = AQuoter) then begin + Inc(p); + while p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} do begin + if p^ = {$IFDEF NEXTGEN}$5C{$ELSE}#$5C{$ENDIF} then begin + Inc(p); + if p^<>{$IFDEF NEXTGEN}0{$ELSE}#0{$ENDIF} then + Inc(p); + end else if p^ = AQuoter then begin + Inc(p); + if p^ = AQuoter then + Inc(p) + else + Break; + end else + Inc(p); + end; + end else if CharInA(p, AExpects) then + Break + else + Inc(p, CharSizeA(p)); + end; + Result := IntPtr(p) - IntPtr(ps); +end; +{$ENDIF} +{$IFNDEF NEXTGEN} +function SkipUntilU(var p: PAnsiChar; AExpects: PAnsiChar; AQuoter: AnsiChar): Integer; +var + ps: PAnsiChar; +begin + ps := p; + while p^<>#0 do begin + if (p^ = AQuoter) then begin + Inc(p); + while p^<>#0 do begin + if p^=#$5C then begin + Inc(p); + if p^<>#0 then + Inc(p); + end else if p^=AQuoter then begin + Inc(p); + if p^=AQuoter then + Inc(p) + else + Break; + end else + Inc(p); + end; + end else if CharInU(p, AExpects) then + Break + else + Inc(p, CharSizeU(p)); + end; + Result := p - ps; +end; +{$ENDIF} + +function SkipUntilW(var p: PWideChar; AExpects: PWideChar; AQuoter: WideChar): Integer; +var + ps: PWideChar; +begin + ps := p; + while p^<>#0 do begin + if (p^=AQuoter) then begin + Inc(p); + while p^<>#0 do begin + if p^=#$5C then begin + Inc(p); + if p^<>#0 then + Inc(p); + end else if p^=AQuoter then begin + Inc(p); + if p^=AQuoter then + Inc(p) + else + Break; + end else + Inc(p); + end; + end else if CharInW(p, AExpects) then + Break + else + Inc(p, CharSizeW(p)); + end; + Result := p - ps; +end; + +function StartWith(s, startby: PChar; AIgnoreCase: Boolean): Boolean; +begin + while (s^<>#0) and (startby^<>#0) do begin + if AIgnoreCase then begin + {$IFDEF UNICODE} + if CharUpperW(s^) <> CharUpperW(startby^) then + {$ELSE} + if CharUpperA(s^) <> CharUpperA(startby^) then + {$ENDIF} + Break; + end else if s^ <> startby^ then + Break; + Inc(s); + Inc(startby); + end; + Result := startby^ = #0; +end; + +function StartWithIgnoreCase(s, startby: PChar): Boolean; +begin + while (s^<>#0) and (startby^<>#0) do begin + {$IFDEF UNICODE} + if CharUpperW(s^) <> CharUpperW(startby^) then + {$ELSE} + if CharUpperA(s^) <> CharUpperA(startby^) then + {$ENDIF} + Break; + Inc(s); + Inc(startby); + end; + Result := startby^ = #0; +end; + +procedure SaveTextA(AStream: TStream; const S: AnsiString); +begin + AStream.WriteBuffer(PAnsiChar(S)^, Length(S)) +end; + +procedure SaveTextU(AStream: TStream; const S: AnsiString; AWriteBom: Boolean); + + procedure WriteBom; + var + ABom:TBytes; + begin + SetLength(ABom,3); + ABom[0]:=$EF; + ABom[1]:=$BB; + ABom[2]:=$BF; + AStream.WriteBuffer(ABom[0],3); + end; + + procedure SaveAnsi; + var + T: AnsiString; + begin + T := YxdStr.Utf8Encode({$IFDEF NEXTGEN}AnsiDecode(S){$ELSE}string(S){$ENDIF}); + AStream.WriteBuffer(PAnsiChar(T)^, Length(T)); + end; + +begin + if AWriteBom then + WriteBom; + SaveAnsi; +end; + +procedure SaveTextW(AStream: TStream; const S: StringW; AWriteBom: Boolean); + procedure WriteBom; + var + bom: Word; + begin + bom := $FEFF; + AStream.WriteBuffer(bom, 2); + end; +begin + if AWriteBom then + WriteBom; + AStream.WriteBuffer(PWideChar(S)^, System.Length(S) shl 1); +end; + +procedure SaveTextWBE(AStream: TStream; const S: StringW; AWriteBom: Boolean); +var + pw, pe: PWord; + w: Word; + ABuilder: TStringCatHelper; +begin + pw := PWord(PWideChar(S)); + pe := pw; + Inc(pe, Length(S)); + ABuilder := TStringCatHelper.Create(IntPtr(pe)-IntPtr(pw)); + try + while IntPtr(pw) 0 then begin + VCStrStr := TMSVCStrStr(GetProcAddress(hMsvcrtl, 'strstr')); + VCStrStrW := TMSVCStrStrW(GetProcAddress(hMsvcrtl, 'wcsstr')); + VCMemCmp := TMSVCMemCmp(GetProcAddress(hMsvcrtl, 'memcmp')); + end else begin + VCStrStr := nil; + VCStrStrW := nil; + VCMemCmp := nil; + end; + {$ENDIF} + {$IFDEF MSWINDOWS} + SysACP := GetACP(); + {$ELSE} + SysACP := TEncoding.ANSI.CodePage + {$ENDIF} + +finalization + {$IFDEF MSWINDOWS} + if hMsvcrtl <> 0 then + FreeLibrary(hMsvcrtl); + {$ENDIF} + +end. + \ No newline at end of file diff --git a/source/pcre.pas b/source/pcre.pas new file mode 100644 index 0000000..50ee33e --- /dev/null +++ b/source/pcre.pas @@ -0,0 +1,1145 @@ +{**************************************************************************************************} +{ } +{ Project JEDI Code Library (JCL) } +{ } +{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } +{ you may not use this file except in compliance with the License. You may obtain a copy of the } +{ License at http://www.mozilla.org/MPL/ } +{ } +{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } +{ ANY KIND, either express or implied. See the License for the specific language governing rights } +{ and limitations under the License. } +{ } +{ The Original Code is pcre.pas. } +{ } +{ The Initial Developer of the Original Code is Peter Thornqvist. } +{ Portions created by Peter Thornqvist are Copyright (C) of Peter Thornqvist. All rights reserved. } +{ Portions created by University of Cambridge are } +{ Copyright (C) 1997-2001 by University of Cambridge. } +{ } +{ Contributor(s): } +{ Robert Rossmair (rrossmair) } +{ Mario R. Carro } +{ Florent Ouchet (outchy) } +{ } +{ The latest release of PCRE is always available from } +{ ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-xxx.tar.gz } +{ } +{ Modified by Jan Goyvaerts for use with TPerlRegEx } +{ TPerlRegEx is available at http://www.regular-expressions.info/delphi.html } +{ } +{**************************************************************************************************} +{ } +{ Header conversion of pcre.h } +{ } +{**************************************************************************************************} + +unit pcre; + +interface + +(************************************************* +* Perl-Compatible Regular Expressions * +*************************************************) + +{$WEAKPACKAGEUNIT ON} + +// Define PCRE_STATICLINK to link the OBJ files with PCRE 7.9. +{$DEFINE PCRE_STATICLINK} + +// Define PCRE_LINKDLL to use pcrelib.dll +{.$DEFINE PCRE_LINKDLL} + +// The supplied pcrelib.dll compiled PCRE 7.9 using the C calling convention +{$IFDEF PCRE_LINKDLL} + {$DEFINE PCRE_EXPORT_CDECL} +{$ENDIF} + +(*$HPPEMIT '#include "pcre.h"'*) + +const + MAX_PATTERN_LENGTH = $10003; + {$EXTERNALSYM MAX_PATTERN_LENGTH} + MAX_QUANTIFY_REPEAT = $10000; + {$EXTERNALSYM MAX_QUANTIFY_REPEAT} + MAX_CAPTURE_COUNT = $FFFF; + {$EXTERNALSYM MAX_CAPTURE_COUNT} + MAX_NESTING_DEPTH = 200; + {$EXTERNALSYM MAX_NESTING_DEPTH} + +const + (* Options *) + PCRE_CASELESS = $00000001; + {$EXTERNALSYM PCRE_CASELESS} + PCRE_MULTILINE = $00000002; + {$EXTERNALSYM PCRE_MULTILINE} + PCRE_DOTALL = $00000004; + {$EXTERNALSYM PCRE_DOTALL} + PCRE_EXTENDED = $00000008; + {$EXTERNALSYM PCRE_EXTENDED} + PCRE_ANCHORED = $00000010; + {$EXTERNALSYM PCRE_ANCHORED} + PCRE_DOLLAR_ENDONLY = $00000020; + {$EXTERNALSYM PCRE_DOLLAR_ENDONLY} + PCRE_EXTRA = $00000040; + {$EXTERNALSYM PCRE_EXTRA} + PCRE_NOTBOL = $00000080; + {$EXTERNALSYM PCRE_NOTBOL} + PCRE_NOTEOL = $00000100; + {$EXTERNALSYM PCRE_NOTEOL} + PCRE_UNGREEDY = $00000200; + {$EXTERNALSYM PCRE_UNGREEDY} + PCRE_NOTEMPTY = $00000400; + {$EXTERNALSYM PCRE_NOTEMPTY} + PCRE_UTF8 = $00000800; + {$EXTERNALSYM PCRE_UTF8} + PCRE_NO_AUTO_CAPTURE = $00001000; + {$EXTERNALSYM PCRE_NO_AUTO_CAPTURE} + PCRE_NO_UTF8_CHECK = $00002000; + {$EXTERNALSYM PCRE_NO_UTF8_CHECK} + PCRE_AUTO_CALLOUT = $00004000; + {$EXTERNALSYM PCRE_AUTO_CALLOUT} + PCRE_PARTIAL = $00008000; + {$EXTERNALSYM PCRE_PARTIAL} + PCRE_DFA_SHORTEST = $00010000; + {$EXTERNALSYM PCRE_DFA_SHORTEST} + PCRE_DFA_RESTART = $00020000; + {$EXTERNALSYM PCRE_DFA_RESTART} + PCRE_FIRSTLINE = $00040000; + {$EXTERNALSYM PCRE_FIRSTLINE} + PCRE_DUPNAMES = $00080000; + {$EXTERNALSYM PCRE_DUPNAMES} + PCRE_NEWLINE_CR = $00100000; + {$EXTERNALSYM PCRE_NEWLINE_CR} + PCRE_NEWLINE_LF = $00200000; + {$EXTERNALSYM PCRE_NEWLINE_LF} + PCRE_NEWLINE_CRLF = $00300000; + {$EXTERNALSYM PCRE_NEWLINE_CRLF} + PCRE_NEWLINE_ANY = $00400000; + {$EXTERNALSYM PCRE_NEWLINE_ANY} + PCRE_NEWLINE_ANYCRLF = $00500000; + {$EXTERNALSYM PCRE_NEWLINE_ANYCRLF} + PCRE_BSR_ANYCRLF = $00800000; + {$EXTERNALSYM PCRE_BSR_ANYCRLF} + PCRE_BSR_UNICODE = $01000000; + {$EXTERNALSYM PCRE_BSR_UNICODE} + PCRE_JAVASCRIPT_COMPAT = $02000000; + {$EXTERNALSYM PCRE_JAVASCRIPT_COMPAT} + PCRE_NO_START_OPTIMIZE = $04000000; + {$EXTERNALSYM PCRE_NO_START_OPTIMIZE} + PCRE_NO_START_OPTIMISE = $04000000; + {$EXTERNALSYM PCRE_NO_START_OPTIMISE} + + (* Exec-time and get-time error codes *) + + PCRE_ERROR_NOMATCH = -1; + {$EXTERNALSYM PCRE_ERROR_NOMATCH} + PCRE_ERROR_NULL = -2; + {$EXTERNALSYM PCRE_ERROR_NULL} + PCRE_ERROR_BADOPTION = -3; + {$EXTERNALSYM PCRE_ERROR_BADOPTION} + PCRE_ERROR_BADMAGIC = -4; + {$EXTERNALSYM PCRE_ERROR_BADMAGIC} + PCRE_ERROR_UNKNOWN_NODE = -5; + {$EXTERNALSYM PCRE_ERROR_UNKNOWN_NODE} + PCRE_ERROR_NOMEMORY = -6; + {$EXTERNALSYM PCRE_ERROR_NOMEMORY} + PCRE_ERROR_NOSUBSTRING = -7; + {$EXTERNALSYM PCRE_ERROR_NOSUBSTRING} + PCRE_ERROR_MATCHLIMIT = -8; + {$EXTERNALSYM PCRE_ERROR_MATCHLIMIT} + PCRE_ERROR_CALLOUT = -9; (* Never used by PCRE itself *) + {$EXTERNALSYM PCRE_ERROR_CALLOUT} + PCRE_ERROR_BADUTF8 = -10; + {$EXTERNALSYM PCRE_ERROR_BADUTF8} + PCRE_ERROR_BADUTF8_OFFSET = -11; + {$EXTERNALSYM PCRE_ERROR_BADUTF8_OFFSET} + PCRE_ERROR_PARTIAL = -12; + {$EXTERNALSYM PCRE_ERROR_PARTIAL} + PCRE_ERROR_BADPARTIAL = -13; + {$EXTERNALSYM PCRE_ERROR_BADPARTIAL} + PCRE_ERROR_INTERNAL = -14; + {$EXTERNALSYM PCRE_ERROR_INTERNAL} + PCRE_ERROR_BADCOUNT = -15; + {$EXTERNALSYM PCRE_ERROR_BADCOUNT} + PCRE_ERROR_DFA_UITEM = -16; + {$EXTERNALSYM PCRE_ERROR_DFA_UITEM} + PCRE_ERROR_DFA_UCOND = -17; + {$EXTERNALSYM PCRE_ERROR_DFA_UCOND} + PCRE_ERROR_DFA_UMLIMIT = -18; + {$EXTERNALSYM PCRE_ERROR_DFA_UMLIMIT} + PCRE_ERROR_DFA_WSSIZE = -19; + {$EXTERNALSYM PCRE_ERROR_DFA_WSSIZE} + PCRE_ERROR_DFA_RECURSE = -20; + {$EXTERNALSYM PCRE_ERROR_DFA_RECURSE} + PCRE_ERROR_RECURSIONLIMIT = -21; + {$EXTERNALSYM PCRE_ERROR_RECURSIONLIMIT} + PCRE_ERROR_NULLWSLIMIT = -22; (* No longer actually used *) + {$EXTERNALSYM PCRE_ERROR_NULLWSLIMIT} + PCRE_ERROR_BADNEWLINE = -23; + {$EXTERNALSYM PCRE_ERROR_BADNEWLINE} + + (* Request types for pcre_fullinfo() *) + + PCRE_INFO_OPTIONS = 0; + {$EXTERNALSYM PCRE_INFO_OPTIONS} + PCRE_INFO_SIZE = 1; + {$EXTERNALSYM PCRE_INFO_SIZE} + PCRE_INFO_CAPTURECOUNT = 2; + {$EXTERNALSYM PCRE_INFO_CAPTURECOUNT} + PCRE_INFO_BACKREFMAX = 3; + {$EXTERNALSYM PCRE_INFO_BACKREFMAX} + PCRE_INFO_FIRSTCHAR = 4; + {$EXTERNALSYM PCRE_INFO_FIRSTCHAR} + PCRE_INFO_FIRSTTABLE = 5; + {$EXTERNALSYM PCRE_INFO_FIRSTTABLE} + PCRE_INFO_LASTLITERAL = 6; + {$EXTERNALSYM PCRE_INFO_LASTLITERAL} + PCRE_INFO_NAMEENTRYSIZE = 7; + {$EXTERNALSYM PCRE_INFO_NAMEENTRYSIZE} + PCRE_INFO_NAMECOUNT = 8; + {$EXTERNALSYM PCRE_INFO_NAMECOUNT} + PCRE_INFO_NAMETABLE = 9; + {$EXTERNALSYM PCRE_INFO_NAMETABLE} + PCRE_INFO_STUDYSIZE = 10; + {$EXTERNALSYM PCRE_INFO_STUDYSIZE} + PCRE_INFO_DEFAULT_TABLES = 11; + {$EXTERNALSYM PCRE_INFO_DEFAULT_TABLES} + PCRE_INFO_OKPARTIAL = 12; + {$EXTERNALSYM PCRE_INFO_OKPARTIAL} + PCRE_INFO_JCHANGED = 13; + {$EXTERNALSYM PCRE_INFO_JCHANGED} + PCRE_INFO_HASCRORLF = 14; + {$EXTERNALSYM PCRE_INFO_HASCRORLF} + + (* Request types for pcre_config() *) + PCRE_CONFIG_UTF8 = 0; + {$EXTERNALSYM PCRE_CONFIG_UTF8} + PCRE_CONFIG_NEWLINE = 1; + {$EXTERNALSYM PCRE_CONFIG_NEWLINE} + PCRE_CONFIG_LINK_SIZE = 2; + {$EXTERNALSYM PCRE_CONFIG_LINK_SIZE} + PCRE_CONFIG_POSIX_MALLOC_THRESHOLD = 3; + {$EXTERNALSYM PCRE_CONFIG_POSIX_MALLOC_THRESHOLD} + PCRE_CONFIG_MATCH_LIMIT = 4; + {$EXTERNALSYM PCRE_CONFIG_MATCH_LIMIT} + PCRE_CONFIG_STACKRECURSE = 5; + {$EXTERNALSYM PCRE_CONFIG_STACKRECURSE} + PCRE_CONFIG_UNICODE_PROPERTIES = 6; + {$EXTERNALSYM PCRE_CONFIG_UNICODE_PROPERTIES} + PCRE_CONFIG_MATCH_LIMIT_RECURSION = 7; + {$EXTERNALSYM PCRE_CONFIG_MATCH_LIMIT_RECURSION} + PCRE_CONFIG_BSR = 8; + {$EXTERNALSYM PCRE_CONFIG_BSR} + + (* Bit flags for the pcre_extra structure *) + + PCRE_EXTRA_STUDY_DATA = $0001; + {$EXTERNALSYM PCRE_EXTRA_STUDY_DATA} + PCRE_EXTRA_MATCH_LIMIT = $0002; + {$EXTERNALSYM PCRE_EXTRA_MATCH_LIMIT} + PCRE_EXTRA_CALLOUT_DATA = $0004; + {$EXTERNALSYM PCRE_EXTRA_CALLOUT_DATA} + PCRE_EXTRA_TABLES = $0008; + {$EXTERNALSYM PCRE_EXTRA_TABLES} + PCRE_EXTRA_MATCH_LIMIT_RECURSION = $0010; + {$EXTERNALSYM PCRE_EXTRA_MATCH_LIMIT_RECURSION} + +type + (* Types *) + PPAnsiChar = ^PAnsiChar; + {$EXTERNALSYM PPAnsiChar} + PPPAnsiChar = ^PPAnsiChar; + {$EXTERNALSYM PPPAnsiChar} + PInteger = ^Integer; + {$EXTERNALSYM PInteger} + + real_pcre = packed record + {magic_number: Longword; + size: Integer; + tables: PAnsiChar; + options: Longword; + top_bracket: Word; + top_backref: word; + first_char: PAnsiChar; + req_char: PAnsiChar; + code: array [0..0] of AnsiChar;} + end; + TPCRE = real_pcre; + PPCRE = ^TPCRE; + + real_pcre_extra = packed record + {options: PAnsiChar; + start_bits: array [0..31] of AnsiChar;} + flags: Cardinal; (* Bits for which fields are set *) + study_data: Pointer; (* Opaque data from pcre_study() *) + match_limit: Cardinal; (* Maximum number of calls to match() *) + callout_data: Pointer; (* Data passed back in callouts *) + tables: PAnsiChar; (* Pointer to character tables *) + match_limit_recursion: Cardinal; (* Max recursive calls to match() *) + end; + TPCREExtra = real_pcre_extra; + PPCREExtra = ^TPCREExtra; + + pcre_callout_block = packed record + version: Integer; (* Identifies version of block *) + (* ------------------------ Version 0 ------------------------------- *) + callout_number: Integer; (* Number compiled into pattern *) + offset_vector: PInteger; (* The offset vector *) + subject: PAnsiChar; (* The subject being matched *) + subject_length: Integer; (* The length of the subject *) + start_match: Integer; (* Offset to start of this match attempt *) + current_position: Integer; (* Where we currently are in the subject *) + capture_top: Integer; (* Max current capture *) + capture_last: Integer; (* Most recently closed capture *) + callout_data: Pointer; (* Data passed in with the call *) + (* ------------------- Added for Version 1 -------------------------- *) + pattern_position: Integer; (* Offset to next item in the pattern *) + next_item_length: Integer; (* Length of next item in the pattern *) + (* ------------------------------------------------------------------ *) + end; + + pcre_malloc_callback = function(Size: Integer): Pointer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_malloc_callback} + pcre_free_callback = procedure(P: Pointer); {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_free_callback} + pcre_stack_malloc_callback = function(Size: Integer): Pointer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_stack_malloc_callback} + pcre_stack_free_callback = procedure(P: Pointer); {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_stack_free_callback} + pcre_callout_callback = function(var callout_block: pcre_callout_block): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_callout_callback} + +var + // renamed from "pcre_X" to "pcre_X_func" to allow functions with name "pcre_X" to be + // declared in implementation when static linked + pcre_malloc_func: ^pcre_malloc_callback = nil; + {$EXTERNALSYM pcre_malloc_func} + pcre_free_func: ^pcre_free_callback = nil; + {$EXTERNALSYM pcre_free_func} + pcre_stack_malloc_func: ^pcre_stack_malloc_callback = nil; + {$EXTERNALSYM pcre_stack_malloc_func} + pcre_stack_free_func: ^pcre_stack_free_callback = nil; + {$EXTERNALSYM pcre_stack_free_func} + pcre_callout_func: ^pcre_callout_callback = nil; + {$EXTERNALSYM pcre_callout_func} + +procedure SetPCREMallocCallback(const Value: pcre_malloc_callback); +{$EXTERNALSYM SetPCREMallocCallback} +function GetPCREMallocCallback: pcre_malloc_callback; +{$EXTERNALSYM GetPCREMallocCallback} +function CallPCREMalloc(Size: Integer): Pointer; +{$EXTERNALSYM CallPCREMalloc} + +procedure SetPCREFreeCallback(const Value: pcre_free_callback); +{$EXTERNALSYM SetPCREFreeCallback} +function GetPCREFreeCallback: pcre_free_callback; +{$EXTERNALSYM GetPCREFreeCallback} +procedure CallPCREFree(P: Pointer); +{$EXTERNALSYM CallPCREFree} + +procedure SetPCREStackMallocCallback(const Value: pcre_stack_malloc_callback); +{$EXTERNALSYM SetPCREStackMallocCallback} +function GetPCREStackMallocCallback: pcre_stack_malloc_callback; +{$EXTERNALSYM GetPCREStackMallocCallback} +function CallPCREStackMalloc(Size: Integer): Pointer; +{$EXTERNALSYM CallPCREStackMalloc} + +procedure SetPCREStackFreeCallback(const Value: pcre_stack_free_callback); +{$EXTERNALSYM SetPCREStackFreeCallback} +function GetPCREStackFreeCallback: pcre_stack_free_callback; +{$EXTERNALSYM GetPCREStackFreeCallback} +procedure CallPCREStackFree(P: Pointer); +{$EXTERNALSYM CallPCREStackFree} + +procedure SetPCRECalloutCallback(const Value: pcre_callout_callback); +{$EXTERNALSYM SetPCRECalloutCallback} +function GetPCRECalloutCallback: pcre_callout_callback; +{$EXTERNALSYM GetPCRECalloutCallback} +function CallPCRECallout(var callout_block: pcre_callout_block): Integer; +{$EXTERNALSYM CallPCRECallout} + +type + TPCRELibNotLoadedHandler = procedure; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + +var + // Value to initialize function pointers below with, in case LoadPCRE fails + // or UnloadPCRE is called. Typically the handler will raise an exception. + LibNotLoadedHandler: TPCRELibNotLoadedHandler = nil; + +(* Functions *) + +{$IFNDEF PCRE_LINKONREQUEST} +// static link and static dll import +function pcre_compile(const pattern: PAnsiChar; options: Integer; + const errptr: PPAnsiChar; erroffset: PInteger; const tableptr: PAnsiChar): PPCRE; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_compile} +function pcre_compile2(const pattern: PAnsiChar; options: Integer; + const errorcodeptr: PInteger; const errorptr: PPAnsiChar; erroroffset: PInteger; + const tables: PAnsiChar): PPCRE; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_compile2} +function pcre_config(what: Integer; where: Pointer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_config} +function pcre_copy_named_substring(const code: PPCRE; const subject: PAnsiChar; + ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar; + buffer: PAnsiChar; size: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_copy_named_substring} +function pcre_copy_substring(const subject: PAnsiChar; ovector: PInteger; + stringcount, stringnumber: Integer; buffer: PAnsiChar; buffersize: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_copy_substring} +function pcre_dfa_exec(const argument_re: PPCRE; const extra_data: PPCREExtra; + const subject: PAnsiChar; length: Integer; start_offset: Integer; + options: Integer; offsets: PInteger; offsetcount: Integer; workspace: PInteger; + wscount: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_dfa_exec} +function pcre_exec(const code: PPCRE; const extra: PPCREExtra; const subject: PAnsiChar; + length, startoffset, options: Integer; ovector: PInteger; ovecsize: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_exec} +procedure pcre_free_substring(stringptr: PAnsiChar); + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_free_substring} +procedure pcre_free_substring_list(stringlistptr: PPAnsiChar); + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_free_substring_list} +function pcre_fullinfo(const code: PPCRE; const extra: PPCREExtra; + what: Integer; where: Pointer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_fullinfo} +function pcre_get_named_substring(const code: PPCRE; const subject: PAnsiChar; + ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar; + const stringptr: PPAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_get_named_substring} +function pcre_get_stringnumber(const code: PPCRE; const stringname: PAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_get_stringnumber} +function pcre_get_stringtable_entries(const code: PPCRE; const stringname: PAnsiChar; + firstptr: PPAnsiChar; lastptr: PPAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_get_stringtable_entries} +function pcre_get_substring(const subject: PAnsiChar; ovector: PInteger; + stringcount, stringnumber: Integer; const stringptr: PPAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_get_substring} +function pcre_get_substring_list(const subject: PAnsiChar; ovector: PInteger; + stringcount: Integer; listptr: PPPAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_get_substring_list} +function pcre_info(const code: PPCRE; optptr, firstcharptr: PInteger): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_info} +function pcre_maketables: PAnsiChar; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_maketables} +function pcre_refcount(argument_re: PPCRE; adjust: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_refcount} +function pcre_study(const code: PPCRE; options: Integer; const errptr: PPAnsiChar): PPCREExtra; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_study} +function pcre_version: PAnsiChar; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} +{$EXTERNALSYM pcre_version} + +// Calling pcre_free in the DLL causes an access violation error; use pcre_dispose instead +procedure pcre_dispose(pattern, hints, chartable: Pointer); {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + +{$ELSE} +// dynamic dll import +type + pcre_compile_func = function(const pattern: PAnsiChar; options: Integer; + const errptr: PPAnsiChar; erroffset: PInteger; const tableptr: PAnsiChar): PPCRE; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_compile_func} + pcre_compile2_func = function(const pattern: PAnsiChar; options: Integer; + const errorcodeptr: PInteger; const errorptr: PPAnsiChar; erroroffset: PInteger; + const tables: PAnsiChar): PPCRE; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_compile2_func} + pcre_config_func = function(what: Integer; where: Pointer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_config_func} + pcre_copy_named_substring_func = function(const code: PPCRE; const subject: PAnsiChar; + ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar; + buffer: PAnsiChar; size: Integer): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_copy_named_substring_func} + pcre_copy_substring_func = function(const subject: PAnsiChar; ovector: PInteger; + stringcount, stringnumber: Integer; buffer: PAnsiChar; buffersize: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_copy_substring_func} + pcre_dfa_exec_func = function(const argument_re: PPCRE; const extra_data: PPCREExtra; + const subject: PAnsiChar; length: Integer; start_offset: Integer; + options: Integer; offsets: PInteger; offsetcount: Integer; workspace: PInteger; + wscount: Integer): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_dfa_exec_func} + pcre_exec_func = function(const code: PPCRE; const extra: PPCREExtra; const subject: PAnsiChar; + length, startoffset, options: Integer; ovector: PInteger; ovecsize: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_exec_func} + pcre_free_substring_func = procedure(stringptr: PAnsiChar); + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_free_substring_func} + pcre_free_substring_list_func = procedure(stringptr: PPAnsiChar); + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_free_substring_list_func} + pcre_fullinfo_func = function(const code: PPCRE; const extra: PPCREExtra; + what: Integer; where: Pointer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_fullinfo_func} + pcre_get_named_substring_func = function(const code: PPCRE; const subject: PAnsiChar; + ovector: PInteger; stringcount: Integer; const stringname: PAnsiChar; + const stringptr: PPAnsiChar): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_get_named_substring_func} + pcre_get_stringnumber_func = function(const code: PPCRE; + const stringname: PAnsiChar): Integer; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_get_stringnumber_func} + pcre_get_stringtable_entries_func = function(const code: PPCRE; const stringname: PAnsiChar; + firstptr: PPAnsiChar; lastptr: PPAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_get_stringtable_entries_func} + pcre_get_substring_func = function(const subject: PAnsiChar; ovector: PInteger; + stringcount, stringnumber: Integer; const stringptr: PPAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_get_substring_func} + pcre_get_substring_list_func = function(const subject: PAnsiChar; ovector: PInteger; + stringcount: Integer; listptr: PPPAnsiChar): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_get_substring_list_func} + pcre_info_func = function(const code: PPCRE; optptr, firstcharptr: PInteger): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_info_func} + pcre_maketables_func = function: PAnsiChar; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_maketables_func} + pcre_refcount_func = function(argument_re: PPCRE; adjust: Integer): Integer; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_refcount_func} + pcre_study_func = function(const code: PPCRE; options: Integer; const errptr: PPAnsiChar): PPCREExtra; + {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_study_func} + pcre_version_func = function: PAnsiChar; {$IFDEF PCRE_EXPORT_CDECL} cdecl; {$ENDIF PCRE_EXPORT_CDECL} + {$EXTERNALSYM pcre_version_func} + +var + pcre_compile: pcre_compile_func = nil; + {$EXTERNALSYM pcre_compile} + pcre_compile2: pcre_compile2_func = nil; + {$EXTERNALSYM pcre_compile2} + pcre_config: pcre_config_func = nil; + {$EXTERNALSYM pcre_config} + pcre_copy_named_substring: pcre_copy_named_substring_func = nil; + {$EXTERNALSYM pcre_copy_named_substring} + pcre_copy_substring: pcre_copy_substring_func = nil; + {$EXTERNALSYM pcre_copy_substring} + pcre_dfa_exec: pcre_dfa_exec_func = nil; + {$EXTERNALSYM pcre_dfa_exec} + pcre_exec: pcre_exec_func = nil; + {$EXTERNALSYM pcre_exec} + pcre_free_substring: pcre_free_substring_func = nil; + {$EXTERNALSYM pcre_free_substring} + pcre_free_substring_list: pcre_free_substring_list_func = nil; + {$EXTERNALSYM pcre_free_substring_list} + pcre_fullinfo: pcre_fullinfo_func = nil; + {$EXTERNALSYM pcre_fullinfo} + pcre_get_named_substring: pcre_get_named_substring_func = nil; + {$EXTERNALSYM pcre_get_named_substring} + pcre_get_stringnumber: pcre_get_stringnumber_func = nil; + {$EXTERNALSYM pcre_get_stringnumber} + pcre_get_stringtable_entries: pcre_get_stringtable_entries_func = nil; + {$EXTERNALSYM pcre_get_stringtable_entries} + pcre_get_substring: pcre_get_substring_func = nil; + {$EXTERNALSYM pcre_get_substring} + pcre_get_substring_list: pcre_get_substring_list_func = nil; + {$EXTERNALSYM pcre_get_substring_list} + pcre_info: pcre_info_func = nil; + {$EXTERNALSYM pcre_info} + pcre_maketables: pcre_maketables_func = nil; + {$EXTERNALSYM pcre_maketables} + pcre_refcount: pcre_refcount_func = nil; + {$EXTERNALSYM pcre_refcount} + pcre_study: pcre_study_func = nil; + {$EXTERNALSYM pcre_study} + pcre_version: pcre_version_func = nil; + {$EXTERNALSYM pcre_version} + +{$ENDIF ~PCRE_LINKONREQUEST} + +function IsPCRELoaded: Boolean; +function LoadPCRE: Boolean; +procedure UnloadPCRE; + +implementation + +uses + SysUtils, + {$IFDEF MSWINDOWS} + Windows; + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + {$IFDEF HAS_UNIT_TYPES} + Types, + {$ENDIF HAS_UNIT_TYPES} + {$IFDEF HAS_UNIT_LIBC} + Libc; + {$ELSE ~HAS_UNIT_LIBC} + dl; + {$ENDIF ~HAS_UNIT_LIBC} + {$ENDIF UNIX} + +{$IFDEF PCRE_STATICLINK} +{$LINK pcre\pcre_compile.obj} +{$LINK pcre\pcre_config.obj} +{$LINK pcre\pcre_dfa_exec.obj} +{$LINK pcre\pcre_exec.obj} +{$LINK pcre\pcre_fullinfo.obj} +{$LINK pcre\pcre_get.obj} +{$LINK pcre\pcre_globals.obj} +{$LINK pcre\pcre_info.obj} +{$LINK pcre\pcre_maketables.obj} +{$LINK pcre\pcre_newline.obj} +{$LINK pcre\pcre_ord2utf8.obj} +{$LINK pcre\pcre_refcount.obj} +{$LINK pcre\pcre_study.obj} +{$LINK pcre\pcre_tables.obj} +{$LINK pcre\pcre_try_flipped.obj} +{$LINK pcre\pcre_ucd.obj} +{$LINK pcre\pcre_valid_utf8.obj} +{$LINK pcre\pcre_version.obj} +{$LINK pcre\pcre_xclass.obj} +{$LINK pcre\pcre_default_tables.obj} + +// user's defined callbacks +var + pcre_malloc_user: pcre_malloc_callback; + pcre_free_user: pcre_free_callback; + pcre_stack_malloc_user: pcre_stack_malloc_callback; + pcre_stack_free_user: pcre_stack_free_callback; + pcre_callout_user: pcre_callout_callback; + +function pcre_compile; external; +function pcre_compile2; external; +function pcre_config; external; +function pcre_copy_named_substring; external; +function pcre_copy_substring; external; +function pcre_dfa_exec; external; +function pcre_exec; external; +procedure pcre_free_substring; external; +procedure pcre_free_substring_list; external; +function pcre_fullinfo; external; +function pcre_get_named_substring; external; +function pcre_get_stringnumber; external; +function pcre_get_stringtable_entries; external; +function pcre_get_substring; external; +function pcre_get_substring_list; external; +function pcre_info; external; +function pcre_maketables; external; +function pcre_refcount; external; +function pcre_study; external; +function pcre_version; external; + +type + size_t = Longint; + +const + szMSVCRT = 'MSVCRT.DLL'; + +function _memcpy(dest, src: Pointer; count: size_t): Pointer; cdecl; external szMSVCRT name 'memcpy'; +function _memmove(dest, src: Pointer; count: size_t): Pointer; cdecl; external szMSVCRT name 'memmove'; +function _memset(dest: Pointer; val: Integer; count: size_t): Pointer; cdecl; external szMSVCRT name 'memset'; +function _strncmp(s1: PAnsiChar; s2: PAnsiChar; n: size_t): Integer; cdecl; external szMSVCRT name 'strncmp'; +function _memcmp(s1: Pointer; s2: Pointer; n: size_t): Integer; cdecl; external szMSVCRT name 'memcmp'; +function _strlen(s: PAnsiChar): size_t; cdecl; external szMSVCRT name 'strlen'; +function __ltolower(__ch: Integer): Integer; cdecl; external szMSVCRT name 'tolower'; +function __ltoupper(__ch: Integer): Integer; cdecl; external szMSVCRT name 'toupper'; +function _isalnum(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isalnum'; +function _isalpha(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isalpha'; +function _iscntrl(__ch: Integer): Integer; cdecl; external szMSVCRT name 'iscntrl'; +function _isdigit(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isdigit'; +function _isgraph(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isgraph'; +function _islower(__ch: Integer): Integer; cdecl; external szMSVCRT name 'islower'; +function _isprint(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isprint'; +function _ispunct(__ch: Integer): Integer; cdecl; external szMSVCRT name 'ispunct'; +function _isspace(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isspace'; +function _isupper(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isupper'; +function _isxdigit(__ch: Integer): Integer; cdecl; external szMSVCRT name 'isxdigit'; +function _strchr(__s: PAnsiChar; __c: Integer): PAnsiChar; cdecl; external szMSVCRT name 'strchr'; + +function malloc(size: size_t): Pointer; cdecl; external szMSVCRT name 'malloc'; + +function pcre_malloc(Size: Integer): Pointer; +begin + if Assigned(pcre_malloc_user) then + Result := pcre_malloc_user(Size) + else + Result := malloc(Size); +end; + +function pcre_stack_malloc(Size: Integer): Pointer; +begin + if Assigned(pcre_stack_malloc_user) then + Result := pcre_stack_malloc_user(Size) + else + Result := malloc(Size); +end; + +function _malloc(size: size_t): Pointer; +begin + Result := pcre_malloc(size); +end; + +procedure free(pBlock: Pointer); cdecl; external szMSVCRT name 'free'; + +procedure pcre_free(P: Pointer); +begin + if Assigned(pcre_free_user) then + pcre_free_user(P) + else + free(P); +end; + +procedure pcre_stack_free(P: Pointer); +begin + if Assigned(pcre_stack_free_user) then + pcre_stack_free_user(P) + else + free(P); +end; + +procedure _free(pBlock: Pointer); +begin + pcre_free(pBlock); +end; + +function pcre_callout(var callout_block: pcre_callout_block): Integer; cdecl; +begin + if Assigned(pcre_callout_user) then + Result := pcre_callout_user(callout_block) + else + Result := 0; +end; + +{$ELSE ~PCRE_STATICLINK} + +type + {$IFDEF MSWINDOWS} + TModuleHandle = HINST; + {$ENDIF MSWINDOWS} + {$IFDEF LINUX} + TModuleHandle = Pointer; + {$ENDIF LINUX} + +const + {$IFDEF MSWINDOWS} + libpcremodulename = 'pcrelib.dll'; + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + libpcremodulename = 'libpcre.so.0'; + {$ENDIF UNIX} + PCRECompileExportName = 'pcre_compile'; + PCRECompile2ExportName = 'pcre_compile2'; + PCREConfigExportName = 'pcre_config'; + PCRECopyNamedSubstringExportName = 'pcre_copy_named_substring'; + PCRECopySubStringExportName = 'pcre_copy_substring'; + PCREDfaExecExportName = 'pcre_dfa_exec'; + PCREExecExportName = 'pcre_exec'; + PCREFreeSubStringExportName = 'pcre_free_substring'; + PCREFreeSubStringListExportName = 'pcre_free_substring_list'; + PCREFullInfoExportName = 'pcre_fullinfo'; + PCREGetNamedSubstringExportName = 'pcre_get_named_substring'; + PCREGetStringNumberExportName = 'pcre_get_stringnumber'; + PCREGetStringTableEntriesExportName = 'pcre_get_stringtable_entries'; + PCREGetSubStringExportName = 'pcre_get_substring'; + PCREGetSubStringListExportName = 'pcre_get_substring_list'; + PCREInfoExportName = 'pcre_info'; + PCREMakeTablesExportName = 'pcre_maketables'; + PCRERefCountExportName = 'pcre_refcount'; + PCREStudyExportName = 'pcre_study'; + PCREVersionExportName = 'pcre_version'; + PCREMallocExportName = 'pcre_malloc'; + PCREFreeExportName = 'pcre_free'; + PCREStackMallocExportName = 'pcre_stack_malloc'; + PCREStackFreeExportName = 'pcre_stack_free'; + PCRECalloutExportName = 'pcre_callout'; + INVALID_MODULEHANDLE_VALUE = TModuleHandle(0); + +var + PCRELib: TModuleHandle = INVALID_MODULEHANDLE_VALUE; +{$ENDIF ~PCRE_STATICLINK} + +procedure SetPCREMallocCallback(const Value: pcre_malloc_callback); +begin + {$IFDEF PCRE_STATICLINK} + pcre_malloc_user := Value; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_malloc_func) then + LoadPCRE; + + if Assigned(pcre_malloc_func) then + pcre_malloc_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + {$ENDIF ~PCRE_STATICLINK} +end; + +function GetPCREMallocCallback: pcre_malloc_callback; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_malloc_user; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_malloc_func) then + LoadPCRE; + + if not Assigned(pcre_malloc_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_malloc_func^; + {$ENDIF ~PCRE_STATICLINK} +end; + +function CallPCREMalloc(Size: Integer): Pointer; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_malloc(Size); + {$ELSE ~PCRE_STATICLINK} + Result := pcre_malloc_func^(Size); + {$ENDIF ~PCRE_STATICLINK} +end; + +procedure SetPCREFreeCallback(const Value: pcre_free_callback); +begin + {$IFDEF PCRE_STATICLINK} + pcre_free_user := Value; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_free_func) then + LoadPCRE; + + if Assigned(pcre_free_func) then + pcre_free_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + {$ENDIF ~PCRE_STATICLINK} +end; + +function GetPCREFreeCallback: pcre_free_callback; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_free_user; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_free_func) then + LoadPCRE; + + if not Assigned(pcre_free_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_free_func^ + {$ENDIF ~PCRE_STATICLINK} +end; + +procedure CallPCREFree(P: Pointer); +begin + {$IFDEF PCRE_STATICLINK} + pcre_free(P); + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_free_func) then + LoadPCRE; + pcre_free_func^(P); + {$ENDIF ~PCRE_STATICLINK} +end; + +procedure SetPCREStackMallocCallback(const Value: pcre_stack_malloc_callback); +begin + {$IFDEF PCRE_STATICLINK} + pcre_stack_malloc_user := Value; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_stack_malloc_func) then + LoadPCRE; + + if Assigned(pcre_stack_malloc_func) then + pcre_stack_malloc_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + {$ENDIF ~PCRE_STATICLINK} +end; + +function GetPCREStackMallocCallback: pcre_stack_malloc_callback; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_stack_malloc_user; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_stack_malloc_func) then + LoadPCRE; + + if not Assigned(pcre_stack_malloc_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_stack_malloc_func^; + {$ENDIF ~PCRE_STATICLINK} +end; + +function CallPCREStackMalloc(Size: Integer): Pointer; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_stack_malloc(Size); + {$ELSE ~PCRE_STATICLINK} + Result := pcre_stack_malloc_func^(Size); + {$ENDIF ~PCRE_STATICLINK} +end; + +procedure SetPCREStackFreeCallback(const Value: pcre_stack_free_callback); +begin + {$IFDEF PCRE_STATICLINK} + pcre_stack_free_user := Value; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_stack_free_func) then + LoadPCRE; + + if Assigned(pcre_stack_free_func) then + pcre_stack_free_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + {$ENDIF ~PCRE_STATICLINK} +end; + +function GetPCREStackFreeCallback: pcre_stack_free_callback; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_stack_free_user; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_stack_free_func) then + LoadPCRE; + + if not Assigned(pcre_stack_free_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_stack_free_func^; + {$ENDIF ~PCRE_STATICLINK} +end; + +procedure CallPCREStackFree(P: Pointer); +begin + {$IFDEF PCRE_STATICLINK} + pcre_stack_free(P); + {$ELSE ~PCRE_STATICLINK} + pcre_stack_free_func^(P); + {$ENDIF ~PCRE_STATICLINK} +end; + +procedure SetPCRECalloutCallback(const Value: pcre_callout_callback); +begin + {$IFDEF PCRE_STATICLINK} + pcre_callout_user := Value; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_callout_func) then + LoadPCRE; + + if Assigned(pcre_callout_func) then + pcre_callout_func^ := Value + else if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + {$ENDIF ~PCRE_STATICLINK} +end; + +function GetPCRECalloutCallback: pcre_callout_callback; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_callout_user; + {$ELSE ~PCRE_STATICLINK} + if not Assigned(pcre_callout_func) then + LoadPCRE; + + if not Assigned(pcre_callout_func) then + begin + Result := nil; + if Assigned(LibNotLoadedHandler) then + LibNotLoadedHandler; + end + else + Result := pcre_callout_func^; + {$ENDIF ~PCRE_STATICLINK} +end; + +function CallPCRECallout(var callout_block: pcre_callout_block): Integer; +begin + {$IFDEF PCRE_STATICLINK} + Result := pcre_callout(callout_block); + {$ELSE ~PCRE_STATICLINK} + Result := pcre_callout_func^(callout_block); + {$ENDIF ~PCRE_STATICLINK} +end; + +{$IFNDEF PCRE_STATICLINK} +procedure InitPCREFuncPtrs(const Value: Pointer); +begin + {$IFDEF PCRE_LINKONREQUEST} + @pcre_compile := Value; + @pcre_compile2 := Value; + @pcre_config := Value; + @pcre_copy_named_substring := Value; + @pcre_copy_substring := Value; + @pcre_dfa_exec := Value; + @pcre_exec := Value; + @pcre_free_substring := Value; + @pcre_free_substring_list := Value; + @pcre_fullinfo := Value; + @pcre_get_named_substring := Value; + @pcre_get_stringnumber := Value; + @pcre_get_stringtable_entries := Value; + @pcre_get_substring := Value; + @pcre_get_substring_list := Value; + @pcre_info := Value; + @pcre_maketables := Value; + @pcre_refcount := Value; + @pcre_study := Value; + @pcre_version := Value; + {$ENDIF PCRE_LINKONREQUEST} + pcre_malloc_func := nil; + pcre_free_func := nil; + pcre_stack_malloc_func := nil; + pcre_stack_free_func := nil; + pcre_callout_func := nil; +end; +{$ENDIF ~PCRE_STATICLINK} + +function IsPCRELoaded: Boolean; +begin + {$IFDEF PCRE_STATICLINK} + Result := True; + {$ELSE ~PCRE_STATICLINK} + Result := PCRELib <> INVALID_MODULEHANDLE_VALUE; + {$ENDIF ~PCRE_STATICLINK} +end; + +function LoadPCRE: Boolean; +{$IFDEF PCRE_STATICLINK} +begin + Result := True; +end; +{$ELSE ~PCRE_STATICLINK} + function GetSymbol(SymbolName: PAnsiChar): Pointer; + begin + {$IFDEF MSWINDOWS} + Result := GetProcAddress(PCRELib, PChar(SymbolName)); + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + Result := dlsym(PCRELib, PChar(SymbolName)); + {$ENDIF UNIX} + end; + +begin + Result := PCRELib <> INVALID_MODULEHANDLE_VALUE; + if Result then + Exit; + + if PCRELib = INVALID_MODULEHANDLE_VALUE then + {$IFDEF MSWINDOWS} + PCRELib := SafeLoadLibrary(libpcremodulename); + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + PCRELib := dlopen(PAnsiChar(libpcremodulename), RTLD_NOW); + {$ENDIF UNIX} + Result := PCRELib <> INVALID_MODULEHANDLE_VALUE; + if Result then + begin + {$IFDEF PCRE_LINKONREQUEST} + @pcre_compile := GetSymbol(PCRECompileExportName); + @pcre_compile2 := GetSymbol(PCRECompile2ExportName); + @pcre_config := GetSymbol(PCREConfigExportName); + @pcre_copy_named_substring := GetSymbol(PCRECopyNamedSubstringExportName); + @pcre_copy_substring := GetSymbol(PCRECopySubStringExportName); + @pcre_dfa_exec := GetSymbol(PCREDfaExecExportName); + @pcre_exec := GetSymbol(PCREExecExportName); + @pcre_free_substring := GetSymbol(PCREFreeSubStringExportName); + @pcre_free_substring_list := GetSymbol(PCREFreeSubStringListExportName); + @pcre_fullinfo := GetSymbol(PCREFullInfoExportName); + @pcre_get_named_substring := GetSymbol(PCREGetNamedSubstringExportName); + @pcre_get_stringnumber := GetSymbol(PCREGetStringNumberExportName); + @pcre_get_stringtable_entries := GetSymbol(PCREGetStringTableEntriesExportName); + @pcre_get_substring := GetSymbol(PCREGetSubStringExportName); + @pcre_get_substring_list := GetSymbol(PCREGetSubStringListExportName); + @pcre_info := GetSymbol(PCREInfoExportName); + @pcre_maketables := GetSymbol(PCREMakeTablesExportName); + @pcre_refcount := GetSymbol(PCRERefCountExportName); + @pcre_study := GetSymbol(PCREStudyExportName); + @pcre_version := GetSymbol(PCREVersionExportName); + {$ENDIF PCRE_LINKONREQUEST} + pcre_malloc_func := GetSymbol(PCREMallocExportName); + pcre_free_func := GetSymbol(PCREFreeExportName); + pcre_stack_malloc_func := GetSymbol(PCREStackMallocExportName); + pcre_stack_free_func := GetSymbol(PCREStackFreeExportName); + pcre_callout_func := GetSymbol(PCRECalloutExportName); + end + else + InitPCREFuncPtrs(@LibNotLoadedHandler); +end; +{$ENDIF ~PCRE_STATICLINK} + +procedure UnloadPCRE; +begin + {$IFNDEF PCRE_STATICLINK} + if PCRELib <> INVALID_MODULEHANDLE_VALUE then + {$IFDEF MSWINDOWS} + FreeLibrary(PCRELib); + {$ENDIF MSWINDOWS} + {$IFDEF UNIX} + dlclose(Pointer(PCRELib)); + {$ENDIF UNIX} + PCRELib := INVALID_MODULEHANDLE_VALUE; + InitPCREFuncPtrs(@LibNotLoadedHandler); + {$ENDIF ~PCRE_STATICLINK} +end; + +{$IFDEF PCRE_STATICLINK} +procedure pcre_dispose(pattern, hints, chartable: Pointer); +begin + if pattern <> nil then pcre_free(pattern); + if hints <> nil then pcre_free(hints); + if chartable <> nil then pcre_free(chartable); +end; +{$ENDIF PCRE_STATICLINK} + +{$IFDEF PCRE_LINKDLL} +function pcre_compile; external libpcremodulename name PCRECompileExportName; +function pcre_compile2; external libpcremodulename name PCRECompile2ExportName; +function pcre_config; external libpcremodulename name PCREConfigExportName; +function pcre_copy_named_substring; external libpcremodulename name PCRECopyNamedSubStringExportName; +function pcre_copy_substring; external libpcremodulename name PCRECopySubStringExportName; +function pcre_dfa_exec; external libpcremodulename name PCREDfaExecExportName; +function pcre_exec; external libpcremodulename name PCREExecExportName; +procedure pcre_free_substring; external libpcremodulename name PCREFreeSubStringExportName; +procedure pcre_free_substring_list; external libpcremodulename name PCREFreeSubStringListExportName; +function pcre_fullinfo; external libpcremodulename name PCREFullInfoExportName; +function pcre_get_named_substring; external libpcremodulename name PCREGetNamedSubStringExportName; +function pcre_get_stringnumber; external libpcremodulename name PCREGetStringNumberExportName; +function pcre_get_stringtable_entries; external libpcremodulename name PCREGetStringTableEntriesExportName; +function pcre_get_substring; external libpcremodulename name PCREGetSubStringExportName; +function pcre_get_substring_list; external libpcremodulename name PCREGetSubStringListExportName; +function pcre_info; external libpcremodulename name PCREInfoExportName; +function pcre_maketables; external libpcremodulename name PCREMakeTablesExportName; +function pcre_refcount; external libpcremodulename name PCRERefCountExportName; +function pcre_study; external libpcremodulename name PCREStudyExportName; +function pcre_version; external libpcremodulename name PCREVersionExportName; +procedure pcre_dispose; external libpcremodulename name 'pcre_dispose'; +{$ENDIF PCRE_LINKDLL} + +end. + diff --git a/source/pcre/makefile.mak b/source/pcre/makefile.mak new file mode 100644 index 0000000..c2bb7ff --- /dev/null +++ b/source/pcre/makefile.mak @@ -0,0 +1,130 @@ +# +# makefile to make pcre .obj files using Borland's C++ compiler bcc32 +# derived from a makefile generated by BCB6' bpr2mak +# +# if pcre source directory is different from $(JCL)\source\pcre-7.7, use +# "make -Dpcresrc=" to tell make where to find the +# source files +# +# Make.exe needs to reside in the same directory as bcc32.exe. +# For example, if you have Borlands free C++ v. 5.5 compiler (available from +# http://www.borland.com/products/downloads/download_cbuilder.html#) installed: +# +# >C:\Program Files\Borland\BCC55\Bin\make +# +# or, if you want to use C++ Builder 6: +# +# >C:\Program Files\Borland\CBuilder6\Bin\make +# +# or, if you want to use Borland Developer Studio 2006: +# +# >C:\Program files\Borland\BDS\4.0\bin\make +# +# To choose the target CPU, pass "-DCPU=n" as option to make, with n being a +# number between 3 and 6, with the following meanings: +# +# n Target CPU (or compatible) +# -------------------------------- +# 3 80386 +# 4 80486 +# 5 Pentium (default) +# 6 Pentium Pro +# +# Robert Rossmair, 2004-10-16 +# + +CallingConvention = -pr + +!if !$d(BCB) +BCB = $(MAKEDIR)\.. +!endif + +BCC = $(BCB) + +!if !$d(pcresrc) +pcresrc = ..\..\..\pcre-7.7 +!endif + +!if !$d(CPU) +CPU = 5 # Pentium +!endif + +# --------------------------------------------------------------------------- +# IDE SECTION +# --------------------------------------------------------------------------- +# The following section of the project makefile is managed by the BCB IDE. +# It is recommended to use the IDE to change any of the values in this +# section. +# --------------------------------------------------------------------------- + +VERSION = BCB.06.00 +# --------------------------------------------------------------------------- +OBJFILES = .\pcre_compile.obj .\pcre_config.obj .\pcre_dfa_exec.obj \ + .\pcre_exec.obj .\pcre_fullinfo.obj .\pcre_get.obj .\pcre_globals.obj \ + .\pcre_info.obj .\pcre_maketables.obj .\pcre_newline.obj \ + .\pcre_ord2utf8.obj .\pcre_refcount.obj .\pcre_study.obj .\pcre_tables.obj \ + .\pcre_try_flipped.obj .\pcre_ucd.obj .\pcre_valid_utf8.obj \ + .\pcre_version.obj .\pcre_xclass.obj .\pcre_default_tables.obj + +# --------------------------------------------------------------------------- +DEBUGLIBPATH = $(BCB)\lib\debug +RELEASELIBPATH = $(BCB)\lib\release +USERDEFINES = SUPPORT_UTF8;SUPPORT_UCP +SYSDEFINES = NO_STRICT;_NO_VCL;_RTLDLL +INCLUDEPATH = $(pcresrc);$(BCC)\include;$(BCB)\include\vcl +LIBPATH = $(BCB)\lib\obj;$(BCB)\lib +# LIBPATH = $(pcresrc) +WARNINGS= -wpar -w-aus +PATHC = .;$(pcresrc) +# PATHOBJ = .;$(LIBPATH) +ALLLIB = import32.lib cw32i.lib +# --------------------------------------------------------------------------- +CFLAG1 = -O2 -Ve -X- -a8 -$(CPU) -b -d -k- -vi -tWM- -DHAVE_CONFIG_H + +LFLAGS = -D"" -ap -Tpe -x -Gn +# --------------------------------------------------------------------------- +# MAKE SECTION +# --------------------------------------------------------------------------- +# This section of the project file is not used by the BCB IDE. It is for +# the benefit of building from the command-line using the MAKE utility. +# --------------------------------------------------------------------------- + +.autodepend +# --------------------------------------------------------------------------- + +!if !$d(BCC32) +BCC32 = bcc32 +!endif + +!if !$d(LINKER) +LINKER = ilink32 +!endif + +# --------------------------------------------------------------------------- +!if $d(PATHC) +.PATH.C = $(PATHC) +!endif + +# --------------------------------------------------------------------------- +pcre: includes tables $(OBJFILES) + +# --------------------------------------------------------------------------- +.c.obj: + $(BCC)\BIN\$(BCC32) -c $(CFLAG1) $(CallingConvention) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n$(@D) {$< } + +includes: + copy /Y $(pcresrc)\pcre.h.generic $(pcresrc)\pcre.h + copy /Y $(pcresrc)\config.h.generic $(pcresrc)\config.h + +tables: + $(BCC)\BIN\$(BCC32) -c -tWC $(CFLAG1) $(WARNINGS) -I$(INCLUDEPATH) -D$(USERDEFINES);$(SYSDEFINES) -n.\ $(pcresrc)\dftables.c + $(BCC)\BIN\$(LINKER) $(LFLAGS) -L$(LIBPATH) c0x32.obj .\dftables.obj, .\dftables.exe,, $(ALLLIB),, + del dftables.tds + del dftables.obj + dftables.exe pcre_default_tables.c + del dftables.exe +# --------------------------------------------------------------------------- + + + + diff --git a/source/pcre/pcre_compile.obj b/source/pcre/pcre_compile.obj new file mode 100644 index 0000000000000000000000000000000000000000..84f86983144456ee66375af152705b924e21c580 GIT binary patch literal 34296 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 literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_default_tables.obj b/source/pcre/pcre_default_tables.obj new file mode 100644 index 0000000000000000000000000000000000000000..4b24e0cec6f48d9037bbb183f860027d9cb7029d GIT binary patch literal 2619 zcmZpWWDwVjDM&6#jZaBUODxSPi7!b^%1JHOOJ?X0Wnf?sbjmNvNz6-8aMspVFx4~F zGi2ycU|@KGQ=6&(aw~=oeg=k@*>PD`?0PY!$pv~D3>^YM5$2k1D-MWtAR$Yjkb=f- z4MP{Jn1CRE_aMhQ!x*T2G0ySvPNAM4fy}%ZJ-wLByyTqHl+>8q)Lf8WE2v&0QuM~> zZB+)A%*D?w-(nVDCT zT9lUv_LmS)4pg9V>BS`H1Cuy7mB<4HK_wvWLX{b)f?=8l18aPUYeWc(v%iZgknQ3a z;>ZFb*j(I${6hm+;+=wnnSq#L20Oz_1_nkZ1~AKkg@qNuVqjqBU|^WV#=!QU`9BlG z1R0**Up+`2~eV#U-U> z!)t7~fO>KhuHnp;}i+B-VCx_f&2`X@}BGo;uNw0XRJbd){$~I}^!dxzZ{L6X{Pp|K z-+%vU?te!oXBSsDcMnf5Zy#Sj|A4@t;E>R;@QBE$!Qp>9U=jc&2fW}va~)6~gt3GF zz$Ap+Nw@JlG7%hy^YTObjfPnE}+@ H4Ju0j!GAEi literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_dfa_exec.obj b/source/pcre/pcre_dfa_exec.obj new file mode 100644 index 0000000000000000000000000000000000000000..d628a6154116de6ad30dd46aa8c526110bc57656 GIT binary patch literal 26959 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 literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_exec.obj b/source/pcre/pcre_exec.obj new file mode 100644 index 0000000000000000000000000000000000000000..94de74aac1bc9cf70fe171d5c5d6f03456f54c7f GIT binary patch literal 42191 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 Hg!ume$&2G= literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_fullinfo.obj b/source/pcre/pcre_fullinfo.obj new file mode 100644 index 0000000000000000000000000000000000000000..6a7ff4fa390e3db3153d408198fd784df33e8b2f GIT binary patch literal 2098 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 YE&P1bU=G8TxjIzlMf@)f6`1kj-#X1K*Z=?k literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_info.obj b/source/pcre/pcre_info.obj new file mode 100644 index 0000000000000000000000000000000000000000..fb7438d736a486ff160355cb971dfaffc1d33711 GIT binary patch literal 1648 zcmbtUO=}ZT6utQl^&?5Oq9BrmV4yG%YZi_Q`K0P5AxT@op^tQCVqct(IC)uAL@gmW zWfWYA2tpTb`U|Q%!EX8w#O}Ik$;wR^QXJo##)#eJbuq)`%scnaJ@0T{#^8(~wPj01 z*l3!94E7WNB=V-EH4G(Ln4V5%gp7~|`vSn;vBQ^NnIhORfc?jdEiocUUAZkRgB_1xB0m-*>L(eM&C9T`o0B;2i`?la@) z-XRJ$xrbA>qBc)LZ&)W0p0sq&{+xcrp4s{QigipnZ1X!q*)!wFt}n@CrS18h>)Mdf z)igGpk4-O)|KHKSTj!cS+WZn#ZsWcqCPsNkRLf|1dri`BEYKzdNj9m)J(tj++-C`R zJU{~!hj%Fmp?c{-Jy<9gOSCTL>bW3$g^RP*@?0f|^0iukKJY#Q8&AOJ2d@GMhW=KO zD8Pp>gx!Gahu2dunMGby6Ke%EHQa8i3c%Y0fNvr@%5WOrU{h?}5C5U-UGM?a=6hS; zouAH+{?~5g^KaL62MbPo^;fA{sE~^-cvHU`oY~%EO}rC_>_n!&LryuB=<4o^r-y;H zbNvHyUs%hvv$4%=ytkEFeE1DMhTzk+c^^1*LHGl& C@FsKs literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_maketables.obj b/source/pcre/pcre_maketables.obj new file mode 100644 index 0000000000000000000000000000000000000000..68658f0b31373f1b77ae3b88bad300e54e7085e6 GIT binary patch literal 2503 zcmbtWO>7fa5FXnb+eutvD5Rir@WF?aNGk;=Qfp|*k7)!#3dG3^KTEco^(Nl1UT@dC zYJsRqt#qQ+*S6h%*9!Agc58S451U9p~xp1xF)J3l@)IzIG6AJ@?0 zMtFXB@(gvzD#x;%tcr@6k+__cqugYJdwefkUdfA;e6m6A-wPSJf+Bg>E*QFJZRE=8 zoOik;=wjA8-N-2NjAuHp&dN^WPBikK*h}6EMn=wi7DskQn*9*Epnpi>jrpA2{vP{P zwwv8szsfn;DA1dK*+~1qUecbM5smAb-S3{t#;c~HP<3x?ESmST^KR8Wsc+x>2xjil zSu44}F&kyokaShB?{xrUbf9&xoS4TJx4Yz1bkYgfe3%9>3io=Tjh{Guae^5cOPxkL zH8e5A(4SyxbbM^`Ji`yC(*gX#{SZ8Q==b@-wxYnaITk=D4DcWb!Fr(XhdU1;!Z&8O zf@2CgcwRB`O8z@ZhcGV}9Lw>VnugvGk8`vj8Fn}$&&iJE1Sv&OR5NEs3z{HG(2lNFzm7QRu}4iwk?J66n&Rd{=4w$TJ5 zJetnpx$(~qeu}GMY@y)N+9EEjwRd{#w#CFRU*)H-JcXaG!s`g}RgMvVjWr$aq&vf!-+<6hPw>8Ll|)_kf66gKtsnc)YJE%&mRejv9ZyUV?&F6u}LHj zOHx?)oCXw42U-UwCa1B*?JnuYAf1437^5ycf^YkvO`71(PPm6hBRuXS zLlZ-8y0k}5j*SX2w-g>9ci|7d^T5sLEW?7m18}#!-g&$L-?u}1-BoAd`bXfCBztBX z`qZbAo|shSz#C%y04w-Brt3uGwle#A$m4v66)ctUHIL>KJ7+or@Y6pmt^)bX{IxtD zKa-DE@ z{T=V$=${Ldja!%%HXoB{IkbB1gU(-q=HKn+(?PNk-h7I|0}oGxRu`XH9>Pr28zwT&R3`o=)#Q{LpG=dl~A;Nt#_{nb-r@oA^ECIPk&o{ zY`JY0gqpd{8nGQ0KDLgNXf?Dt*I9_wv)k_&o?8vAZH2M8JloLvN2J`X)%NQ;hERUm z7&Kq^Y3jf+)0)>KyZWECrif4t-9dtpdiLd839O;L+4mzn{LlqY{BiclaGcrtInHja K#~C2Bu73f&w<);* literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_ord2utf8.obj b/source/pcre/pcre_ord2utf8.obj new file mode 100644 index 0000000000000000000000000000000000000000..d1556f6cc021471418896c2caecd0c6a115f568d GIT binary patch literal 1660 zcmZn=W)ReiDM&6#jn6MiF)A%dv(QUs=n!RKU=Vc5FUm>GOHpvv)>bgpGu1O>=#XMy zc!5Pj?rcd*h7NuPhL_oKSyt?NF{Q}`dKnBI0zeVwnr@$pWfo*;qDycj*bn9RK7oYIuknB3G{kX|dOUL#WU#^>ZGgEZSf zHJgy48K^fgCzX7=i%W{gwi~E7GcTQd?Wu`H$rY8oZl6N@Mb_>!z#aPlWJy=Hvp(#UsvJR;V~vK{VoNnpsW^y;>Pm5Z5qC8UqX+SPWnSpW)(=-@Z<3n5{LRg&rU0i`|7sn7s77)Sa z;vVE58o(0o6dcS9#0)do8CH5TFfuWKSquy;tS}Zk2Lr<_HU_r;%>S7fdYl-9;^V=^ zHmJysFG)PfdxOq0)GZZ28Q4;{oa0gSbBV%0jVTQT93{P0Y7@vrTF+Z^lV`AtAdXNDC!Py|) literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_refcount.obj b/source/pcre/pcre_refcount.obj new file mode 100644 index 0000000000000000000000000000000000000000..e9cdda34d95ecc1b3f85cf1f3dde8af300e3462d GIT binary patch literal 1587 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 QGCrPSaeN~o2XILK2fo-xdH?_b literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_tables.obj b/source/pcre/pcre_tables.obj new file mode 100644 index 0000000000000000000000000000000000000000..94fa034036c2ee7d21eac285d44a22d11012d908 GIT binary patch literal 3559 zcmbuBTWlLe6o!9$ous5KY0^v5dkX~$EiP%BLc^uRPDn$Y*u*ZEVzC;pC*G;|V%EE@ zj0&xUgeodf5fUIIpb`jid8v2-fe<|K01rqYgj7HXgoFeSAl?9h6#j9NhEn=acjfcV z*>mR1nK?5a`&c)+lEs=8NHY{AN7j=TjxPt$o$>=mc;yZ0En7AWBnOiHINk^RbYAy0 zZ#+1N<6Xed)dvm^b|j0DRZH49zJwOz$ce!PGsA1sZkjf1{$_Lk@L(}FnH`-RdZfQN zo2QsI&D8!eZBSk@nJg;La-y;d`k!4G8??}VuBef9NBvAdKk2v zo3YBPv>q-+VA-vQhh;~VS`Ih8DK%5Ln`i63<6`Pw56jALIXR=ta_UmTMQ|yF4-dM{ zY-0xXr0NT<@1O)Pf0J0`UX7z5;f?-Gn>(77DnzZ zB+}X85$eN3g`tE-`|#*wc7HBmrt*1%g6BH$IuMJasV(bBw9RNh#{%H_cC=3$({Y?! z$v!=%*}Sr&aO$R+d%K@k_Jw@g*Id}QW9Nl^JAdOEPOf9V-t+UBb@h&LX~oF!W|vm!lx zR5Kj!R3gEO9i=wiL7CRFp2Av9PakckT|XaEzNh>~`GfK|gK6G_Hk(~^cuF2EsL;%JZtj?i)$9wj^RINuX^f^r+4B^Tl)auHr57vnT} zIo>0?@DaHLpOW1;L-yc1aw&c!m*H1(IesTs;4iWl@mKZhTY`#*u!z!Fhu=MWa#ZhmU=BeByYr9++>H;&L3~W!jL*poz9etK8P22~ zUo*>Ud`qsu_vBiH+^5*Nk73Nc$78Yh+>_u}H6GWZoBw&432JNGxDkfYZp=SzhQZI- zLFWdaZ*i*6V7=!JGulk5dCrZ<Cr}gdf5b0RxuhK6oCj8mB5Xvw!&Warc@$FH<9Q2*egqcJdC|6;P9Tnn8(?wP?oVz zcwC)e8;YuS?Gp`Qn;C_Dwg~*X%~Vuf8StcJu0}@qlu&ud$eLvfWRzQqNRR(X%@i^Y zGA=Ug6e2@=!_t$_G;{$mj&b2|e24WKw=ga-PNVPXg=u#~c&-RAfr&CECNxA6+|6&Jk0Rs^WJyf@4esSJ??@| zNhzt8Mr>=DXx zAmcMLL40661RD8VjrMUbHwl--P)!*p2s7r`^>)|#gOnWqlo)ZEYx9Cp}MH`!bypv5If>m{q|F2(fcJ8yJON_E8PcNx^x}Ni{Ce_at-o L-WIlodiDGSav6}y literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_ucd.obj b/source/pcre/pcre_ucd.obj new file mode 100644 index 0000000000000000000000000000000000000000..77562db222464f3492a957a7b45d852dfe784ff2 GIT binary patch literal 54707 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 literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_valid_utf8.obj b/source/pcre/pcre_valid_utf8.obj new file mode 100644 index 0000000000000000000000000000000000000000..b6537d0837c39b5dc27af558e2a98caf4a26e818 GIT binary patch literal 1815 zcmbtUO-K}B7=Fk7(YkD_m`Oo<3SnVt{efKjTU`+oQv9oE@MCs%HpgaXm)UQG$e$fH zVQ~aINd$pc&xL2JLwhU_9TFj-H1I-J z(q%L)s!9?WICYVipx6ii!S0N%idr(%6Ap(u_zpe-#WMh34uzD9Gf^l80Cw+9Ohs#X z!H}~2Bot53jAzD*)>X!^R3}Y^TDDpu@u)B~JlHoJn~Mm}ETIRX?y-KBP&9$(1x1ro zBPk1MInA(92isZ=7O5GDfiF4W_G-WsE2^^l**UDco=vfecGo>z7IkUTJsc-hCE*&5 zw3JdIuFK)Qts35QIH_b@52xs)oH`0z)Q>_uo=V&6?{Z%id)eLXtCCi5j-C9nLwiRx zw5JmizMr-C-PN@r%}`Y~-5r`JhTVo5?o9VnUVM0Un(K;DXmGl>=F) ziVmwy90xEmesf`SHei*?Tx>D;aMSo@y$!#yO1s;%C9vLUl00*kHmNnq2DP)t!X@j) zE=yP+3p-Z>>(8hQUEk+@h2NgJO7%I-gx|&5wicVsoedh8`*~_{rMg00>%#!98^8?% zxV4=7`T7b?8Cmw_@&l86Vv%o;3*mrCzOrCwu{poLs`uL+w@vcR>9~NKO!9*U^R$z_ zjdQGXdHSr~dH(-j@Y{uxHx~Bhw`11(!b`HS6<7`uLJB25LZ7{~vceJh1xx7ebda`3QFI&5ke-KgNcl%kg{+Z05CVVk7w)VwU&tRRSZ z5DDzXlZYVfwcnse!H?m!2Nyp?eI^^UsF$4{CYgDj{N|r|=HdT#1)ltNNxf@{{23DKfQ>UABzAFOvX$e*xtx-P2cqet7q)yJgnt~ zwtydG@YXyqv3<{#*B-`^v^KFkIFiTReqcpPVdidirFnS29=RLF`6BKRwq9<%FRvGSd#J#nUdiK|$ew|FO|L zrSP$eqGfD9H45!+XPf4orqL{jN2#;Z>+bIstk&LMo(4XZak_?_f^?>$P@K(x@&fR= zgwjR+LcxdYKyK;LWz#0VMnd6mT;akl=DRz g6+^+~HYooLcUeQ-tkoJDwXF?J6U$30U@SKI1(YlO-~a#s literal 0 HcmV?d00001 diff --git a/source/pcre/pcre_xclass.obj b/source/pcre/pcre_xclass.obj new file mode 100644 index 0000000000000000000000000000000000000000..627e282fc45ead26d80ed2bc60e2c9186f39224e GIT binary patch literal 2410 zcmbtVT})eL7=F)BTgK)-n40-_K|(LcW*VWzxON07UD)On0;TFKoRrf8boPg|rz(a? z*|8CNMl^` zobUOb_j}Hp?Q+AWZ8|TL?qcB~E2s0tuYs%ZJ&>@5xnwFcIU3PCzZ+BO3$6L+b zijKFFJcpDy}?fg#;=E<7A48|SF_ZrzFRG0`80=oUw|Ul_h0x-WV^#*3#y zTKntl*MQdS`ua5x3WzcF93vJ`s%vW z|2p}j=-j2_vc0}?CkDcz5DojZbM3=->V;No@$wODaji@CBdFeh+b>ZD-@y4MFmUc8 zFS*&yZr2f?!r-?&yPFiUv>C2dae23YbwY?@UG(Pc#nlBYL>PY&4blWHu+vSC& z;_jPHc~SaWNzPCH=*W}gZrrj;b&sY%ljLVeu2ymUQ$>DK6KK4t$^EYmNw+ws{>b2t z6J7FxR_qKWFmuAp$`cqtGs`HAwvSbC_VA>kq6R7mpN?}~!&Up_g&4Zd;}E_T4` zX%D?R>!DT)INtV9#RDF?*pA}`4_o}iV<=`kOz~ScRlJUo$GCy<1IDVGT0817f{ez& EpX`BEXaE2J literal 0 HcmV?d00001