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 0000000..a2c4926 Binary files /dev/null and b/bin/test.unicode.json differ 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 0000000..e877da3 Binary files /dev/null and "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" differ 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 0000000..d6cf632 Binary files /dev/null and "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" differ diff --git "a/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Project1.res" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Project1.res" new file mode 100644 index 0000000..d6cf632 Binary files /dev/null and "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Project1.res" differ 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.dfm" "b/demo/YxdJson/JSON\344\270\216\346\225\260\346\215\256\351\233\206\344\272\222\350\275\254/Unit1.dfm" new file mode 100644 index 0000000..2dd74b3 --- /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.dfm" @@ -0,0 +1,316 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'Form1' + ClientHeight = 747 + ClientWidth = 1120 + 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 = 0 + Top = 343 + Width = 1120 + Height = 9 + Cursor = crVSplit + Align = alBottom + ExplicitTop = 456 + end + object DBGrid1: TDBGrid + Left = 0 + Top = 105 + Width = 1120 + Height = 238 + Align = alClient + DataSource = DataSource1 + TabOrder = 0 + TitleFont.Charset = DEFAULT_CHARSET + TitleFont.Color = clWindowText + TitleFont.Height = -11 + TitleFont.Name = 'Tahoma' + TitleFont.Style = [] + end + object Panel1: TPanel + Left = 0 + Top = 0 + Width = 1120 + Height = 105 + Align = alTop + BevelOuter = bvNone + TabOrder = 1 + DesignSize = ( + 1120 + 105) + object Label1: TLabel + Left = 6 + Top = 10 + Width = 72 + Height = 13 + Caption = #27979#35797#25968#25454#24211#65306 + end + object Button1: TButton + Left = 8 + Top = 34 + Width = 75 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'ToDataSet' + TabOrder = 0 + OnClick = Button1Click + end + object Button2: TButton + Left = 87 + Top = 34 + Width = 75 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'ToDataSet' + TabOrder = 1 + OnClick = Button2Click + end + object Button4: TButton + Left = 281 + Top = 34 + Width = 75 + Height = 25 + Anchors = [akTop, akRight] + Caption = #21152#36733#25968#25454 + TabOrder = 2 + OnClick = Button4Click + end + object Button3: TButton + Left = 168 + Top = 34 + Width = 75 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'PutDataSet' + TabOrder = 3 + OnClick = Button3Click + end + object Edit1: TEdit + Left = 84 + Top = 7 + Width = 565 + Height = 21 + TabOrder = 4 + Text = 'E:\Users Folder\Desktop\db2.mdb' + end + object Button5: TButton + Left = 362 + Top = 34 + Width = 75 + Height = 25 + Caption = #20445#23384'Data' + TabOrder = 5 + OnClick = Button5Click + end + object CheckBox1: TCheckBox + Left = 680 + Top = 9 + Width = 145 + Height = 17 + Caption = 'Base64 '#32534#30721' Blob' + Checked = True + State = cbChecked + TabOrder = 6 + end + object Button7: TButton + Left = 191 + Top = 65 + Width = 177 + Height = 25 + Caption = #19982#28784#32418'QDBJson'#21453#24207#21015#21270#23545#27604 + TabOrder = 7 + OnClick = Button7Click + end + object Button8: TButton + Left = 384 + Top = 65 + Width = 153 + Height = 25 + Caption = #19982'AdoStream'#24207#21015#21270#23545#27604 + TabOrder = 8 + OnClick = Button8Click + end + object Button9: TButton + Left = 543 + Top = 65 + Width = 162 + Height = 25 + Caption = #19982'AdoStream'#21453#24207#21015#21270#23545#27604 + TabOrder = 9 + OnClick = Button9Click + end + end + object Button6: TButton + Left = 8 + Top = 65 + Width = 177 + Height = 25 + Caption = #19982#28784#32418'QDBJson'#24207#21015#21270#23545#27604 + TabOrder = 2 + OnClick = Button6Click + end + object PageControl1: TPageControl + Left = 0 + Top = 352 + Width = 1120 + Height = 395 + ActivePage = TabSheet1 + Align = alBottom + TabOrder = 3 + object TabSheet1: TTabSheet + Caption = 'YxdJson' + object Memo1: 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 + Lines.Strings = ( + '{' + ' "rows": [' + ' {' + ' "FInterID": 22087,' + ' "FBillNo": "XSD20140722000441",' + ' "FDate": "2014-07-11T00:00:00",' + ' "FSupplyID": 36,' + ' "FMerchandizerID": 5,' + ' "FQCID": 5,' + ' "FDelDate": "2013-12-31T00:00:00",' + ' "FShipmentID": 10,' + ' "FDocumentoryID": 5,' + ' "FNote": "gh",' + ' "FFilePath": "",' + ' "FModifyTime": "1900-01-01T00:00:00",' + ' "FProgramID": 0,' + ' "FCreateUser": 1,' + ' "techDate": null,' + ' "loadingDate": null,' + ' "factoryDelDate": null,' + ' "FQty": null,' + ' "Price": null,' + ' "FQCID2": 1,' + ' "FContact": "42",' + ' "FTranType": 0,' + ' "FPhone": "12",' + ' "FAddress": "",' + ' "FSupplierName": "321",' + ' "MerchandizerName": "13",' + ' "FQCName": "13",' + ' "FDocumentoryName": "13",' + ' "FShipmentName": "[blobs]<123456>"' + ' },' + ' {' + ' "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 0000000..93e7e94 Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.otares" differ diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.res" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.res" new file mode 100644 index 0000000..70d86d0 Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest.res" differ 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 0000000..9917d72 Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_Icon2.ico" differ 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 0000000..b60aad9 Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_XE.res" differ diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.dpr" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.dpr" new file mode 100644 index 0000000..c0bafad --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.dpr" @@ -0,0 +1,17 @@ +program JSONTest; + +uses + Forms, + Unit2 in 'Unit2.pas' {Form2}, + uJSON in 'uJSON.pas', + 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_d2007.dproj" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.dproj" new file mode 100644 index 0000000..1f7ef53 --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.dproj" @@ -0,0 +1,77 @@ + + + {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 0000000..4f415bd Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007.res" differ diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007_Icon.ico" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007_Icon.ico" new file mode 100644 index 0000000..2bc1f2c Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSONTest_d2007_Icon.ico" differ diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.dproj" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.dproj" new file mode 100644 index 0000000..c1146d8 --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.dproj" @@ -0,0 +1,165 @@ + + + {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 0000000..6892d28 Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test.res" differ diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test_Icon.ico" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test_Icon.ico" new file mode 100644 index 0000000..2bc1f2c Binary files /dev/null and "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/JSON_Test_Icon.ico" differ diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.dfm" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.dfm" new file mode 100644 index 0000000..4be071c --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.dfm" @@ -0,0 +1,275 @@ +object Form2: TForm2 + Left = 0 + Top = 0 + Caption = 'JSON '#24615#33021#27979#35797 + ClientHeight = 508 + ClientWidth = 939 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + DesignSize = ( + 939 + 508) + PixelsPerInch = 96 + TextHeight = 13 + object YxdPageControl1: TPageControl + Left = 8 + Top = 208 + Width = 923 + Height = 292 + ActivePage = TabSheet1 + Anchors = [akLeft, akTop, akRight, akBottom] + TabOrder = 0 + object TabSheet1: TTabSheet + Caption = #28304'JSON'#25968#25454 + object Memo1: TMemo + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 909 + Height = 258 + Align = alClient + Lines.Strings = ( + '{' + #9'"error": 0,' + #9'"status": "success",' + #9'"currentCity": "'#21271#20140'",' + #9'"dateTime": "2014/07/12/11/29/38",' + '"items": [1,2,3,4,5,6,7,8,9,[123,456,789]],' + #9'"results": [' + #9#9'{' + #9#9#9'"startTime": "2014/02/26/00/00/00",' + #9#9#9'"title": "'#20986#21475#21277#36947#38480#21046'('#38271#26399')",' + + #9#9#9'"description": "2014'#24180'2'#26376'26'#26085#36215#65292#27599#22825'7'#26102#33267'15'#26102#65292#21271#20845#29615#22806#29615#65292#20140#34255#39640#36895#20844#36335#20986#21475#21277#36947#65292#31105#27490#36733#36135#27773#36710#36890 + + #34892#12290'",' + #9#9#9'"location": {' + #9#9#9#9'"lng": 116.2499668836,' + #9#9#9#9'"lat": 40.172181392632' + #9#9#9'},' + #9#9#9'"type": "3"' + #9#9'},' + #9#9'{' + #9#9#9'"title": "'#21271#19977#29615#35199#36335#36947#36335#26045#24037'",' + + #9#9#9'"description": "'#20174'7'#26376'3'#26085#21040'8'#26376'28'#26085#65292#33487#24030#26725#65292#22806#29615#26041#21521#65292#36827#34892#22823#20462#26045#24037#65292#26045#24037#26102#38388#20026#27599#22825#26202#38388'23'#26102'30'#20998#33267#20940 + + #26216'5'#26102'30'#20998#65292#30333#22825#27491#24120#25918#34892#31038#20250#20132#36890'",' + #9#9#9'"location": {' + #9#9#9#9'"lng": 116.31302490979,' + #9#9#9#9'"lat": 39.964084503915' + #9#9#9'},' + #9#9#9'"type": "2"' + #9#9'}' + #9']' + '}') + ScrollBars = ssBoth + TabOrder = 0 + end + object CheckBox1: TCheckBox + Left = 712 + Top = -48 + Width = 97 + Height = 17 + Caption = 'CheckBox1' + TabOrder = 1 + end + end + object TabSheet2: TTabSheet + Caption = #36755#20986#32467#26524 + ExplicitLeft = 0 + ExplicitTop = 0 + ExplicitWidth = 0 + ExplicitHeight = 0 + object Memo2: TMemo + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 909 + Height = 258 + Align = alClient + ReadOnly = True + ScrollBars = ssBoth + TabOrder = 0 + end + end + object TabSheet3: TTabSheet + Caption = #26085#24535 + ExplicitLeft = 0 + ExplicitTop = 0 + ExplicitWidth = 0 + ExplicitHeight = 0 + object ListBox1: TListBox + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 909 + Height = 258 + Align = alClient + ItemHeight = 13 + PopupMenu = PopupMenu1 + TabOrder = 0 + end + end + end + object Button1: TButton + Left = 8 + Top = 8 + Width = 161 + Height = 25 + Caption = #35835#21462#28304'JSON, toString(0)' + TabOrder = 1 + OnClick = Button1Click + end + object Button2: TButton + Left = 8 + Top = 70 + Width = 161 + Height = 25 + Caption = #35835#21462#28304'JSON'#25968#25454'20000'#27425 + TabOrder = 2 + OnClick = Button2Click + end + object Button3: TButton + Left = 8 + Top = 39 + Width = 161 + Height = 25 + Caption = #35835#21462#28304'JSON, toString(4)' + TabOrder = 3 + OnClick = Button3Click + end + object Button4: TButton + Left = 8 + Top = 101 + Width = 161 + Height = 25 + Caption = #28155#21152#21024#38500#24615#33021#27979#35797 + TabOrder = 4 + OnClick = Button4Click + end + object Button5: TButton + Left = 175 + Top = 70 + Width = 171 + Height = 25 + Caption = 'QJson'#35835#21462#28304'JSON, toString' + TabOrder = 5 + OnClick = Button5Click + end + object Button6: TButton + Left = 8 + Top = 132 + Width = 161 + Height = 25 + Caption = #21152#36733'test.json' + TabOrder = 6 + OnClick = Button6Click + end + object Button7: TButton + Left = 8 + Top = 163 + Width = 161 + Height = 25 + Caption = 'QJson'#21152#36733'test.json' + TabOrder = 7 + OnClick = Button7Click + end + object Button8: TButton + Left = 175 + Top = 8 + Width = 171 + Height = 25 + Caption = 'yxdjson'#21152#36733'Preferences.txt' + TabOrder = 8 + OnClick = Button8Click + end + object Button9: TButton + Left = 175 + Top = 39 + Width = 171 + Height = 25 + Caption = 'qjson'#21152#36733'Preferences.txt' + TabOrder = 9 + OnClick = Button9Click + end + object CheckBox2: TCheckBox + Left = 262 + Top = 209 + Width = 179 + Height = 17 + Caption = #21152#36733#23436#25104#21518#26174#31034#21040#36755#20986#32467#26524 + TabOrder = 10 + end + object Button10: TButton + Left = 175 + Top = 101 + Width = 171 + Height = 25 + Caption = 'parseStringByName' + TabOrder = 11 + OnClick = Button10Click + end + object Button11: TButton + Left = 175 + Top = 132 + Width = 171 + Height = 25 + Caption = #21152#36733#25351#23450' json '#25991#20214 + TabOrder = 12 + OnClick = Button11Click + end + object Button12: TButton + Left = 352 + Top = 70 + Width = 177 + Height = 25 + Caption = 'ItemByPath' + TabOrder = 13 + OnClick = Button12Click + end + object Button16: TButton + Left = 352 + Top = 8 + Width = 177 + Height = 25 + Caption = 'ParseObjectByName' + TabOrder = 14 + OnClick = Button16Click + end + object Button17: TButton + Left = 352 + Top = 39 + Width = 177 + Height = 25 + Caption = 'AddChildObject' + TabOrder = 15 + OnClick = Button17Click + end + object Button18: TButton + Left = 175 + Top = 163 + Width = 171 + Height = 25 + Caption = 'SuperJson'#29992#27861#27979#35797 + TabOrder = 16 + OnClick = Button18Click + end + object PopupMenu1: TPopupMenu + Left = 520 + Top = 328 + object N1: TMenuItem + Caption = #28165#31354 + OnClick = N1Click + end + end + object OpenDialog1: TOpenDialog + Filter = '*.json|*.json;*.txt|*.*|*.*' + Left = 432 + Top = 128 + end +end diff --git "a/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.pas" "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.pas" new file mode 100644 index 0000000..8ef61c2 --- /dev/null +++ "b/demo/YxdJson/JSON\345\237\272\346\234\254\346\265\213\350\257\225/Unit2.pas" @@ -0,0 +1,426 @@ +unit Unit2; + +interface + +{$WARNINGS OFF} + +uses + YxdStr, + Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, + Dialogs, StdCtrls, Menus, + {$IF (RTLVersion>=26)}Vcl.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 0000000..b3baee1 Binary files /dev/null and "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/JSONTest.res" differ 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 0000000..9917d72 Binary files /dev/null and "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" differ 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 0000000..2bc1f2c Binary files /dev/null and "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" differ 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.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_D2007.dproj" new file mode 100644 index 0000000..b1f303a --- /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_D2007.dproj" @@ -0,0 +1,101 @@ + + + + {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 0000000..b3baee1 Binary files /dev/null and "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" differ 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 0000000..b60aad9 Binary files /dev/null and "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" differ diff --git "a/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/MSVCRT.LIB" "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/MSVCRT.LIB" new file mode 100644 index 0000000..e6a3686 Binary files /dev/null and "b/demo/YxdJson/JSON\346\200\247\350\203\275\345\257\271\346\257\224\346\265\213\350\257\225/MSVCRT.LIB" differ 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 0000000..d6cf632 Binary files /dev/null and b/demo/YxdJson/JsonDebug/JSONDebug.res differ diff --git a/demo/YxdJson/JsonDebug/Project1.res b/demo/YxdJson/JsonDebug/Project1.res new file mode 100644 index 0000000..d6cf632 Binary files /dev/null and b/demo/YxdJson/JsonDebug/Project1.res differ diff --git a/demo/YxdJson/JsonDebug/Project1.skincfg b/demo/YxdJson/JsonDebug/Project1.skincfg new file mode 100644 index 0000000..7bf0e74 --- /dev/null +++ b/demo/YxdJson/JsonDebug/Project1.skincfg @@ -0,0 +1,46 @@ +[ExpressSkins] +Default=1 +ShowNotifications=0 +Enabled=0 +dxSkinBlack=1 +dxSkinBlue=1 +dxSkinBlueprint=1 +dxSkinCaramel=1 +dxSkinCoffee=1 +dxSkinDarkRoom=1 +dxSkinDarkSide=1 +dxSkinDevExpressDarkStyle=1 +dxSkinDevExpressStyle=1 +dxSkinFoggy=1 +dxSkinGlassOceans=1 +dxSkinHighContrast=1 +dxSkiniMaginary=1 +dxSkinLilian=1 +dxSkinLiquidSky=1 +dxSkinLondonLiquidSky=1 +dxSkinMcSkin=1 +dxSkinMoneyTwins=1 +dxSkinOffice2007Black=1 +dxSkinOffice2007Blue=1 +dxSkinOffice2007Green=1 +dxSkinOffice2007Pink=1 +dxSkinOffice2007Silver=1 +dxSkinOffice2010Black=1 +dxSkinOffice2010Blue=1 +dxSkinOffice2010Silver=1 +dxSkinOffice2013White=1 +dxSkinPumpkin=1 +dxSkinSeven=1 +dxSkinSevenClassic=1 +dxSkinSharp=1 +dxSkinSharpPlus=1 +dxSkinSilver=1 +dxSkinSpringTime=1 +dxSkinStardust=1 +dxSkinSummer2008=1 +dxSkinTheAsphaltWorld=1 +dxSkinsDefaultPainters=1 +dxSkinValentine=1 +dxSkinVS2010=1 +dxSkinWhiteprint=1 +dxSkinXmas2008Blue=1 diff --git a/demo/YxdJson/JsonDebug/Unit1.dfm b/demo/YxdJson/JsonDebug/Unit1.dfm new file mode 100644 index 0000000..e39fe56 --- /dev/null +++ b/demo/YxdJson/JsonDebug/Unit1.dfm @@ -0,0 +1,57 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'JSON To Record (RTTI'#27979#35797')' + ClientHeight = 382 + ClientWidth = 796 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + PixelsPerInch = 96 + TextHeight = 13 + object mmo1: TMemo + Left = 104 + Top = 0 + Width = 692 + Height = 382 + Align = alRight + Lines.Strings = ( + + '{"Sess_Id":"","Sess_Ids":"","ID":"{326A2635-4AEC-480F-A772-C746E' + + '801BA60}","Sender_ID":"{8EE3965B-C598-4854-A9CA-483DBE34FE8B}","' + + 'Sender_Name":"'#26031#29595#29305#26234#24935#37202#24215'","Groups":"","Title":"","Msg_Info":"'#27426#36814#30331#24405#26412#37202 + + #24215#26234#24935#31649#29702#31995#32479#65281#35831#24744#29992#25163#26426#25511#21046#26412#23458#25151#37324#30340#26234#33021#37202#24215#35774#22791#65281'","Msg_Type":"mtInnerUser","Send_Type' + + '":"stLogin","SEND_START_TIME":"08:30:00.000","SEND_End_TIME":"20' + + ':00:00.000",' + + '"Weeks":127,"Timers":"[\"08:00:00\",\"11:30:00\",\"20:30:00\"]",' + + '"Timers_Delay":30,"Create_Time":"2014-06-16T14:20:53.000","Start' + + '_Time":"2014-06-16","End_Time":"2015-06-16","Using":true}') + ScrollBars = ssBoth + TabOrder = 0 + ExplicitTop = 48 + ExplicitHeight = 334 + end + object Button1: TButton + Left = 8 + Top = 8 + Width = 90 + Height = 25 + Caption = 'JsonToRecord' + TabOrder = 1 + OnClick = ToRecordClick + end + object Button2: TButton + Left = 8 + Top = 39 + Width = 90 + Height = 25 + Caption = 'RecordToJson' + TabOrder = 2 + OnClick = Button2Click + end +end diff --git a/demo/YxdJson/JsonDebug/Unit1.pas b/demo/YxdJson/JsonDebug/Unit1.pas new file mode 100644 index 0000000..748d0dd --- /dev/null +++ b/demo/YxdJson/JsonDebug/Unit1.pas @@ -0,0 +1,97 @@ +unit Unit1; + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; + +type + TForm1 = class(TForm) + mmo1: TMemo; + Button1: TButton; + Button2: TButton; + procedure ToRecordClick(Sender: TObject); + procedure Button2Click(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + Form1: TForm1; + +implementation + +uses yxdjson, YxdRtti; //, YxdPersistent; + +{$R *.dfm} +type + // Ϣ + // 0:; 1:ǼǷ,2:עǼǣ3:¼4:dz;5:ʱ;6:7:񴥷 + TSend_Type = (stNone, stRegister, stUnRegister, stLogin, stLogout, stTimer, + stInterval, stService); + // Ϣ + TMsg_Type = (mtInnerSys, mtInnerUser, mtSvcSys, mtSvcUser); +TAppMsg = record + Sess_Id: string; // ¼sessIDҪʱŸֵ + Sess_Ids:string;// sessionIDͶ̣߳ѭ߳ + ID: string; // ϢΨһID + Sender_ID: string; // ID + Sender_Name: string; // + Groups: string; // Ϣ + Title: string; // Ϣ + Msg_Info: string; // ϢϢ + Msg_Type: TMsg_Type; // Ϣ + Send_Type: TSend_Type; // + SEND_START_TIME: TTime; // ʼͽֹʱ + SEND_End_TIME: TTime; // ʼͽֹʱ + Weeks: Integer; // 127ȫѡ Ϊ 127ѡΪ0 + Timers: string; // JSONַ + Timers_Delay: Integer; // ʱӳ + Create_Time: TDateTime; + Start_Time: TDateTime; // Чʼʱ + End_Time: TDateTime; // Чֹʱ + Using: Boolean; // ʹ +end; + + +procedure TForm1.Button2Click(Sender: TObject); +var + Msg: TAppMsg; + Json: JSONObject; +begin + FillChar(Msg, SizeOf(Msg), 0); + Msg.Msg_Info := 'ϢϢ'; + Msg.ID := '0001'; + Msg.Sender_Name := 'RecordToJson Test'; + Json := JSONObject.Create; + try + Json.PutRecord('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 0000000..caf200b Binary files /dev/null and b/demo/YxdJson/YxdJson_VS_QJson/jsondemo.res differ diff --git a/demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.dproj b/demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.dproj new file mode 100644 index 0000000..a85d047 --- /dev/null +++ b/demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.dproj @@ -0,0 +1,58 @@ + + + {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 0000000..9591d43 Binary files /dev/null and b/demo/YxdJson/YxdJson_VS_QJson/jsondemo_2007.res differ 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 0000000..b1c1298 Binary files /dev/null and b/demo/YxdJson/YxdJson_VS_QJson/jsondemo_Icon.ico differ 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 0000000..84f8698 Binary files /dev/null and b/source/pcre/pcre_compile.obj differ diff --git a/source/pcre/pcre_config.obj b/source/pcre/pcre_config.obj new file mode 100644 index 0000000..4c8fd32 Binary files /dev/null and b/source/pcre/pcre_config.obj differ diff --git a/source/pcre/pcre_default_tables.obj b/source/pcre/pcre_default_tables.obj new file mode 100644 index 0000000..4b24e0c Binary files /dev/null and b/source/pcre/pcre_default_tables.obj differ diff --git a/source/pcre/pcre_dfa_exec.obj b/source/pcre/pcre_dfa_exec.obj new file mode 100644 index 0000000..d628a61 Binary files /dev/null and b/source/pcre/pcre_dfa_exec.obj differ diff --git a/source/pcre/pcre_exec.obj b/source/pcre/pcre_exec.obj new file mode 100644 index 0000000..94de74a Binary files /dev/null and b/source/pcre/pcre_exec.obj differ diff --git a/source/pcre/pcre_fullinfo.obj b/source/pcre/pcre_fullinfo.obj new file mode 100644 index 0000000..6a7ff4f Binary files /dev/null and b/source/pcre/pcre_fullinfo.obj differ diff --git a/source/pcre/pcre_get.obj b/source/pcre/pcre_get.obj new file mode 100644 index 0000000..f42080d Binary files /dev/null and b/source/pcre/pcre_get.obj differ diff --git a/source/pcre/pcre_globals.obj b/source/pcre/pcre_globals.obj new file mode 100644 index 0000000..062c13e Binary files /dev/null and b/source/pcre/pcre_globals.obj differ diff --git a/source/pcre/pcre_info.obj b/source/pcre/pcre_info.obj new file mode 100644 index 0000000..fb7438d Binary files /dev/null and b/source/pcre/pcre_info.obj differ diff --git a/source/pcre/pcre_maketables.obj b/source/pcre/pcre_maketables.obj new file mode 100644 index 0000000..68658f0 Binary files /dev/null and b/source/pcre/pcre_maketables.obj differ diff --git a/source/pcre/pcre_newline.obj b/source/pcre/pcre_newline.obj new file mode 100644 index 0000000..f710524 Binary files /dev/null and b/source/pcre/pcre_newline.obj differ diff --git a/source/pcre/pcre_ord2utf8.obj b/source/pcre/pcre_ord2utf8.obj new file mode 100644 index 0000000..d1556f6 Binary files /dev/null and b/source/pcre/pcre_ord2utf8.obj differ diff --git a/source/pcre/pcre_refcount.obj b/source/pcre/pcre_refcount.obj new file mode 100644 index 0000000..e9cdda3 Binary files /dev/null and b/source/pcre/pcre_refcount.obj differ diff --git a/source/pcre/pcre_study.obj b/source/pcre/pcre_study.obj new file mode 100644 index 0000000..e1f28cf Binary files /dev/null and b/source/pcre/pcre_study.obj differ diff --git a/source/pcre/pcre_tables.obj b/source/pcre/pcre_tables.obj new file mode 100644 index 0000000..94fa034 Binary files /dev/null and b/source/pcre/pcre_tables.obj differ diff --git a/source/pcre/pcre_try_flipped.obj b/source/pcre/pcre_try_flipped.obj new file mode 100644 index 0000000..5795787 Binary files /dev/null and b/source/pcre/pcre_try_flipped.obj differ diff --git a/source/pcre/pcre_ucd.obj b/source/pcre/pcre_ucd.obj new file mode 100644 index 0000000..77562db Binary files /dev/null and b/source/pcre/pcre_ucd.obj differ diff --git a/source/pcre/pcre_valid_utf8.obj b/source/pcre/pcre_valid_utf8.obj new file mode 100644 index 0000000..b6537d0 Binary files /dev/null and b/source/pcre/pcre_valid_utf8.obj differ diff --git a/source/pcre/pcre_version.obj b/source/pcre/pcre_version.obj new file mode 100644 index 0000000..0452f62 Binary files /dev/null and b/source/pcre/pcre_version.obj differ diff --git a/source/pcre/pcre_xclass.obj b/source/pcre/pcre_xclass.obj new file mode 100644 index 0000000..627e282 Binary files /dev/null and b/source/pcre/pcre_xclass.obj differ