From 81dc7ea4ea3513f153200a802ede2248befcf053 Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Thu, 24 Nov 2016 08:58:55 -0500 Subject: [PATCH] Embeddable: Fix if_exists (restore) --- 100_core/.classpath | 10 + 100_core/src/gplx/Array_.java | 117 ++ 100_core/src/gplx/Array__tst.java | 41 + 100_core/src/gplx/Bool_.java | 64 + 100_core/src/gplx/Bool__tst.java | 31 + 100_core/src/gplx/Bry_.java | 1061 +++++++++++++++++ 100_core/src/gplx/Bry__tst.java | 295 +++++ 100_core/src/gplx/Bry_bfr.java | 654 ++++++++++ 100_core/src/gplx/Bry_bfr_.java | 45 + 100_core/src/gplx/Bry_bfr_tst.java | 236 ++++ 100_core/src/gplx/Bry_find_.java | 365 ++++++ 100_core/src/gplx/Bry_find__tst.java | 77 ++ 100_core/src/gplx/Bry_fmt.java | 88 ++ 100_core/src/gplx/Bry_split_.java | 136 +++ 100_core/src/gplx/Bry_split__tst.java | 71 ++ 100_core/src/gplx/Byte_.java | 63 + 100_core/src/gplx/Byte__tst.java | 35 + 100_core/src/gplx/Byte_ascii.java | 129 ++ 100_core/src/gplx/Cancelable.java | 22 + 100_core/src/gplx/Cancelable_.java | 31 + 100_core/src/gplx/Char_.java | 73 ++ 100_core/src/gplx/CompareAble.java | 20 + 100_core/src/gplx/CompareAble_.java | 78 ++ 100_core/src/gplx/CompareAble_tst.java | 38 + 100_core/src/gplx/DateAdp.java | 153 +++ 100_core/src/gplx/DateAdp_.java | 126 ++ 100_core/src/gplx/DateAdp__tst.java | 88 ++ 100_core/src/gplx/Datetime_now.java | 69 ++ 100_core/src/gplx/Decimal_adp.java | 92 ++ 100_core/src/gplx/Decimal_adp_.java | 70 ++ 100_core/src/gplx/Decimal_adp__tst.java | 88 ++ 100_core/src/gplx/Double_.java | 54 + 100_core/src/gplx/Double__tst.java | 29 + 100_core/src/gplx/Enm_.java | 22 + 100_core/src/gplx/Err.java | 87 ++ 100_core/src/gplx/Err_.java | 76 ++ 100_core/src/gplx/Err_tst.java | 47 + 100_core/src/gplx/Float_.java | 41 + 100_core/src/gplx/GfoMsg.java | 72 ++ 100_core/src/gplx/GfoMsgUtl.java | 25 + 100_core/src/gplx/GfoMsg_.java | 270 +++++ 100_core/src/gplx/GfoMsg_tst.java | 51 + 100_core/src/gplx/GfoTemplate.java | 21 + 100_core/src/gplx/GfoTemplateFactory.java | 32 + 100_core/src/gplx/Gfo_evt_itm.java | 19 + 100_core/src/gplx/Gfo_evt_mgr.java | 126 ++ 100_core/src/gplx/Gfo_evt_mgr_.java | 45 + 100_core/src/gplx/Gfo_evt_mgr_owner.java | 21 + 100_core/src/gplx/Gfo_evt_mgr_tst.java | 69 ++ 100_core/src/gplx/Gfo_invk.java | 28 + 100_core/src/gplx/Gfo_invk_.java | 43 + 100_core/src/gplx/Gfo_invk_cmd.java | 32 + 100_core/src/gplx/Gfo_invk_cmd_mgr.java | 68 ++ 100_core/src/gplx/Gfo_invk_cmd_mgr_owner.java | 21 + 100_core/src/gplx/Gfo_invk_root_wkr.java | 21 + 100_core/src/gplx/Gfo_invk_to_str.java | 44 + 100_core/src/gplx/Gfo_log.java | 27 + 100_core/src/gplx/Gfo_log_.java | 36 + 100_core/src/gplx/Gfo_log_bfr.java | 29 + 100_core/src/gplx/Gfo_usr_dlg.java | 35 + 100_core/src/gplx/Gfo_usr_dlg_.java | 74 ++ 100_core/src/gplx/Gfo_usr_dlg__gui.java | 27 + 100_core/src/gplx/Gfo_usr_dlg__gui_.java | 52 + 100_core/src/gplx/Gfo_usr_dlg__gui_test.java | 29 + 100_core/src/gplx/Gfo_usr_dlg__log.java | 31 + 100_core/src/gplx/Gfo_usr_dlg__log_.java | 35 + 100_core/src/gplx/Gfo_usr_dlg__log_base.java | 123 ++ 100_core/src/gplx/Gfo_usr_dlg_base.java | 66 + 100_core/src/gplx/GfsCtx.java | 65 + 100_core/src/gplx/Guid_adp.java | 22 + 100_core/src/gplx/Guid_adp_.java | 25 + 100_core/src/gplx/Guid_adp__tst.java | 28 + 100_core/src/gplx/Hash_adp.java | 30 + 100_core/src/gplx/Hash_adp_.java | 37 + 100_core/src/gplx/Hash_adp_bry.java | 207 ++++ 100_core/src/gplx/Hash_adp_bry_tst.java | 68 ++ 100_core/src/gplx/Int_.java | 289 +++++ 100_core/src/gplx/Int__tst.java | 115 ++ 100_core/src/gplx/Internal.java | 19 + 100_core/src/gplx/Io_mgr.java | 178 +++ 100_core/src/gplx/Io_mgr__tst.java | 100 ++ 100_core/src/gplx/Io_url.java | 91 ++ 100_core/src/gplx/Io_url_.java | 113 ++ 100_core/src/gplx/Io_url__tst.java | 32 + 100_core/src/gplx/Keyval.java | 32 + 100_core/src/gplx/Keyval_.java | 103 ++ 100_core/src/gplx/Keyval_hash.java | 41 + 100_core/src/gplx/Keyval_list.java | 36 + 100_core/src/gplx/List_adp.java | 77 ++ 100_core/src/gplx/List_adp_.java | 55 + 100_core/src/gplx/List_adp_base.java | 175 +++ 100_core/src/gplx/List_adp_tst.java | 222 ++++ 100_core/src/gplx/Long_.java | 112 ++ 100_core/src/gplx/Long__tst.java | 49 + 100_core/src/gplx/Math_.java | 73 ++ 100_core/src/gplx/Math__tst.java | 61 + 100_core/src/gplx/New.java | 19 + 100_core/src/gplx/ObjAry.java | 36 + 100_core/src/gplx/Object_.java | 68 ++ 100_core/src/gplx/Object__tst.java | 36 + 100_core/src/gplx/Ordered_hash.java | 30 + 100_core/src/gplx/Ordered_hash_.java | 30 + 100_core/src/gplx/Ordered_hash_base.java | 95 ++ 100_core/src/gplx/Ordered_hash_tst.java | 39 + 100_core/src/gplx/RandomAdp.java | 24 + 100_core/src/gplx/RandomAdp_.java | 30 + 100_core/src/gplx/Rls_able.java | 21 + 100_core/src/gplx/Rls_able_.java | 26 + 100_core/src/gplx/Short_.java | 23 + 100_core/src/gplx/String_.java | 563 +++++++++ 100_core/src/gplx/String__tst.java | 184 +++ 100_core/src/gplx/Tfds.java | 236 ++++ 100_core/src/gplx/TfdsTstr_fxt.java | 99 ++ 100_core/src/gplx/Time_span.java | 83 ++ 100_core/src/gplx/Time_span_.java | 163 +++ 100_core/src/gplx/To_str_able.java | 21 + 100_core/src/gplx/To_str_able_.java | 21 + 100_core/src/gplx/Type_adp_.java | 81 ++ 100_core/src/gplx/UsrDlg.java | 51 + 100_core/src/gplx/UsrDlg_.java | 21 + 100_core/src/gplx/UsrMsg.java | 68 ++ 100_core/src/gplx/UsrMsgWkr.java | 50 + 100_core/src/gplx/UsrMsgWkr_.java | 27 + 100_core/src/gplx/UsrMsgWkr_console.java | 35 + 100_core/src/gplx/UsrMsgWkr_test.java | 38 + 100_core/src/gplx/Virtual.java | 19 + 100_core/src/gplx/Yn.java | 79 ++ 100_core/src/gplx/core/bits/Bitmask_.java | 40 + 100_core/src/gplx/core/brys/Bfr_arg.java | 21 + 100_core/src/gplx/core/brys/Bfr_arg_.java | 35 + .../src/gplx/core/brys/Bfr_arg_clearable.java | 22 + 100_core/src/gplx/core/brys/Bry_bfr_able.java | 21 + .../src/gplx/core/brys/Bry_bfr_able_.java | 37 + 100_core/src/gplx/core/brys/Bry_bfr_mkr.java | 42 + .../src/gplx/core/brys/Bry_bfr_mkr_mgr.java | 100 ++ .../src/gplx/core/brys/Bry_bfr_mkr_tst.java | 57 + 100_core/src/gplx/core/brys/Bry_err_wkr.java | 53 + 100_core/src/gplx/core/brys/Bry_rdr.java | 231 ++++ 100_core/src/gplx/core/brys/Bry_rdr_old.java | 169 +++ 100_core/src/gplx/core/brys/Bry_rdr_tst.java | 56 + .../src/gplx/core/brys/Bry_split_wkr.java | 21 + .../src/gplx/core/brys/args/Bfr_arg__bry.java | 43 + .../gplx/core/brys/args/Bfr_arg__bry_ary.java | 26 + .../gplx/core/brys/args/Bfr_arg__bry_fmt.java | 38 + .../core/brys/args/Bfr_arg__bry_fmtr.java | 28 + .../gplx/core/brys/args/Bfr_arg__byte.java | 23 + .../core/brys/args/Bfr_arg__decimal_int.java | 25 + .../src/gplx/core/brys/args/Bfr_arg__int.java | 28 + .../gplx/core/brys/args/Bfr_arg__time.java | 55 + .../core/brys/args/Bfr_arg__time_tst.java | 42 + .../src/gplx/core/brys/fmtrs/Bry_fmtr.java | 266 +++++ .../core/brys/fmtrs/Bry_fmtr_eval_mgr.java | 22 + .../core/brys/fmtrs/Bry_fmtr_eval_mgr_.java | 27 + .../brys/fmtrs/Bry_fmtr_eval_mgr_gfs.java | 26 + .../gplx/core/brys/fmtrs/Bry_fmtr_itm.java | 33 + .../gplx/core/brys/fmtrs/Bry_fmtr_tst.java | 74 ++ .../gplx/core/brys/fmtrs/Bry_fmtr_vals.java | 32 + .../src/gplx/core/brys/fmts/Bfr_fmt_arg.java | 24 + .../src/gplx/core/brys/fmts/Bry_fmt_itm.java | 32 + .../gplx/core/brys/fmts/Bry_fmt_parser_.java | 93 ++ .../src/gplx/core/brys/fmts/Bry_fmt_tst.java | 52 + .../core/brys/fmts/Bry_keys_parser_tst.java | 32 + .../src/gplx/core/btries/Btrie_bwd_mgr.java | 119 ++ .../gplx/core/btries/Btrie_bwd_mgr_tst.java | 87 ++ .../src/gplx/core/btries/Btrie_fast_mgr.java | 194 +++ .../gplx/core/btries/Btrie_fast_mgr_tst.java | 85 ++ .../src/gplx/core/btries/Btrie_itm_stub.java | 23 + 100_core/src/gplx/core/btries/Btrie_mgr.java | 25 + 100_core/src/gplx/core/btries/Btrie_rv.java | 28 + .../src/gplx/core/btries/Btrie_slim_itm.java | 132 ++ .../gplx/core/btries/Btrie_slim_itm_tst.java | 49 + .../src/gplx/core/btries/Btrie_slim_mgr.java | 206 ++++ .../gplx/core/btries/Btrie_slim_mgr_tst.java | 92 ++ .../src/gplx/core/btries/Btrie_u8_itm.java | 68 ++ .../src/gplx/core/btries/Btrie_u8_mgr.java | 99 ++ .../src/gplx/core/consoles/Console_adp.java | 28 + .../src/gplx/core/consoles/Console_adp_.java | 32 + .../gplx/core/consoles/Console_adp__mem.java | 50 + .../gplx/core/consoles/Console_adp__sys.java | 67 ++ .../src/gplx/core/criterias/Criteria.java | 24 + .../src/gplx/core/criterias/Criteria_.java | 53 + .../gplx/core/criterias/Criteria_between.java | 40 + .../core/criterias/Criteria_bool_base.java | 48 + .../gplx/core/criterias/Criteria_comp.java | 35 + .../src/gplx/core/criterias/Criteria_eq.java | 34 + .../src/gplx/core/criterias/Criteria_fld.java | 71 ++ .../src/gplx/core/criterias/Criteria_in.java | 48 + .../core/criterias/Criteria_ioItm_tst.java | 50 + .../gplx/core/criterias/Criteria_ioMatch.java | 37 + .../gplx/core/criterias/Criteria_like.java | 34 + .../src/gplx/core/criterias/Criteria_not.java | 27 + .../src/gplx/core/criterias/Criteria_tst.java | 92 ++ 100_core/src/gplx/core/encoders/B85_fp_.java | 56 + .../src/gplx/core/encoders/B85_fp__tst.java | 31 + 100_core/src/gplx/core/encoders/Base85_.java | 66 + .../src/gplx/core/encoders/Base85__tst.java | 61 + .../src/gplx/core/encoders/Gfo_hzip_int_.java | 63 + 100_core/src/gplx/core/encoders/Hex_utl_.java | 143 +++ .../src/gplx/core/encoders/Hex_utl__tst.java | 70 ++ 100_core/src/gplx/core/envs/Env_.java | 48 + 100_core/src/gplx/core/envs/Jar_adp_.java | 33 + 100_core/src/gplx/core/envs/Op_sys.java | 96 ++ 100_core/src/gplx/core/envs/Op_sys_.java | 34 + 100_core/src/gplx/core/envs/Process_adp.java | 379 ++++++ .../src/gplx/core/envs/Process_adp_tst.java | 32 + 100_core/src/gplx/core/envs/Runtime_.java | 25 + 100_core/src/gplx/core/envs/System_.java | 63 + 100_core/src/gplx/core/errs/Err_msg.java | 48 + 100_core/src/gplx/core/gfo_ndes/GfoFld.java | 29 + .../src/gplx/core/gfo_ndes/GfoFldList.java | 28 + .../src/gplx/core/gfo_ndes/GfoFldList_.java | 63 + 100_core/src/gplx/core/gfo_ndes/GfoNde.java | 72 ++ .../src/gplx/core/gfo_ndes/GfoNdeFxt.java | 36 + .../src/gplx/core/gfo_ndes/GfoNdeList.java | 27 + .../src/gplx/core/gfo_ndes/GfoNdeList_.java | 40 + 100_core/src/gplx/core/gfo_ndes/GfoNde_.java | 47 + .../src/gplx/core/gfo_regys/GfoMsgParser.java | 21 + 100_core/src/gplx/core/gfo_regys/GfoRegy.java | 95 ++ .../src/gplx/core/gfo_regys/GfoRegyItm.java | 31 + .../core/gfo_regys/GfoRegy_RegDir_tst.java | 61 + .../core/gfo_regys/GfoRegy_basic_tst.java | 31 + .../src/gplx/core/interfaces/InjectAble.java | 21 + .../src/gplx/core/interfaces/ParseAble.java | 21 + .../src/gplx/core/interfaces/SrlAble.java | 21 + .../src/gplx/core/interfaces/SrlAble_.java | 65 + .../gplx/core/interfaces/SrlAble__tst.java | 66 + .../src/gplx/core/intls/Gfo_case_itm.java | 24 + .../src/gplx/core/intls/Gfo_case_mgr.java | 22 + .../src/gplx/core/intls/Gfo_case_mgr_.java | 21 + 100_core/src/gplx/core/intls/Utf16_.java | 137 +++ 100_core/src/gplx/core/intls/Utf16__tst.java | 59 + 100_core/src/gplx/core/intls/Utf8_.java | 117 ++ 100_core/src/gplx/core/intls/Utf8__tst.java | 69 ++ 100_core/src/gplx/core/ios/IoEngine.java | 156 +++ 100_core/src/gplx/core/ios/IoEngineFxt.java | 54 + 100_core/src/gplx/core/ios/IoEnginePool.java | 34 + 100_core/src/gplx/core/ios/IoEngine_.java | 30 + 100_core/src/gplx/core/ios/IoEngine_base.java | 59 + .../src/gplx/core/ios/IoEngine_memory.java | 203 ++++ .../src/gplx/core/ios/IoEngine_system.java | 740 ++++++++++++ .../gplx/core/ios/IoEngine_xrg_deleteDir.java | 34 + .../gplx/core/ios/IoEngine_xrg_deleteFil.java | 30 + .../core/ios/IoEngine_xrg_downloadFil.java | 71 ++ .../ios/IoEngine_xrg_fil_affects1_base.java | 25 + .../core/ios/IoEngine_xrg_loadFilStr.java | 44 + .../gplx/core/ios/IoEngine_xrg_openRead.java | 36 + .../gplx/core/ios/IoEngine_xrg_openWrite.java | 32 + .../gplx/core/ios/IoEngine_xrg_queryDir.java | 55 + .../core/ios/IoEngine_xrg_recycleFil.java | 59 + .../core/ios/IoEngine_xrg_saveFilStr.java | 33 + .../gplx/core/ios/IoEngine_xrg_xferDir.java | 37 + .../gplx/core/ios/IoEngine_xrg_xferFil.java | 34 + 100_core/src/gplx/core/ios/IoErr.java | 30 + 100_core/src/gplx/core/ios/IoItmAttrib.java | 25 + 100_core/src/gplx/core/ios/IoItmClassXtn.java | 33 + 100_core/src/gplx/core/ios/IoItmDir.java | 71 ++ 100_core/src/gplx/core/ios/IoItmDir_.java | 55 + 100_core/src/gplx/core/ios/IoItmFil.java | 43 + 100_core/src/gplx/core/ios/IoItmFil_.java | 31 + 100_core/src/gplx/core/ios/IoItmFil_mem.java | 39 + 100_core/src/gplx/core/ios/IoItmHash.java | 43 + 100_core/src/gplx/core/ios/IoItmList.java | 76 ++ 100_core/src/gplx/core/ios/IoItm_base.java | 54 + 100_core/src/gplx/core/ios/IoItm_base_.java | 26 + 100_core/src/gplx/core/ios/IoItm_fxt.java | 34 + 100_core/src/gplx/core/ios/IoRecycleBin.java | 60 + 100_core/src/gplx/core/ios/IoUrlInfo.java | 245 ++++ 100_core/src/gplx/core/ios/IoUrlInfoRegy.java | 54 + 100_core/src/gplx/core/ios/IoUrlInfo_.java | 34 + 100_core/src/gplx/core/ios/IoUrlTypeRegy.java | 76 ++ 100_core/src/gplx/core/ios/IoZipWkr.java | 41 + 100_core/src/gplx/core/ios/IoZipWkr_tst.java | 28 + .../src/gplx/core/ios/Io_download_fmt.java | 94 ++ .../gplx/core/ios/Io_download_fmt_tst.java | 73 ++ 100_core/src/gplx/core/ios/Io_fil.java | 38 + 100_core/src/gplx/core/ios/Io_fil_mkr.java | 24 + 100_core/src/gplx/core/ios/Io_size_.java | 172 +++ 100_core/src/gplx/core/ios/Io_size__tst.java | 85 ++ .../src/gplx/core/ios/Io_url_obj_ref.java | 30 + .../src/gplx/core/ios/drives/Io_drive.java | 22 + .../src/gplx/core/ios/drives/Io_drive_.java | 21 + .../gplx/core/ios/drives/Io_drive__jre.java | 22 + .../src/gplx/core/ios/loaders/Io_loader.java | 21 + .../src/gplx/core/ios/streams/IoStream.java | 33 + .../src/gplx/core/ios/streams/IoStream_.java | 64 + .../gplx/core/ios/streams/IoStream_base.java | 138 +++ .../gplx/core/ios/streams/IoStream_mem.java | 70 ++ .../core/ios/streams/IoStream_mem_tst.java | 30 + .../gplx/core/ios/streams/IoStream_mock.java | 45 + .../core/ios/streams/IoStream_mock_tst.java | 48 + .../core/ios/streams/IoStream_stream_rdr.java | 39 + .../gplx/core/ios/streams/Io_stream_rdr.java | 30 + .../gplx/core/ios/streams/Io_stream_rdr_.java | 102 ++ .../core/ios/streams/Io_stream_rdr__tst.java | 56 + .../gplx/core/ios/streams/Io_stream_tid_.java | 58 + .../gplx/core/ios/streams/Io_stream_wtr.java | 28 + .../gplx/core/ios/streams/Io_stream_wtr_.java | 68 ++ .../ios/streams/rdrs/Io_stream_rdr__adp.java | 41 + .../ios/streams/rdrs/Io_stream_rdr__base.java | 48 + .../streams/rdrs/Io_stream_rdr__bzip2.java | 29 + .../ios/streams/rdrs/Io_stream_rdr__gzip.java | 44 + .../ios/streams/rdrs/Io_stream_rdr__noop.java | 30 + .../ios/streams/rdrs/Io_stream_rdr__raw.java | 37 + .../ios/streams/rdrs/Io_stream_rdr__xz.java | 25 + .../ios/streams/rdrs/Io_stream_rdr__zip.java | 54 + .../ios/streams/wtrs/Io_stream_wtr__base.java | 60 + .../streams/wtrs/Io_stream_wtr__bzip2.java | 25 + .../ios/streams/wtrs/Io_stream_wtr__gzip.java | 25 + .../ios/streams/wtrs/Io_stream_wtr__raw.java | 55 + .../ios/streams/wtrs/Io_stream_wtr__xz.java | 25 + .../ios/streams/wtrs/Io_stream_wtr__zip.java | 68 ++ .../ios/zips/Io_zip_compress_cmd__jre.java | 74 ++ .../core/ios/zips/Io_zip_decompress_cmd.java | 26 + .../core/ios/zips/Io_zip_decompress_cmd_.java | 21 + .../ios/zips/Io_zip_decompress_cmd__base.java | 74 ++ .../ios/zips/Io_zip_decompress_cmd__jre.java | 106 ++ .../src/gplx/core/ios/zips/Io_zip_mgr.java | 25 + .../gplx/core/ios/zips/Io_zip_mgr_base.java | 120 ++ .../gplx/core/ios/zips/Io_zip_mgr_mok.java | 36 + .../gplx/core/ios/zips/Io_zip_mgr_tst.java | 31 + 100_core/src/gplx/core/js/Js_wtr.java | 107 ++ 100_core/src/gplx/core/js/Js_wtr_tst.java | 41 + .../src/gplx/core/lists/ComparerAble.java | 20 + .../src/gplx/core/lists/ComparerAble_.java | 21 + 100_core/src/gplx/core/lists/EnumerAble.java | 19 + .../src/gplx/core/lists/Hash_adp_base.java | 50 + .../src/gplx/core/lists/Hash_adp_list.java | 40 + .../src/gplx/core/lists/Iterator_null.java | 24 + .../src/gplx/core/lists/Iterator_objAry.java | 25 + .../gplx/core/lists/List_adp__getable.java | 22 + .../src/gplx/core/lists/List_adp_sorter.java | 65 + .../gplx/core/lists/List_adp_sorter_tst.java | 37 + 100_core/src/gplx/core/lists/StackAdp.java | 26 + 100_core/src/gplx/core/lists/StackAdp_.java | 41 + .../src/gplx/core/lists/StackAdp_tst.java | 33 + .../src/gplx/core/lists/rings/Ring__long.java | 54 + .../core/lists/rings/Ring__long__tst.java | 41 + .../gplx/core/lists/rings/Ring__string.java | 60 + .../core/lists/rings/Ring__string__tst.java | 46 + .../src/gplx/core/log_msgs/Gfo_msg_data.java | 34 + .../src/gplx/core/log_msgs/Gfo_msg_grp.java | 46 + .../src/gplx/core/log_msgs/Gfo_msg_grp_.java | 30 + .../src/gplx/core/log_msgs/Gfo_msg_itm.java | 58 + .../src/gplx/core/log_msgs/Gfo_msg_itm_.java | 27 + .../src/gplx/core/log_msgs/Gfo_msg_log.java | 60 + .../src/gplx/core/log_msgs/Gfo_msg_obj.java | 22 + .../src/gplx/core/log_msgs/Gfo_msg_root.java | 80 ++ .../gplx/core/log_msgs/Gfo_msg_root_tst.java | 62 + .../src/gplx/core/logs/Gfo_log__base.java | 44 + .../src/gplx/core/logs/Gfo_log__file.java | 63 + 100_core/src/gplx/core/logs/Gfo_log__mem.java | 26 + 100_core/src/gplx/core/logs/Gfo_log_itm.java | 34 + .../src/gplx/core/logs/Gfo_log_itm_wtr.java | 21 + .../gplx/core/logs/Gfo_log_itm_wtr__csv.java | 70 ++ .../src/gplx/core/memorys/Gfo_memory_itm.java | 21 + .../src/gplx/core/memorys/Gfo_memory_mgr.java | 33 + .../gplx/core/primitives/Bool_obj_ref.java | 36 + .../gplx/core/primitives/Bool_obj_val.java | 34 + .../src/gplx/core/primitives/Bry_obj_ref.java | 45 + .../gplx/core/primitives/Byte_obj_ref.java | 31 + .../gplx/core/primitives/Byte_obj_val.java | 29 + .../gplx/core/primitives/Double_obj_val.java | 32 + 100_core/src/gplx/core/primitives/EnmMgr.java | 72 ++ .../gplx/core/primitives/EnmParser_tst.java | 60 + .../src/gplx/core/primitives/Int_ary_.java | 95 ++ .../gplx/core/primitives/Int_ary__tst.java | 44 + .../src/gplx/core/primitives/Int_list.java | 44 + .../src/gplx/core/primitives/Int_obj_ref.java | 37 + .../src/gplx/core/primitives/Int_obj_val.java | 26 + .../gplx/core/primitives/String_obj_ref.java | 30 + .../gplx/core/primitives/String_obj_val.java | 27 + 100_core/src/gplx/core/progs/Gfo_prog_ui.java | 26 + .../src/gplx/core/progs/Gfo_prog_ui_.java | 49 + .../src/gplx/core/progs/Gfo_resume_wkr.java | 53 + .../src/gplx/core/security/Hash_algo.java | 25 + .../src/gplx/core/security/Hash_algo_.java | 147 +++ .../core/security/Hash_algo__md5__tst.java | 41 + .../core/security/Hash_algo__sha1__tst.java | 33 + .../security/Hash_algo__sha2_256__tst.java | 33 + .../core/security/Hash_algo__tth_192.java | 1007 ++++++++++++++++ .../security/Hash_algo__tth_192__tst.java | 36 + .../security/Hash_algo__tth_192_tree_tst.java | 65 + .../core/security/Hash_console_wtr_tst.java | 41 + 100_core/src/gplx/core/stores/DataRdr.java | 51 + 100_core/src/gplx/core/stores/DataRdr_.java | 74 ++ .../src/gplx/core/stores/DataRdr_base.java | 215 ++++ .../src/gplx/core/stores/DataRdr_mem.java | 79 ++ 100_core/src/gplx/core/stores/DataWtr.java | 33 + 100_core/src/gplx/core/stores/DataWtr_.java | 48 + .../src/gplx/core/stores/DataWtr_base.java | 52 + 100_core/src/gplx/core/stores/GfoNdeRdr.java | 22 + 100_core/src/gplx/core/stores/GfoNdeRdr_.java | 49 + 100_core/src/gplx/core/stores/SrlMgr.java | 35 + 100_core/src/gplx/core/stores/SrlObj.java | 22 + .../src/gplx/core/stores/xmls/XmlDataRdr.java | 77 ++ .../gplx/core/stores/xmls/XmlDataRdr_.java | 25 + .../gplx/core/stores/xmls/XmlDataWtr_.java | 113 ++ .../src/gplx/core/strings/String_bldr.java | 116 ++ .../src/gplx/core/strings/String_bldr_.java | 22 + 100_core/src/gplx/core/tests/Gftest.java | 212 ++++ .../src/gplx/core/tests/PerfLogMgr_fxt.java | 66 + .../src/gplx/core/tests/TfdsEqListItmStr.java | 21 + .../src/gplx/core/texts/Base32Converter.java | 99 ++ .../src/gplx/core/texts/Base64Converter.java | 79 ++ .../gplx/core/texts/BaseXXConverter_tst.java | 58 + 100_core/src/gplx/core/texts/CharStream.java | 67 ++ .../src/gplx/core/texts/CharStream_tst.java | 61 + .../gplx/core/texts/RegxPatn_cls_ioMatch.java | 35 + .../core/texts/RegxPatn_cls_ioMatch_.java | 53 + .../core/texts/RegxPatn_cls_ioMatch_tst.java | 58 + .../gplx/core/texts/RegxPatn_cls_like.java | 32 + .../gplx/core/texts/RegxPatn_cls_like_.java | 63 + .../core/texts/RegxPatn_cls_like_tst.java | 86 ++ .../src/gplx/core/texts/StringTableBldr.java | 57 + .../gplx/core/texts/StringTableBldr_tst.java | 59 + .../src/gplx/core/texts/StringTableCol.java | 39 + .../gplx/core/texts/StringTableColAlign.java | 29 + 100_core/src/gplx/core/threads/Gfo_lock.java | 28 + .../src/gplx/core/threads/Thread_adp.java | 46 + .../src/gplx/core/threads/Thread_adp_.java | 34 + .../src/gplx/core/threads/Thread_adp_mgr.java | 61 + .../gplx/core/threads/Thread_halt_cbk.java | 21 + .../gplx/core/threads/Thread_halt_cbk_.java | 24 + .../gplx/core/threads/Thread_halt_wkr.java | 63 + .../threads/poolables/Gfo_poolable_itm.java | 22 + .../threads/poolables/Gfo_poolable_mgr.java | 98 ++ .../threads/poolables/Gfo_poolable_mgr_.java | 22 + .../poolables/Gfo_poolable_mgr_tst.java | 83 ++ .../threads/utils/Gfo_blocking_queue.java | 35 + .../threads/utils/Gfo_countdown_latch.java | 32 + .../src/gplx/core/times/DateAdp_parser.java | 120 ++ .../gplx/core/times/DateAdp_parser_tst.java | 34 + .../gplx/core/times/Time_span__basic_tst.java | 87 ++ .../gplx/core/times/Time_span__parse_tst.java | 54 + .../core/times/Time_span__to_str_tst.java | 59 + .../src/gplx/core/type_xtns/BoolClassXtn.java | 41 + .../src/gplx/core/type_xtns/ByteClassXtn.java | 28 + .../src/gplx/core/type_xtns/ClassXtn.java | 29 + .../src/gplx/core/type_xtns/ClassXtnPool.java | 39 + .../gplx/core/type_xtns/ClassXtn_base.java | 28 + .../gplx/core/type_xtns/DateAdpClassXtn.java | 28 + .../core/type_xtns/DateAdpClassXtn_tst.java | 28 + .../core/type_xtns/DecimalAdpClassXtn.java | 27 + .../gplx/core/type_xtns/DoubleClassXtn.java | 26 + .../gplx/core/type_xtns/FloatClassXtn.java | 26 + .../src/gplx/core/type_xtns/IntClassXtn.java | 28 + .../gplx/core/type_xtns/IoUrlClassXtn.java | 29 + .../src/gplx/core/type_xtns/LongClassXtn.java | 27 + .../gplx/core/type_xtns/ObjectClassXtn.java | 27 + .../gplx/core/type_xtns/StringClassXtn.java | 28 + .../core/type_xtns/TimeSpanAdpClassXtn.java | 28 + .../src/gplx/langs/dsvs/DsvDataRdrOpts.java | 26 + 100_core/src/gplx/langs/dsvs/DsvDataRdr_.java | 248 ++++ .../langs/dsvs/DsvDataRdr_csv_dat_tst.java | 216 ++++ .../langs/dsvs/DsvDataRdr_dsv_dat_tst.java | 70 ++ .../langs/dsvs/DsvDataRdr_dsv_hdr_tst.java | 82 ++ .../langs/dsvs/DsvDataRdr_dsv_misc_tst.java | 76 ++ .../langs/dsvs/DsvDataRdr_layout_tst.java | 131 ++ 100_core/src/gplx/langs/dsvs/DsvDataWtr.java | 115 ++ 100_core/src/gplx/langs/dsvs/DsvDataWtr_.java | 31 + .../gplx/langs/dsvs/DsvDataWtr_csv_tst.java | 100 ++ .../gplx/langs/dsvs/DsvDataWtr_tbls_tst.java | 73 ++ .../src/gplx/langs/dsvs/DsvHeaderList.java | 44 + .../src/gplx/langs/dsvs/DsvStoreLayout.java | 51 + 100_core/src/gplx/langs/dsvs/DsvSymbols.java | 52 + 100_core/src/gplx/langs/gfs/GfsCore.java | 90 ++ 100_core/src/gplx/langs/gfs/GfsCoreHelp.java | 73 ++ 100_core/src/gplx/langs/gfs/GfsCore_.java | 96 ++ 100_core/src/gplx/langs/gfs/GfsCore_tst.java | 113 ++ 100_core/src/gplx/langs/gfs/GfsLibIni.java | 21 + .../src/gplx/langs/gfs/GfsLibIni_core.java | 36 + 100_core/src/gplx/langs/gfs/GfsRegy.java | 54 + 100_core/src/gplx/langs/gfs/GfsTypeNames.java | 28 + 100_core/src/gplx/langs/gfs/Gfs_Date_tst.java | 42 + .../langs/htmls/Url_encoder_interface.java | 22 + .../htmls/Url_encoder_interface_same.java | 23 + .../gplx/langs/htmls/entitys/Gfh_entity_.java | 35 + .../langs/htmls/entitys/Gfh_entity_itm.java | 57 + .../langs/htmls/entitys/Gfh_entity_trie.java | 317 +++++ 100_core/src/gplx/langs/regxs/Regx_adp.java | 65 + 100_core/src/gplx/langs/regxs/Regx_adp_.java | 43 + .../src/gplx/langs/regxs/Regx_adp__tst.java | 93 ++ 100_core/src/gplx/langs/regxs/Regx_bldr.java | 62 + 100_core/src/gplx/langs/regxs/Regx_group.java | 26 + 100_core/src/gplx/langs/regxs/Regx_match.java | 28 + 100_core/src/gplx/langs/xmls/HierStrBldr.java | 56 + .../src/gplx/langs/xmls/HierStrBldr_tst.java | 47 + 100_core/src/gplx/langs/xmls/XmlAtr.java | 24 + 100_core/src/gplx/langs/xmls/XmlAtrList.java | 37 + 100_core/src/gplx/langs/xmls/XmlDoc.java | 24 + 100_core/src/gplx/langs/xmls/XmlDoc_.java | 53 + 100_core/src/gplx/langs/xmls/XmlDoc_tst.java | 71 ++ .../src/gplx/langs/xmls/XmlFileSplitter.java | 141 +++ .../gplx/langs/xmls/XmlFileSplitterOpts.java | 27 + .../gplx/langs/xmls/XmlFileSplitter_tst.java | 88 ++ 100_core/src/gplx/langs/xmls/XmlNde.java | 51 + 100_core/src/gplx/langs/xmls/XmlNdeList.java | 35 + 100_core/src/gplx/langs/xmls/XmlSplitRdr.java | 51 + 100_core/src/gplx/langs/xmls/XmlSplitWtr.java | 40 + 100_core/src/gplx/langs/xmls/Xpath_.java | 106 ++ 100_core/src/gplx/langs/xmls/Xpath__tst.java | 44 + 100_core/tst/gplx/GfoMsg_rdr_tst.java | 57 + 100_core/tst/gplx/GfoTreeBldr_fxt.java | 32 + .../core/ios/IoEngine_dir_basic_base.java | 79 ++ .../ios/IoEngine_dir_basic_memory_tst.java | 24 + .../ios/IoEngine_dir_basic_system_tst.java | 28 + .../gplx/core/ios/IoEngine_dir_deep_base.java | 126 ++ .../ios/IoEngine_dir_deep_memory_tst.java | 36 + .../ios/IoEngine_dir_deep_system_tst.java | 25 + .../core/ios/IoEngine_fil_basic_base.java | 176 +++ .../ios/IoEngine_fil_basic_memory_tst.java | 57 + .../ios/IoEngine_fil_basic_system_tst.java | 58 + .../gplx/core/ios/IoEngine_fil_xfer_base.java | 106 ++ .../ios/IoEngine_fil_xfer_memory_tst.java | 28 + .../ios/IoEngine_fil_xfer_system_tst.java | 28 + .../core/ios/IoEngine_stream_xfer_tst.java | 49 + .../core/ios/IoEngine_xrg_queryDir_tst.java | 65 + .../core/ios/IoEngine_xrg_recycleFil_tst.java | 32 + .../ios/IoItmDir_FetchDeepOrNull_tst.java | 40 + .../gplx/core/ios/IoUrlInfo_alias_tst.java | 58 + 100_core/tst/gplx/core/ios/IoUrl_lnx_tst.java | 55 + 100_core/tst/gplx/core/ios/IoUrl_map_tst.java | 31 + 100_core/tst/gplx/core/ios/IoUrl_wnt_tst.java | 98 ++ .../gplx/core/stores/GfoNdeRdr_read_tst.java | 48 + .../tst/gplx/core/stores/GfoNdeRdr_tst.java | 189 +++ .../gplx/core/stores/xmls/XmlDataRdr_tst.java | 102 ++ .../gplx/core/stores/xmls/XmlDataWtr_tst.java | 95 ++ 110_gfml/.classpath | 14 + 110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java | 34 + 110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java | 217 ++++ 110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java | 29 + .../src_100_tkn/gplx/gfml/GfmlObjList.java | 25 + 110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java | 46 + 110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java | 65 + 110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java | 114 ++ .../src_100_tkn/gplx/gfml/IntObjHash.java | 77 ++ 110_gfml/src_200_type/gplx/gfml/GfmlFld.java | 40 + .../src_200_type/gplx/gfml/GfmlFldList.java | 34 + 110_gfml/src_200_type/gplx/gfml/GfmlType.java | 53 + .../gplx/gfml/GfmlTypeCompiler.java | 105 ++ .../src_200_type/gplx/gfml/GfmlTypeHash.java | 49 + .../src_200_type/gplx/gfml/GfmlTypeMakr.java | 67 ++ .../src_200_type/gplx/gfml/GfmlTypeMgr.java | 140 +++ 110_gfml/src_300_gdoc/gplx/gfml/GfmlAtr.java | 76 ++ 110_gfml/src_300_gdoc/gplx/gfml/GfmlDoc.java | 45 + .../src_300_gdoc/gplx/gfml/GfmlDocLxrs.java | 130 ++ .../src_300_gdoc/gplx/gfml/GfmlDocPos.java | 71 ++ .../src_300_gdoc/gplx/gfml/GfmlDocWtr_.java | 40 + 110_gfml/src_300_gdoc/gplx/gfml/GfmlDoc_.java | 46 + 110_gfml/src_300_gdoc/gplx/gfml/GfmlItm.java | 29 + .../src_300_gdoc/gplx/gfml/GfmlItmHnds.java | 24 + .../src_300_gdoc/gplx/gfml/GfmlItmKeys.java | 61 + 110_gfml/src_300_gdoc/gplx/gfml/GfmlNde.java | 113 ++ .../src_300_gdoc/gplx/gfml/GfmlScopeItm.java | 73 ++ .../src_400_pragma/gplx/gfml/GfmlPragma.java | 50 + .../gplx/gfml/GfmlPragmaDefault.java | 133 +++ .../gplx/gfml/GfmlPragmaLxrFrm.java | 64 + .../gplx/gfml/GfmlPragmaLxrSym.java | 56 + .../gplx/gfml/GfmlPragmaType.java | 79 ++ .../gplx/gfml/GfmlPragmaVar.java | 78 ++ .../src_400_pragma/gplx/gfml/GfmlVarCtx.java | 62 + .../src_400_pragma/gplx/gfml/GfmlVarItm.java | 32 + .../src_400_pragma/gplx/gfml/GfmlVarTkn.java | 39 + .../src_500_build/gplx/gfml/GfmlBldr.java | 74 ++ .../src_500_build/gplx/gfml/GfmlBldrCmd.java | 40 + .../src_500_build/gplx/gfml/GfmlBldrCmds.java | 128 ++ .../src_500_build/gplx/gfml/GfmlBldr_.java | 44 + .../src_500_build/gplx/gfml/GfmlFrame.java | 35 + .../src_500_build/gplx/gfml/GfmlFrame_.java | 87 ++ .../gplx/gfml/GfmlFrame_nde.java | 250 ++++ .../gplx/gfml/GfmlFrame_ndeTknMgr.java | 90 ++ .../gplx/gfml/GfmlStringHighlighter.java | 103 ++ .../src_600_rdrWtr/gplx/gfml/GfmlDataNde.java | 102 ++ .../src_600_rdrWtr/gplx/gfml/GfmlDataRdr.java | 48 + .../gplx/gfml/GfmlDataRdr_base.java | 60 + .../src_600_rdrWtr/gplx/gfml/GfmlDataWtr.java | 99 ++ .../gplx/gfml/GfmlDataWtrOpts.java | 27 + .../gplx/gfml/GfoMsgParser_gfml.java | 23 + .../src_600_rdrWtr/gplx/gfml/SqlConsts.java | 42 + 110_gfml/src_600_rdrWtr/gplx/gfml/SqlDoc.java | 144 +++ 110_gfml/tst/gplx/gfml/GfmlDataRdr_tst.java | 120 ++ .../tst/gplx/gfml/yfxts_GfmlParse_fxt.java | 79 ++ .../gplx/gfml/yfxts_GfmlTypeCompiler_fxt.java | 102 ++ .../gplx/gfml/ymoks_GfmlAtr_GfmlNde_mok.java | 182 +++ 110_gfml/tst/gplx/gfml/ymoks_GfmlTkn_mok.java | 77 ++ .../gplx/gfml/ymoks_GfmlTyp_GfmlFld_mok.java | 83 ++ 110_gfml/tst/gplx/gfml/ymoks_UsrMsg_mok.java | 36 + .../tst/gplx/gfml/z011_IntObjHash_tst.java | 63 + 110_gfml/tst/gplx/gfml/z012_GfmlTrie_tst.java | 73 ++ .../tst/gplx/gfml/z015_GfmlDocPos_tst.java | 52 + .../tst/gplx/gfml/z016_GfmlScopeList_tst.java | 55 + .../gfml/z017_GfmlStringHighlighter_tst.java | 53 + .../gplx/gfml/z051_GfmlFldPool_keyed_tst.java | 77 ++ .../tst/gplx/gfml/z081_GfmlDataWtr_tst.java | 71 ++ .../gplx/gfml/z082_GfmlDataWtrOpts_tst.java | 54 + .../tst/gplx/gfml/z091_GfmlLxr_basic_tst.java | 68 ++ .../gplx/gfml/z101_core_ndeInline_tst.java | 50 + .../gplx/gfml/z102_core_whitespace_tst.java | 79 ++ .../tst/gplx/gfml/z103_core_elmKey_tst.java | 52 + .../tst/gplx/gfml/z111_core_comment0_tst.java | 51 + .../tst/gplx/gfml/z112_core_comment1_tst.java | 69 ++ .../tst/gplx/gfml/z120_quotes_eval0_tst.java | 41 + .../gplx/gfml/z121_quotes_quotes0_tst.java | 52 + .../gfml/z122_quotes_quote0_eval0_tst.java | 64 + .../gplx/gfml/z123_quotes_quoteBlock_tst.java | 40 + .../gplx/gfml/z124_quotes_quoteFold_tst.java | 61 + .../tst/gplx/gfml/z151_ndeSubs_basic_tst.java | 65 + .../tst/gplx/gfml/z152_ndeSubs_data_tst.java | 68 ++ .../gplx/gfml/z161_ndeHdrs_inline_tst.java | 46 + .../tst/gplx/gfml/z162_ndeHdrs_err_tst.java | 33 + .../tst/gplx/gfml/z163_ndeHdrs_body_tst.java | 52 + .../tst/gplx/gfml/z164_hdeHdrs_data_tst.java | 53 + .../tst/gplx/gfml/z181_ndeDots_basic_tst.java | 65 + .../tst/gplx/gfml/z182_ndeDots_subs_tst.java | 73 ++ .../gplx/gfml/z183_ndeDots_parens_tst.java | 140 +++ .../gplx/gfml/z184_ndeDots_atrSpr_tst.java | 47 + .../gplx/gfml/z191_ndeProps_basic_tst.java | 120 ++ .../tst/gplx/gfml/z192_ndeProps_dots_tst.java | 39 + .../tst/gplx/gfml/z400_GfmlTypeMakr_tst.java | 66 + .../gfml/z401_types_compile_basic_tst.java | 65 + .../gfml/z402_types_compile_implicit_tst.java | 63 + .../gfml/z403_types_compile_default_tst.java | 64 + .../gfml/z411_types_apply_atrs_basic_tst.java | 66 + .../gfml/z421_types_apply_ndes_basic_tst.java | 52 + .../gfml/z422_types_apply_ndes_multi_tst.java | 74 ++ .../gfml/z423_types_apply_ndes_misc_tst.java | 49 + .../gfml/z424_types_apply_ndes_nest_tst.java | 67 ++ .../gplx/gfml/z441_types_parse_basic_tst.java | 53 + .../gfml/z442_types_parse_default_tst.java | 101 ++ .../gplx/gfml/z443_types_parse_keyd_tst.java | 50 + .../gfml/z450_fx_GfmlDefaultItem_fxt.java | 41 + .../tst/gplx/gfml/z451_dflts_compile_tst.java | 46 + .../tst/gplx/gfml/z452_dflts_exec_tst.java | 96 ++ .../tst/gplx/gfml/z455_dflts_scope_tst.java | 49 + .../tst/gplx/gfml/z456_dflts_parse_tst.java | 35 + .../tst/gplx/gfml/z481_vars_compile_tst.java | 35 + .../tst/gplx/gfml/z482_vars_parse_tst.java | 85 ++ .../tst/gplx/gfml/z501_lxr_parse_tst.java | 88 ++ 110_gfml/tst/gplx/gfml/z601_edit_atr_tst.java | 130 ++ 110_gfml/tst/gplx/gfml/z602_edit_nde_tst.java | 24 + .../gplx/gfml/z801_useCase_DataRdr_tst.java | 81 ++ .../gfml/z803_useCase_KbdKeyboard_tst.java | 87 ++ .../gplx/gfml/z811_useCase_GfmlIoSql_tst.java | 50 + 110_gfml/tst/gplx/gfml/z901_perf_tst.java | 117 ++ 140_dbs/.classpath | 14 + .../src/gplx/core/srls/Dbmeta_dat_itm.java | 24 + .../src/gplx/core/srls/Dbmeta_dat_mgr.java | 30 + 140_dbs/src/gplx/core/srls/Gfo_srl_ctx.java | 23 + 140_dbs/src/gplx/core/srls/Gfo_srl_itm.java | 23 + .../src/gplx/core/srls/Gfo_srl_mgr_rdr.java | 26 + .../src/gplx/core/srls/Gfo_srl_mgr_wtr.java | 27 + 140_dbs/src/gplx/core/stores/DbMaprArg.java | 27 + 140_dbs/src/gplx/core/stores/DbMaprItm.java | 52 + 140_dbs/src/gplx/core/stores/DbMaprMgr.java | 48 + .../src/gplx/core/stores/DbMaprMgr_tst.java | 140 +++ 140_dbs/src/gplx/core/stores/DbMaprRdr.java | 122 ++ 140_dbs/src/gplx/core/stores/DbMaprWtr.java | 115 ++ 140_dbs/src/gplx/core/stores/Db_data_rdr.java | 81 ++ .../src/gplx/core/stores/Db_data_rdr_.java | 22 + 140_dbs/src/gplx/core/stores/MockDiscObj.java | 130 ++ 140_dbs/src/gplx/dbs/Db_attach_itm.java | 28 + 140_dbs/src/gplx/dbs/Db_attach_mgr.java | 122 ++ 140_dbs/src/gplx/dbs/Db_attach_mgr__tst.java | 59 + 140_dbs/src/gplx/dbs/Db_cmd_mode.java | 42 + 140_dbs/src/gplx/dbs/Db_conn.java | 158 +++ 140_dbs/src/gplx/dbs/Db_conn_.java | 21 + 140_dbs/src/gplx/dbs/Db_conn_bldr.java | 54 + 140_dbs/src/gplx/dbs/Db_conn_bldr_data.java | 24 + 140_dbs/src/gplx/dbs/Db_conn_bldr_wkr.java | 65 + 140_dbs/src/gplx/dbs/Db_conn_info.java | 25 + 140_dbs/src/gplx/dbs/Db_conn_info_.java | 88 ++ 140_dbs/src/gplx/dbs/Db_conn_info__base.java | 50 + 140_dbs/src/gplx/dbs/Db_conn_info_tst.java | 48 + 140_dbs/src/gplx/dbs/Db_conn_pool.java | 66 + 140_dbs/src/gplx/dbs/Db_conn_utl.java | 77 ++ 140_dbs/src/gplx/dbs/Db_crt_.java | 65 + 140_dbs/src/gplx/dbs/Db_crt_tst.java | 52 + 140_dbs/src/gplx/dbs/Db_idx_itm.java | 34 + 140_dbs/src/gplx/dbs/Db_mock_cell.java | 24 + 140_dbs/src/gplx/dbs/Db_mock_row.java | 31 + 140_dbs/src/gplx/dbs/Db_null.java | 24 + 140_dbs/src/gplx/dbs/Db_qry.java | 30 + 140_dbs/src/gplx/dbs/Db_qry_.java | 63 + 140_dbs/src/gplx/dbs/Db_qry_fxt.java | 67 ++ 140_dbs/src/gplx/dbs/Db_rdr.java | 36 + 140_dbs/src/gplx/dbs/Db_rdr_.java | 39 + 140_dbs/src/gplx/dbs/Db_rdr__basic.java | 51 + 140_dbs/src/gplx/dbs/Db_sql_.java | 53 + 140_dbs/src/gplx/dbs/Db_sql_select.java | 78 ++ 140_dbs/src/gplx/dbs/Db_stmt.java | 67 ++ 140_dbs/src/gplx/dbs/Db_stmt_.java | 72 ++ 140_dbs/src/gplx/dbs/Db_stmt_bldr.java | 48 + 140_dbs/src/gplx/dbs/Db_tbl.java | 22 + 140_dbs/src/gplx/dbs/Db_tbl_.java | 30 + 140_dbs/src/gplx/dbs/Db_tbl_owner.java | 21 + 140_dbs/src/gplx/dbs/Dbmeta_fld_itm.java | 81 ++ 140_dbs/src/gplx/dbs/Dbmeta_fld_list.java | 108 ++ 140_dbs/src/gplx/dbs/Dbmeta_fld_tid.java | 73 ++ 140_dbs/src/gplx/dbs/Dbmeta_idx_itm.java | 54 + 140_dbs/src/gplx/dbs/Dbmeta_tbl_itm.java | 43 + .../dbs/conn_props/Db_conn_props_mgr.java | 28 + 140_dbs/src/gplx/dbs/diffs/Gdif_core.java | 46 + 140_dbs/src/gplx/dbs/diffs/Gdif_db.java | 31 + 140_dbs/src/gplx/dbs/diffs/Gdif_db_.java | 33 + 140_dbs/src/gplx/dbs/diffs/Gfdb_diff_cmd.java | 58 + 140_dbs/src/gplx/dbs/diffs/Gfdb_diff_tbl.java | 86 ++ .../src/gplx/dbs/diffs/Gfdb_diff_tbl_mgr.java | 35 + 140_dbs/src/gplx/dbs/diffs/Gfdb_rdr_utl_.java | 78 ++ .../gplx/dbs/diffs/builds/Gdif_bldr_ctx.java | 38 + .../gplx/dbs/diffs/builds/Gfdb_diff_bldr.java | 40 + .../dbs/diffs/builds/Gfdb_diff_bldr_tst.java | 107 ++ .../diffs/builds/Gfdb_diff_rdr_comparer.java | 66 + .../gplx/dbs/diffs/builds/Gfdb_diff_wkr.java | 25 + .../dbs/diffs/builds/Gfdb_diff_wkr__db.java | 89 ++ .../gplx/dbs/diffs/cmds/Gfdb_diff_cmd.java | 305 +++++ .../cmds/Gfdb_diff_cmd__idx__create.java | 117 ++ .../cmds/Gfdb_diff_cmd_sql_bldr_tst.java | 71 ++ .../src/gplx/dbs/diffs/itms/Gdif_cmd_itm.java | 29 + .../src/gplx/dbs/diffs/itms/Gdif_cmd_tbl.java | 43 + .../src/gplx/dbs/diffs/itms/Gdif_job_itm.java | 28 + .../src/gplx/dbs/diffs/itms/Gdif_job_tbl.java | 46 + .../src/gplx/dbs/diffs/itms/Gdif_txn_itm.java | 29 + .../src/gplx/dbs/diffs/itms/Gdif_txn_tbl.java | 43 + 140_dbs/src/gplx/dbs/engines/Db_engine.java | 51 + .../gplx/dbs/engines/Db_engine_sql_base.java | 139 +++ .../gplx/dbs/engines/mems/Mem_conn_info.java | 32 + .../src/gplx/dbs/engines/mems/Mem_db_fxt.java | 74 ++ .../src/gplx/dbs/engines/mems/Mem_engine.java | 72 ++ .../dbs/engines/mems/Mem_exec_select.java | 211 ++++ .../dbs/engines/mems/Mem_exec_select_tst.java | 130 ++ .../gplx/dbs/engines/mems/Mem_qry_set.java | 24 + .../src/gplx/dbs/engines/mems/Mem_rdr.java | 46 + .../src/gplx/dbs/engines/mems/Mem_row.java | 39 + .../src/gplx/dbs/engines/mems/Mem_stmt.java | 148 +++ .../gplx/dbs/engines/mems/Mem_stmt_args.java | 77 ++ .../src/gplx/dbs/engines/mems/Mem_tbl.java | 106 ++ .../dbs/engines/mysql/Mysql_conn_info.java | 45 + .../gplx/dbs/engines/mysql/Mysql_engine.java | 50 + .../dbs/engines/noops/Noop_conn_info.java | 24 + .../gplx/dbs/engines/noops/Noop_engine.java | 52 + .../engines/postgres/Postgres_conn_info.java | 46 + .../dbs/engines/postgres/Postgres_engine.java | 36 + .../dbs/engines/sqlite/Sqlite_conn_info.java | 49 + .../dbs/engines/sqlite/Sqlite_engine.java | 165 +++ .../dbs/engines/sqlite/Sqlite_engine_.java | 79 ++ .../dbs/engines/sqlite/Sqlite_pragma.java | 50 + .../dbs/engines/sqlite/Sqlite_schema_mgr.java | 82 ++ .../gplx/dbs/engines/sqlite/Sqlite_tid.java | 21 + .../dbs/engines/sqlite/Sqlite_txn_mgr.java | 71 ++ .../dbs/engines/tdbs/TdbConnectInfo_tst.java | 32 + .../gplx/dbs/engines/tdbs/TdbDatabase.java | 47 + .../gplx/dbs/engines/tdbs/TdbDbLoadMgr.java | 50 + .../dbs/engines/tdbs/TdbDbLoadMgr_tst.java | 101 ++ .../gplx/dbs/engines/tdbs/TdbDbSaveMgr.java | 64 + .../dbs/engines/tdbs/TdbDbSaveMgr_tst.java | 77 ++ .../src/gplx/dbs/engines/tdbs/TdbDelete.java | 47 + .../src/gplx/dbs/engines/tdbs/TdbEngine.java | 97 ++ .../src/gplx/dbs/engines/tdbs/TdbFile.java | 31 + .../gplx/dbs/engines/tdbs/TdbFileList.java | 64 + .../src/gplx/dbs/engines/tdbs/TdbFlush.java | 34 + .../gplx/dbs/engines/tdbs/TdbFlush_tst.java | 119 ++ .../src/gplx/dbs/engines/tdbs/TdbInsert.java | 68 ++ .../src/gplx/dbs/engines/tdbs/TdbSelect.java | 113 ++ .../src/gplx/dbs/engines/tdbs/TdbStores.java | 32 + .../src/gplx/dbs/engines/tdbs/TdbTable.java | 65 + .../gplx/dbs/engines/tdbs/TdbTableList.java | 63 + .../src/gplx/dbs/engines/tdbs/TdbUpdate.java | 46 + .../gplx/dbs/engines/tdbs/Tdb_conn_info.java | 37 + .../src/gplx/dbs/metas/Dbmeta_fld_mgr.java | 35 + .../src/gplx/dbs/metas/Dbmeta_idx_fld.java | 39 + .../src/gplx/dbs/metas/Dbmeta_idx_mgr.java | 28 + .../src/gplx/dbs/metas/Dbmeta_itm_tid.java | 28 + .../src/gplx/dbs/metas/Dbmeta_reload_cmd.java | 21 + .../gplx/dbs/metas/Dbmeta_reload_cmd_.java | 24 + .../src/gplx/dbs/metas/Dbmeta_tbl_mgr.java | 30 + .../metas/parsers/Dbmeta_fld_wkr__base.java | 148 +++ .../dbs/metas/parsers/Dbmeta_parser__fld.java | 123 ++ .../metas/parsers/Dbmeta_parser__fld_tst.java | 74 ++ .../dbs/metas/parsers/Dbmeta_parser__idx.java | 58 + .../metas/parsers/Dbmeta_parser__idx_tst.java | 37 + .../dbs/metas/parsers/Dbmeta_parser__tbl.java | 51 + .../metas/parsers/Dbmeta_parser__tbl_tst.java | 65 + .../gplx/dbs/metas/parsers/Sql_bry_rdr.java | 56 + .../dbs/metas/parsers/Sql_bry_rdr_tst.java | 52 + 140_dbs/src/gplx/dbs/qrys/Db_arg.java | 24 + 140_dbs/src/gplx/dbs/qrys/Db_arg_owner.java | 33 + .../src/gplx/dbs/qrys/Db_qry__select_cmd.java | 113 ++ .../gplx/dbs/qrys/Db_qry__select_in_tbl.java | 86 ++ 140_dbs/src/gplx/dbs/qrys/Db_qry_delete.java | 32 + 140_dbs/src/gplx/dbs/qrys/Db_qry_dml_tst.java | 44 + 140_dbs/src/gplx/dbs/qrys/Db_qry_flush.java | 37 + 140_dbs/src/gplx/dbs/qrys/Db_qry_insert.java | 66 + .../src/gplx/dbs/qrys/Db_qry_select_tst.java | 89 ++ 140_dbs/src/gplx/dbs/qrys/Db_qry_sql.java | 82 ++ 140_dbs/src/gplx/dbs/qrys/Db_qry_sql_tst.java | 48 + 140_dbs/src/gplx/dbs/qrys/Db_qry_update.java | 59 + 140_dbs/src/gplx/dbs/qrys/Db_stmt_cmd.java | 178 +++ 140_dbs/src/gplx/dbs/qrys/Db_stmt_sql.java | 191 +++ .../src/gplx/dbs/qrys/Db_stmt_sql_tst.java | 29 + 140_dbs/src/gplx/dbs/qrys/Db_val_type.java | 36 + .../dbs/qrys/bats/Db_batch__journal_off.java | 48 + .../dbs/qrys/bats/Db_batch__journal_wal.java | 62 + .../src/gplx/dbs/qrys/bats/Db_batch_grp.java | 43 + .../src/gplx/dbs/qrys/bats/Db_batch_itm.java | 23 + .../src/gplx/dbs/qrys/bats/Db_batch_mgr.java | 28 + 140_dbs/src/gplx/dbs/sqls/Sql_qry_wtr.java | 23 + 140_dbs/src/gplx/dbs/sqls/Sql_qry_wtr_.java | 29 + .../gplx/dbs/sqls/itms/Db_obj_ary_crt.java | 36 + .../gplx/dbs/sqls/itms/Db_obj_ary_fld.java | 23 + .../gplx/dbs/sqls/itms/Db_obj_ary_tst.java | 44 + .../gplx/dbs/sqls/itms/Sql_from_clause.java | 26 + .../gplx/dbs/sqls/itms/Sql_group_clause.java | 28 + .../src/gplx/dbs/sqls/itms/Sql_join_fld.java | 34 + .../gplx/dbs/sqls/itms/Sql_order_clause.java | 27 + .../src/gplx/dbs/sqls/itms/Sql_order_fld.java | 37 + .../dbs/sqls/itms/Sql_order_fld_sorter.java | 41 + .../gplx/dbs/sqls/itms/Sql_select_clause.java | 22 + .../gplx/dbs/sqls/itms/Sql_select_fld.java | 66 + .../dbs/sqls/itms/Sql_select_fld_func.java | 52 + .../dbs/sqls/itms/Sql_select_fld_list.java | 25 + .../src/gplx/dbs/sqls/itms/Sql_tbl_itm.java | 44 + .../gplx/dbs/sqls/itms/Sql_where_clause.java | 25 + .../src/gplx/dbs/sqls/wtrs/Sql_core_wtr.java | 105 ++ .../dbs/sqls/wtrs/Sql_core_wtr__mysql.java | 20 + .../dbs/sqls/wtrs/Sql_core_wtr__sqlite.java | 21 + .../gplx/dbs/sqls/wtrs/Sql_core_wtr_fxt.java | 36 + .../src/gplx/dbs/sqls/wtrs/Sql_from_wtr.java | 54 + .../gplx/dbs/sqls/wtrs/Sql_from_wtr_tst.java | 33 + .../dbs/sqls/wtrs/Sql_qry_wtr__ansi__tst.java | 99 ++ .../sqls/wtrs/Sql_qry_wtr__iosql__tst.java | 61 + .../gplx/dbs/sqls/wtrs/Sql_schema_wtr.java | 105 ++ .../dbs/sqls/wtrs/Sql_schema_wtr_tst.java | 68 ++ .../gplx/dbs/sqls/wtrs/Sql_select_wtr.java | 80 ++ .../dbs/sqls/wtrs/Sql_select_wtr_tst.java | 28 + .../src/gplx/dbs/sqls/wtrs/Sql_val_wtr.java | 91 ++ .../gplx/dbs/sqls/wtrs/Sql_val_wtr_tst.java | 39 + .../src/gplx/dbs/sqls/wtrs/Sql_where_wtr.java | 145 +++ .../gplx/dbs/sqls/wtrs/Sql_where_wtr_tst.java | 55 + .../src/gplx/dbs/sqls/wtrs/Sql_wtr_ctx.java | 22 + 140_dbs/src/gplx/dbs/stmts/Db_stmt_arg.java | 25 + .../src/gplx/dbs/stmts/Db_stmt_arg_list.java | 67 ++ 140_dbs/src/gplx/dbs/stmts/Db_stmt_mgr.java | 53 + 140_dbs/src/gplx/dbs/sys/Db_sys_mgr.java | 38 + 140_dbs/src/gplx/dbs/sys/Db_sys_mgr_tst.java | 34 + 140_dbs/src/gplx/dbs/sys/Db_sys_tbl.java | 68 ++ 140_dbs/src/gplx/dbs/utls/Db_cmd_backup.java | 67 ++ .../src/gplx/dbs/utls/Db_cmd_backup_tst.java | 31 + .../src/gplx/dbs/utls/Db_in_wkr__base.java | 49 + 140_dbs/src/gplx/dbs/utls/Db_tbl__crud_.java | 82 ++ 140_dbs/src/gplx/dbs/utls/PoolIds.java | 52 + 140_dbs/src/gplx/dbs/utls/PoolIds_tst.java | 56 + 140_dbs/tst/gplx/dbs/Db_conn_fxt.java | 55 + 140_dbs/tst/gplx/dbs/GfoNdeTstr.java | 33 + .../tst/gplx/dbs/engines/db_CrudOps_tst.java | 129 ++ .../gplx/dbs/engines/db_DataTypes_tst.java | 79 ++ .../gplx/dbs/groupBys/GroupBys_base_tst.java | 95 ++ .../gplx/dbs/groupBys/GroupBys_mysql_tst.java | 28 + .../gplx/dbs/groupBys/GroupBys_tdb_tst.java | 29 + .../dbs/insertIntos/InsertIntos_base_tst.java | 60 + .../insertIntos/InsertIntos_mysql_tst.java | 24 + .../dbs/insertIntos/InsertIntos_tdb_tst.java | 24 + .../tst/gplx/dbs/joins/Joins_base_tst.java | 44 + 140_dbs/tst/gplx/dbs/joins/Joins_tdb_tst.java | 31 + .../gplx/dbs/orderBys/OrderBys_base_tst.java | 52 + .../gplx/dbs/orderBys/OrderBys_tdb_tst.java | 38 + 140_dbs/xtn/gplx/dbs/Bug_Utf8.java | 176 +++ 140_dbs/xtn/gplx/dbs/SqliteDbMain.java | 247 ++++ 150_gfui/.classpath | 11 + 150_gfui/lib/swt.jar | Bin 0 -> 1918393 bytes 150_gfui/src/gplx/gfui/DirInt.java | 35 + 150_gfui/src/gplx/gfui/GfuiAlign.java | 23 + 150_gfui/src/gplx/gfui/GfuiAlign_.java | 63 + 150_gfui/src/gplx/gfui/GfuiAxisType.java | 25 + 150_gfui/src/gplx/gfui/GfuiBorderEdge.java | 58 + 150_gfui/src/gplx/gfui/PointAdp.java | 35 + 150_gfui/src/gplx/gfui/PointAdp_.java | 32 + 150_gfui/src/gplx/gfui/RectAdp.java | 44 + 150_gfui/src/gplx/gfui/RectAdpF.java | 33 + 150_gfui/src/gplx/gfui/RectAdp_.java | 34 + 150_gfui/src/gplx/gfui/SizeAdp.java | 36 + 150_gfui/src/gplx/gfui/SizeAdpF.java | 26 + 150_gfui/src/gplx/gfui/SizeAdpF_.java | 34 + 150_gfui/src/gplx/gfui/SizeAdp_.java | 42 + .../src/gplx/gfui/controls/GfuiBorderMgr.java | 70 ++ .../gplx/gfui/controls/GfuiBorderMgr_tst.java | 53 + .../customs/DataBndr_whenEvt_execCmd.java | 52 + .../controls/customs/GfuiBnd_box_status.java | 56 + .../controls/customs/GfuiCheckListBox.java | 42 + .../controls/customs/GfuiCheckListPanel.java | 61 + .../gfui/controls/customs/GfuiFormPanel.java | 39 + .../controls/customs/GfuiIoDialogUtl.java | 53 + .../controls/customs/GfuiIoUrlSelectBox.java | 71 ++ .../controls/customs/GfuiIoUrlSelectBox_.java | 22 + .../controls/customs/GfuiMoveElemBnd.java | 82 ++ .../controls/customs/GfuiMoveElemBtn.java | 88 ++ .../controls/customs/GfuiMoveElemBtn_tst.java | 35 + .../gfui/controls/customs/GfuiStatusBar.java | 49 + .../controls/customs/GfuiStatusBarBnd.java | 30 + .../gfui/controls/customs/GfuiStatusBox.java | 94 ++ .../controls/customs/GfuiStatusBoxBnd.java | 40 + .../gfui/controls/customs/GfuiStatusBox_.java | 32 + .../gplx/gfui/controls/elems/GfuiElem.java | 77 ++ .../gfui/controls/elems/GfuiElemBase.java | 294 +++++ .../gfui/controls/elems/GfuiElemBase_.java | 22 + .../gfui/controls/elems/GfuiElemKeys.java | 46 + .../gfui/controls/elems/GfuiElemList.java | 53 + .../gplx/gfui/controls/elems/GfuiElem_.java | 44 + .../gfui/controls/gxws/GxwBoxListener.java | 31 + .../gplx/gfui/controls/gxws/GxwCbkHost.java | 36 + .../gplx/gfui/controls/gxws/GxwCbkHost_.java | 87 ++ .../gfui/controls/gxws/GxwCheckListBox.java | 31 + .../controls/gxws/GxwCheckListBox_lang.java | 201 ++++ .../gplx/gfui/controls/gxws/GxwComboBox.java | 34 + .../gfui/controls/gxws/GxwComboBox_lang.java | 121 ++ .../gplx/gfui/controls/gxws/GxwCore_base.java | 44 + .../gplx/gfui/controls/gxws/GxwCore_lang.java | 160 +++ .../gplx/gfui/controls/gxws/GxwCore_mock.java | 46 + .../src/gplx/gfui/controls/gxws/GxwElem.java | 26 + .../gfui/controls/gxws/GxwElemFactory_.java | 23 + .../controls/gxws/GxwElemFactory_base.java | 32 + .../gxws/GxwElemFactory_cls_lang.java | 43 + .../gxws/GxwElemFactory_cls_mock.java | 31 + .../gplx/gfui/controls/gxws/GxwElem_lang.java | 68 ++ .../gfui/controls/gxws/GxwElem_mock_base.java | 87 ++ .../gplx/gfui/controls/gxws/GxwListBox.java | 25 + .../gfui/controls/gxws/GxwListBox_lang.java | 83 ++ .../gfui/controls/gxws/GxwTextBox_lang.java | 217 ++++ .../gplx/gfui/controls/gxws/GxwTextFld.java | 26 + .../gplx/gfui/controls/gxws/GxwTextHtml.java | 24 + .../gfui/controls/gxws/GxwTextHtml_lang.java | 254 ++++ .../gplx/gfui/controls/gxws/GxwTextMemo.java | 35 + .../gfui/controls/gxws/GxwTextMemo_lang.java | 345 ++++++ .../src/gplx/gfui/controls/gxws/GxwWin.java | 33 + .../gplx/gfui/controls/gxws/GxwWin_lang.java | 268 +++++ .../src/gplx/gfui/controls/gxws/Gxw_html.java | 32 + .../controls/gxws/Gxw_html_load_tid_.java | 35 + .../gplx/gfui/controls/gxws/Gxw_tab_itm.java | 25 + .../gplx/gfui/controls/gxws/Gxw_tab_mgr.java | 32 + .../src/gplx/gfui/controls/gxws/MockForm.java | 32 + .../gplx/gfui/controls/standards/GfuiBtn.java | 74 ++ .../controls/standards/GfuiBtnClickBnd.java | 42 + .../gfui/controls/standards/GfuiBtn_.java | 48 + .../gfui/controls/standards/GfuiChkBox.java | 62 + .../gfui/controls/standards/GfuiChkBox_.java | 74 ++ .../gfui/controls/standards/GfuiComboBox.java | 59 + .../gplx/gfui/controls/standards/GfuiLbl.java | 40 + .../gfui/controls/standards/GfuiLbl_.java | 52 + .../gfui/controls/standards/GfuiListBox.java | 44 + .../gfui/controls/standards/GfuiTextBox.java | 68 ++ .../gfui/controls/standards/GfuiTextBox_.java | 47 + .../gfui/controls/standards/GfuiTextMemo.java | 42 + .../gfui/controls/standards/Gfui_html.java | 72 ++ .../gfui/controls/standards/Gfui_tab_itm.java | 31 + .../controls/standards/Gfui_tab_itm_data.java | 38 + .../standards/Gfui_tab_itm_data_tst.java | 32 + .../gfui/controls/standards/Gfui_tab_mgr.java | 47 + .../src/gplx/gfui/controls/tabs/TabBnd.java | 91 ++ .../src/gplx/gfui/controls/tabs/TabBox.java | 155 +++ .../controls/tabs/TabBoxEvt_orderChanged.java | 33 + .../controls/tabs/TabBoxEvt_tabSelect.java | 37 + .../gplx/gfui/controls/tabs/TabBoxMgr.java | 57 + .../src/gplx/gfui/controls/tabs/TabBox_.java | 34 + .../gplx/gfui/controls/tabs/TabBox_tst.java | 131 ++ .../gplx/gfui/controls/tabs/TabPnlItm.java | 38 + .../gfui/controls/windows/GfoConsoleWin.java | 255 ++++ .../controls/windows/GfuiBnd_win_host.java | 40 + .../gfui/controls/windows/GfuiCmdForm.java | 54 + .../gfui/controls/windows/GfuiFocusMgr.java | 31 + .../controls/windows/GfuiFocusOrderer.java | 59 + .../windows/GfuiFocusOrderer_tst.java | 87 ++ .../controls/windows/GfuiFocusXferBnd.java | 54 + .../gfui/controls/windows/GfuiForm_menu.java | 76 ++ .../gfui/controls/windows/GfuiMenuBar.java | 256 ++++ .../controls/windows/GfuiMenuFormUtl.java | 40 + .../gfui/controls/windows/GfuiQuitMode.java | 37 + .../gfui/controls/windows/GfuiTipTextMgr.java | 34 + .../gplx/gfui/controls/windows/GfuiWin.java | 125 ++ .../controls/windows/GfuiWinFocusMgr.java | 137 +++ .../controls/windows/GfuiWinKeyCmdMgr.java | 62 + .../gplx/gfui/controls/windows/GfuiWin_.java | 73 ++ .../controls/windows/GfuiWin_toaster.java | 182 +++ .../controls/windows/GxwBorderFactory.java | 42 + .../gplx/gfui/controls/windows/GxwWinNpi.java | 20 + 150_gfui/src/gplx/gfui/draws/ColorAdp.java | 46 + .../src/gplx/gfui/draws/ColorAdpCache.java | 28 + 150_gfui/src/gplx/gfui/draws/ColorAdp_.java | 118 ++ .../src/gplx/gfui/draws/ColorAdp__tst.java | 58 + 150_gfui/src/gplx/gfui/draws/FontAdp.java | 65 + .../src/gplx/gfui/draws/FontAdpCache.java | 36 + .../src/gplx/gfui/draws/FontStyleAdp.java | 23 + .../src/gplx/gfui/draws/FontStyleAdp_.java | 68 ++ 150_gfui/src/gplx/gfui/draws/PenAdp.java | 53 + 150_gfui/src/gplx/gfui/draws/PenAdp_.java | 25 + .../src/gplx/gfui/draws/SolidBrushAdp.java | 31 + .../src/gplx/gfui/draws/SolidBrushAdp_.java | 38 + .../src/gplx/gfui/envs/ClipboardAdp_.java | 59 + .../src/gplx/gfui/envs/ClipboardAdp__tst.java | 26 + 150_gfui/src/gplx/gfui/envs/CursorAdp.java | 39 + 150_gfui/src/gplx/gfui/envs/ScreenAdp.java | 32 + 150_gfui/src/gplx/gfui/envs/ScreenAdp_.java | 59 + .../src/gplx/gfui/envs/ScreenAdp_tst.java | 29 + 150_gfui/src/gplx/gfui/envs/TimerAdp.java | 54 + 150_gfui/src/gplx/gfui/gfxs/GfxAdp.java | 30 + 150_gfui/src/gplx/gfui/gfxs/GfxAdpBase.java | 108 ++ 150_gfui/src/gplx/gfui/gfxs/GfxAdpMok.java | 50 + 150_gfui/src/gplx/gfui/gfxs/GfxAdp_.java | 27 + 150_gfui/src/gplx/gfui/gfxs/GfxItm.java | 19 + 150_gfui/src/gplx/gfui/gfxs/GfxItmList.java | 30 + 150_gfui/src/gplx/gfui/gfxs/GfxItm_base.java | 34 + 150_gfui/src/gplx/gfui/gfxs/GfxLineItm.java | 41 + 150_gfui/src/gplx/gfui/gfxs/GfxRectItm.java | 38 + .../src/gplx/gfui/gfxs/GfxStringData.java | 117 ++ 150_gfui/src/gplx/gfui/gfxs/GfxStringItm.java | 39 + 150_gfui/src/gplx/gfui/gfxs/PaintArgs.java | 29 + 150_gfui/src/gplx/gfui/imgs/IconAdp.java | 47 + 150_gfui/src/gplx/gfui/imgs/ImageAdp.java | 51 + 150_gfui/src/gplx/gfui/imgs/ImageAdp_.java | 126 ++ .../src/gplx/gfui/imgs/ImageAdp_base.java | 70 ++ .../src/gplx/gfui/imgs/ImageAdp_null.java | 35 + 150_gfui/src/gplx/gfui/imgs/ImageAdp_tst.java | 48 + .../gplx/gfui/ipts/GfuiClickKeyMgr_tst.java | 31 + 150_gfui/src/gplx/gfui/ipts/IptArg.java | 44 + .../gplx/gfui/ipts/IptArgChainMgr_tst.java | 62 + 150_gfui/src/gplx/gfui/ipts/IptArg_.java | 118 ++ .../src/gplx/gfui/ipts/IptArg_parser_tst.java | 63 + 150_gfui/src/gplx/gfui/ipts/IptBnd.java | 25 + 150_gfui/src/gplx/gfui/ipts/IptBndMgr.java | 294 +++++ .../src/gplx/gfui/ipts/IptBndMgr_tst.java | 72 ++ 150_gfui/src/gplx/gfui/ipts/IptBnd_.java | 62 + .../src/gplx/gfui/ipts/IptBnd_chkBox.java | 43 + .../src/gplx/gfui/ipts/IptBnd_txt_cmd.java | 45 + .../src/gplx/gfui/ipts/IptBnd_txt_range.java | 97 ++ .../gplx/gfui/ipts/IptBnd_upDownRange.java | 60 + 150_gfui/src/gplx/gfui/ipts/IptBndsOwner.java | 21 + 150_gfui/src/gplx/gfui/ipts/IptCfg.java | 93 ++ 150_gfui/src/gplx/gfui/ipts/IptCfgItm.java | 24 + 150_gfui/src/gplx/gfui/ipts/IptCfgRegy.java | 40 + 150_gfui/src/gplx/gfui/ipts/IptCfg_.java | 31 + 150_gfui/src/gplx/gfui/ipts/IptCfg_tst.java | 84 ++ 150_gfui/src/gplx/gfui/ipts/IptEventData.java | 50 + 150_gfui/src/gplx/gfui/ipts/IptEventMgr.java | 99 ++ 150_gfui/src/gplx/gfui/ipts/IptEventType.java | 36 + .../src/gplx/gfui/ipts/IptEventType_.java | 58 + .../src/gplx/gfui/ipts/IptEventType_tst.java | 34 + .../src/gplx/gfui/ipts/IptEvtDataKey.java | 36 + .../src/gplx/gfui/ipts/IptEvtDataKeyHeld.java | 31 + .../src/gplx/gfui/ipts/IptEvtDataMouse.java | 34 + 150_gfui/src/gplx/gfui/ipts/IptKey.java | 30 + 150_gfui/src/gplx/gfui/ipts/IptKeyStrMgr.java | 75 ++ .../src/gplx/gfui/ipts/IptKeyStrMgr_tst.java | 59 + 150_gfui/src/gplx/gfui/ipts/IptKey_.java | 153 +++ 150_gfui/src/gplx/gfui/ipts/IptKey__tst.java | 39 + 150_gfui/src/gplx/gfui/ipts/IptMouseBtn.java | 24 + 150_gfui/src/gplx/gfui/ipts/IptMouseBtn_.java | 54 + 150_gfui/src/gplx/gfui/ipts/IptMouseMove.java | 23 + .../src/gplx/gfui/ipts/IptMouseWheel.java | 23 + .../src/gplx/gfui/ipts/IptMouseWheel_.java | 36 + .../gplx/gfui/kits/core/GfoFactory_gfui.java | 40 + .../gplx/gfui/kits/core/GfsLibIni_gfui.java | 26 + .../src/gplx/gfui/kits/core/GfuiEnv_.java | 113 ++ .../src/gplx/gfui/kits/core/GfuiInvkCmd.java | 20 + .../src/gplx/gfui/kits/core/GfuiInvkCmd_.java | 21 + .../gplx/gfui/kits/core/Gfui_clipboard.java | 26 + .../gplx/gfui/kits/core/Gfui_clipboard_.java | 22 + .../gplx/gfui/kits/core/Gfui_dlg_file.java | 25 + .../gplx/gfui/kits/core/Gfui_dlg_file_.java | 28 + .../src/gplx/gfui/kits/core/Gfui_dlg_msg.java | 25 + .../gplx/gfui/kits/core/Gfui_dlg_msg_.java | 30 + .../src/gplx/gfui/kits/core/Gfui_kit.java | 52 + .../src/gplx/gfui/kits/core/Gfui_kit_.java | 32 + .../gplx/gfui/kits/core/Gfui_kit_base.java | 116 ++ .../src/gplx/gfui/kits/core/Gfui_mnu_grp.java | 30 + .../gplx/gfui/kits/core/Gfui_mnu_grp_.java | 40 + .../src/gplx/gfui/kits/core/Gfui_mnu_itm.java | 38 + .../gplx/gfui/kits/core/Gfui_mnu_itm_.java | 22 + .../src/gplx/gfui/kits/core/Mem_html.java | 69 ++ 150_gfui/src/gplx/gfui/kits/core/Mem_kit.java | 40 + .../src/gplx/gfui/kits/core/Swing_kit.java | 36 + 150_gfui/src/gplx/gfui/kits/core/Swt_kit.java | 372 ++++++ .../src/gplx/gfui/kits/core/TxtFindMgr.java | 48 + .../gplx/gfui/kits/swts/Swt_app_browser.java | 99 ++ .../src/gplx/gfui/kits/swts/Swt_app_main.java | 429 +++++++ 150_gfui/src/gplx/gfui/kits/swts/Swt_btn.java | 69 ++ .../gfui/kits/swts/Swt_btn_no_border.java | 71 ++ .../gplx/gfui/kits/swts/Swt_clipboard.java | 62 + .../src/gplx/gfui/kits/swts/Swt_combo.java | 104 ++ .../gplx/gfui/kits/swts/Swt_combo_ctrl.java | 396 ++++++ .../src/gplx/gfui/kits/swts/Swt_control.java | 29 + .../src/gplx/gfui/kits/swts/Swt_control_.java | 43 + .../gplx/gfui/kits/swts/Swt_core_cmds.java | 255 ++++ .../gplx/gfui/kits/swts/Swt_core_lnrs.java | 172 +++ .../src/gplx/gfui/kits/swts/Swt_dlg_file.java | 40 + .../src/gplx/gfui/kits/swts/Swt_dlg_msg.java | 86 ++ .../src/gplx/gfui/kits/swts/Swt_html.java | 312 +++++ .../gfui/kits/swts/Swt_html_eval_rslt.java | 24 + 150_gfui/src/gplx/gfui/kits/swts/Swt_img.java | 53 + 150_gfui/src/gplx/gfui/kits/swts/Swt_lbl.java | 47 + .../gfui/kits/swts/Swt_lnr__menu_detect.java | 37 + .../gplx/gfui/kits/swts/Swt_popup_grp.java | 221 ++++ .../src/gplx/gfui/kits/swts/Swt_tab_itm.java | 62 + .../src/gplx/gfui/kits/swts/Swt_tab_mgr.java | 283 +++++ .../src/gplx/gfui/kits/swts/Swt_text.java | 65 + .../gfui/kits/swts/Swt_text_w_border.java | 94 ++ 150_gfui/src/gplx/gfui/kits/swts/Swt_win.java | 119 ++ 150_gfui/src/gplx/gfui/layouts/GftBand.java | 91 ++ .../src/gplx/gfui/layouts/GftBand_tst.java | 112 ++ 150_gfui/src/gplx/gfui/layouts/GftCell.java | 26 + 150_gfui/src/gplx/gfui/layouts/GftGrid.java | 139 +++ .../src/gplx/gfui/layouts/GftGrid_fx.java | 93 ++ 150_gfui/src/gplx/gfui/layouts/GftItem.java | 34 + .../src/gplx/gfui/layouts/GftSizeCalc.java | 61 + 400_xowa/.classpath | 13 + 400_xowa/src/gplx/core/brys/Bit_.java | 55 + 400_xowa/src/gplx/core/brys/Bit__tst.java | 68 ++ 400_xowa/src/gplx/core/brys/Bit_heap_rdr.java | 92 ++ .../src/gplx/core/brys/Bit_heap_rdr_tst.java | 80 ++ 400_xowa/src/gplx/core/brys/Bit_heap_wtr.java | 71 ++ .../src/gplx/core/brys/Bit_heap_wtr_tst.java | 86 ++ 400_xowa/src/gplx/core/brys/Bry_bldr.java | 40 + 400_xowa/src/gplx/core/brys/Bry_comparer.java | 26 + 400_xowa/src/gplx/core/brys/Bry_diff_.java | 52 + 400_xowa/src/gplx/core/brys/Bry_diff_tst.java | 44 + .../src/gplx/core/brys/Int_flag_bldr.java | 38 + .../src/gplx/core/brys/Int_flag_bldr_.java | 89 ++ .../gplx/core/brys/Int_flag_bldr__tst.java | 74 ++ 400_xowa/src/gplx/core/brys/Mid_able.java | 23 + .../gplx/core/brys/evals/Bry_eval_mgr.java | 108 ++ .../core/brys/evals/Bry_eval_mgr__tst.java | 52 + .../gplx/core/brys/evals/Bry_eval_wkr.java | 22 + .../gplx/core/btries/Btrie_u8_mgr_tst.java | 96 ++ .../src/gplx/core/caches/Gfo_cache_data.java | 46 + .../src/gplx/core/caches/Gfo_cache_mgr.java | 92 ++ .../gplx/core/caches/Gfo_cache_mgr_base.java | 48 + .../gplx/core/caches/Gfo_cache_mgr_bry.java | 53 + .../gplx/core/caches/Gfo_cache_mgr_tst.java | 73 ++ .../gplx/core/consoles/Gfo_cmd_arg_itm.java | 55 + .../gplx/core/consoles/Gfo_cmd_arg_itm_.java | 48 + .../gplx/core/consoles/Gfo_cmd_arg_mgr.java | 91 ++ .../gplx/core/consoles/Gfo_cmd_arg_mgr_.java | 42 + .../consoles/Gfo_cmd_arg_mgr_printer.java | 95 ++ .../core/consoles/Gfo_cmd_arg_mgr_tst.java | 123 ++ .../src/gplx/core/enums/Gfo_enum_grp.java | 41 + 400_xowa/src/gplx/core/flds/Gfo_fld_base.java | 48 + 400_xowa/src/gplx/core/flds/Gfo_fld_rdr.java | 126 ++ .../src/gplx/core/flds/Gfo_fld_rdr_tst.java | 56 + 400_xowa/src/gplx/core/flds/Gfo_fld_wtr.java | 59 + 400_xowa/src/gplx/core/gfobjs/Gfobj_ary.java | 31 + 400_xowa/src/gplx/core/gfobjs/Gfobj_fld.java | 83 ++ 400_xowa/src/gplx/core/gfobjs/Gfobj_grp.java | 21 + 400_xowa/src/gplx/core/gfobjs/Gfobj_grp_.java | 24 + 400_xowa/src/gplx/core/gfobjs/Gfobj_nde.java | 60 + .../src/gplx/core/gfobjs/Gfobj_rdr__json.java | 92 ++ .../gplx/core/gfobjs/Gfobj_rdr__json_tst.java | 88 ++ .../src/gplx/core/gfobjs/Gfobj_wtr__json.java | 85 ++ .../gplx/core/gfobjs/Gfobj_wtr__json_fxt.java | 54 + .../gplx/core/gfobjs/Gfobj_wtr__json_tst.java | 155 +++ .../gplx/core/intls/String_surrogate_utl.java | 36 + .../core/intls/String_surrogate_utl_tst.java | 57 + .../gplx/core/intls/ucas/Uca_collator.java | 22 + .../gplx/core/intls/ucas/Uca_collator_.java | 25 + .../intls/ucas/Uca_collator__icu__4_8.java | 49 + .../core/intls/ucas/Uca_ltr_extractor.java | 51 + .../gplx/core/ios/BinaryHeap_Io_line_rdr.java | 84 ++ .../core/ios/BinaryHeap_Io_line_rdr_tst.java | 50 + 400_xowa/src/gplx/core/ios/Io_buffer_rdr.java | 73 ++ .../src/gplx/core/ios/Io_buffer_rdr_tst.java | 64 + 400_xowa/src/gplx/core/ios/Io_fil_chkr.java | 32 + 400_xowa/src/gplx/core/ios/Io_line_rdr.java | 163 +++ .../gplx/core/ios/Io_line_rdr_key_gen.java | 21 + .../gplx/core/ios/Io_line_rdr_key_gen_.java | 56 + .../src/gplx/core/ios/Io_line_rdr_tst.java | 96 ++ 400_xowa/src/gplx/core/ios/Io_make_cmd.java | 21 + 400_xowa/src/gplx/core/ios/Io_sort.java | 99 ++ 400_xowa/src/gplx/core/ios/Io_sort_cmd.java | 23 + .../src/gplx/core/ios/Io_sort_filCmd.java | 29 + .../src/gplx/core/ios/Io_sort_fil_basic.java | 38 + .../src/gplx/core/ios/Io_sort_misc_tst.java | 58 + .../src/gplx/core/ios/Io_sort_split_itm.java | 35 + .../core/ios/Io_sort_split_itm_sorter.java | 25 + 400_xowa/src/gplx/core/ios/Io_sort_tst.java | 66 + .../gplx/core/ios/Io_stream_rdr_process.java | 68 ++ .../src/gplx/core/ios/Io_stream_zip_mgr.java | 56 + 400_xowa/src/gplx/core/ios/Io_url_gen.java | 24 + 400_xowa/src/gplx/core/ios/Io_url_gen_.java | 45 + 400_xowa/src/gplx/core/lists/StatRng.java | 142 +++ 400_xowa/src/gplx/core/lists/StatRng_tst.java | 39 + .../binary_searches/Binary_comparer.java | 21 + .../lists/binary_searches/Binary_search_.java | 63 + .../binary_searches/Binary_search__tst.java | 55 + .../binary_searches/Binary_search_cmp.java | 36 + .../binary_searches/Binary_search_grp.java | 34 + .../gplx/core/lists/hashs/Hash_adp__int.java | 31 + 400_xowa/src/gplx/core/net/Gfo_inet_conn.java | 24 + .../src/gplx/core/net/Gfo_inet_conn_.java | 47 + .../gplx/core/net/Gfo_inet_conn__http.java | 29 + .../src/gplx/core/net/Gfo_protocol_itm.java | 139 +++ 400_xowa/src/gplx/core/net/Gfo_url.java | 40 + .../src/gplx/core/net/Gfo_url_parser.java | 186 +++ .../src/gplx/core/net/Gfo_url_parser_fxt.java | 56 + .../src/gplx/core/net/Gfo_url_parser_tst.java | 128 ++ .../src/gplx/core/net/Gfo_url_site_data.java | 24 + .../src/gplx/core/net/Http_client_rdr.java | 25 + .../src/gplx/core/net/Http_client_rdr_.java | 36 + .../core/net/Http_client_rdr__stream.java | 38 + .../src/gplx/core/net/Http_client_wtr.java | 27 + .../src/gplx/core/net/Http_client_wtr_.java | 21 + .../core/net/Http_client_wtr__stream.java | 53 + .../gplx/core/net/Http_post_data_hash.java | 27 + .../src/gplx/core/net/Http_post_data_itm.java | 23 + .../src/gplx/core/net/Http_request_itm.java | 82 ++ .../gplx/core/net/Http_request_parser.java | 176 +++ .../core/net/Http_request_parser_tst.java | 95 ++ .../src/gplx/core/net/Http_server_wtr.java | 24 + .../src/gplx/core/net/Http_server_wtr_.java | 22 + .../core/net/Http_server_wtr__console.java | 27 + 400_xowa/src/gplx/core/net/Local_host_.java | 24 + .../src/gplx/core/net/Server_socket_adp.java | 23 + .../core/net/Server_socket_adp__base.java | 44 + 400_xowa/src/gplx/core/net/Socket_adp.java | 24 + .../src/gplx/core/net/Socket_adp__base.java | 39 + .../core/net/downloads/Http_download_wkr.java | 26 + .../net/downloads/Http_download_wkr_.java | 21 + .../downloads/Http_download_wkr__base.java | 83 ++ .../net/downloads/Http_download_wkr__jre.java | 111 ++ .../gplx/core/net/emails/Gfo_email_mgr.java | 24 + .../gplx/core/net/emails/Gfo_email_mgr_.java | 22 + .../net/emails/Gfo_email_mgr__apache.java | 22 + .../core/net/emails/Gfo_email_mgr__jre.java | 38 + .../core/net/qargs/Gfo_qarg_enum_itm.java | 29 + .../core/net/qargs/Gfo_qarg_enum_mgr.java | 29 + .../src/gplx/core/net/qargs/Gfo_qarg_itm.java | 29 + .../src/gplx/core/net/qargs/Gfo_qarg_mgr.java | 64 + .../gplx/core/net/qargs/Gfo_qarg_mgr_old.java | 107 ++ .../src/gplx/core/primitives/Bry_ary.java | 57 + .../src/gplx/core/primitives/Bry_cache.java | 35 + .../core/primitives/Gfo_number_parser.java | 187 +++ .../primitives/Gfo_number_parser_tst.java | 109 ++ .../core/primitives/Hash_adp__primitive.java | 29 + .../src/gplx/core/primitives/Int_2_ref.java | 58 + .../src/gplx/core/primitives/Int_2_val.java | 33 + .../src/gplx/core/primitives/Int_ary.java | 75 ++ .../gplx/core/primitives/Int_ary_bldr.java | 23 + .../gplx/core/primitives/Int_ary_parser.java | 39 + .../core/primitives/Int_ary_parser_tst.java | 30 + .../src/gplx/core/primitives/Int_pool.java | 70 ++ .../gplx/core/primitives/Int_pool_tst.java | 75 ++ .../core/primitives/Obj_ary_parser_base.java | 58 + .../gplx/core/progs/rates/Gfo_rate_list.java | 46 + .../core/progs/rates/Gfo_rate_list_tst.java | 36 + .../gplx/core/progs/rates/Gfo_rate_mgr.java | 39 + .../gplx/core/scripts/Gfo_script_engine.java | 26 + .../gplx/core/scripts/Gfo_script_engine_.java | 31 + .../Gfo_script_engine__javascript.java | 72 ++ .../core/scripts/Gfo_script_engine__luaj.java | 99 ++ .../core/scripts/Gfo_script_engine__noop.java | 26 + .../gplx/core/security/files/Cksum_itm.java | 31 + .../gplx/core/security/files/Cksum_list.java | 84 ++ .../core/security/files/Cksum_list_tst.java | 50 + 400_xowa/src/gplx/core/tests/Tst_chkr.java | 30 + 400_xowa/src/gplx/core/tests/Tst_mgr.java | 134 +++ .../gplx/core/threads/Gfo_async_cmd_itm.java | 42 + .../gplx/core/threads/Gfo_async_cmd_mkr.java | 34 + .../src/gplx/core/threads/Gfo_async_mgr.java | 58 + .../src/gplx/core/threads/Gfo_thread_cmd.java | 30 + .../gplx/core/threads/Gfo_thread_cmd_.java | 22 + .../core/threads/Gfo_thread_cmd_base.java | 42 + .../core/threads/Gfo_thread_cmd_download.java | 74 ++ .../core/threads/Gfo_thread_cmd_replace.java | 62 + .../core/threads/Gfo_thread_cmd_unzip.java | 116 ++ .../src/gplx/core/threads/Gfo_thread_grp.java | 90 ++ .../src/gplx/core/threads/Gfo_thread_itm.java | 24 + .../src/gplx/core/threads/Gfo_thread_mgr.java | 37 + .../gplx/core/threads/Gfo_thread_pool.java | 62 + .../src/gplx/core/threads/Gfo_thread_wkr.java | 23 + 400_xowa/src/gplx/dbs/Db_diff_bldr.java | 54 + .../src/gplx/dbs/bulks/Db_bulk_exec_.java | 88 ++ 400_xowa/src/gplx/dbs/bulks/Db_bulk_prog.java | 21 + 400_xowa/src/gplx/dbs/bulks/Db_tbl_copy.java | 56 + .../src/gplx/dbs/bulks/Db_tbl_copy_tst.java | 39 + 400_xowa/src/gplx/dbs/cfgs/Db_cfg_hash.java | 34 + 400_xowa/src/gplx/dbs/cfgs/Db_cfg_itm.java | 57 + 400_xowa/src/gplx/dbs/cfgs/Db_cfg_tbl.java | 139 +++ .../src/gplx/dbs/metas/Schema_db_mgr.java | 28 + .../src/gplx/dbs/metas/Schema_loader_mgr.java | 21 + .../gplx/dbs/metas/Schema_loader_mgr_.java | 50 + .../dbs/metas/updates/Schema_update_cmd.java | 23 + .../dbs/metas/updates/Schema_update_cmd_.java | 37 + .../dbs/metas/updates/Schema_update_mgr.java | 32 + .../metas/updates/Schema_update_mgr_tst.java | 55 + .../gplx/dbs/percentiles/Log_tbl_fmtr.java | 74 ++ .../gplx/dbs/percentiles/Percentile_rng.java | 87 ++ .../dbs/percentiles/Percentile_rng_log.java | 43 + .../dbs/percentiles/Percentile_rng_tst.java | 63 + .../percentiles/Percentile_select_base.java | 67 ++ 400_xowa/src/gplx/dbs/updates/Sql_runner.java | 55 + 400_xowa/src/gplx/fsdb/Fsdb_db_file.java | 28 + 400_xowa/src/gplx/fsdb/Fsdb_db_mgr.java | 30 + 400_xowa/src/gplx/fsdb/Fsdb_db_mgr_.java | 68 ++ 400_xowa/src/gplx/fsdb/Fsdb_db_mgr__v1.java | 113 ++ 400_xowa/src/gplx/fsdb/Fsdb_db_mgr__v2.java | 66 + .../src/gplx/fsdb/Fsdb_db_mgr__v2_bldr.java | 112 ++ 400_xowa/src/gplx/fsdb/data/Fsd_bin_itm.java | 34 + 400_xowa/src/gplx/fsdb/data/Fsd_bin_tbl.java | 119 ++ 400_xowa/src/gplx/fsdb/data/Fsd_dir_itm.java | 31 + 400_xowa/src/gplx/fsdb/data/Fsd_dir_tbl.java | 69 ++ 400_xowa/src/gplx/fsdb/data/Fsd_fil_itm.java | 47 + 400_xowa/src/gplx/fsdb/data/Fsd_fil_tbl.java | 120 ++ 400_xowa/src/gplx/fsdb/data/Fsd_img_itm.java | 27 + 400_xowa/src/gplx/fsdb/data/Fsd_thm_itm.java | 68 ++ 400_xowa/src/gplx/fsdb/data/Fsd_thm_tbl.java | 159 +++ .../src/gplx/fsdb/data/Fsd_thm_tbl_tst.java | 58 + 400_xowa/src/gplx/fsdb/meta/Fsm_atr_fil.java | 109 ++ 400_xowa/src/gplx/fsdb/meta/Fsm_atr_mgr.java | 34 + 400_xowa/src/gplx/fsdb/meta/Fsm_atr_tbl.java | 60 + 400_xowa/src/gplx/fsdb/meta/Fsm_bin_fil.java | 41 + 400_xowa/src/gplx/fsdb/meta/Fsm_bin_mgr.java | 70 ++ 400_xowa/src/gplx/fsdb/meta/Fsm_bin_tbl.java | 60 + 400_xowa/src/gplx/fsdb/meta/Fsm_cfg_mgr.java | 54 + 400_xowa/src/gplx/fsdb/meta/Fsm_id_itm.java | 24 + 400_xowa/src/gplx/fsdb/meta/Fsm_mnt_itm.java | 79 ++ 400_xowa/src/gplx/fsdb/meta/Fsm_mnt_mgr.java | 64 + 400_xowa/src/gplx/fsdb/meta/Fsm_mnt_tbl.java | 60 + 400_xowa/src/gplx/gfui/Gfui_bnd_parser.java | 317 +++++ .../src/gplx/gfui/Gfui_bnd_parser_tst.java | 60 + .../src/gplx/langs/dsvs/Dsv_fld_parser.java | 22 + .../src/gplx/langs/dsvs/Dsv_fld_parser_.java | 114 ++ .../src/gplx/langs/dsvs/Dsv_tbl_parser.java | 83 ++ .../langs/dsvs/Dsv_tbl_parser_int_tst.java | 64 + .../langs/dsvs/Dsv_tbl_parser_str_tst.java | 109 ++ .../src/gplx/langs/dsvs/Dsv_wkr_base.java | 42 + 400_xowa/src/gplx/langs/gfs/Gfs_lxr.java | 214 ++++ 400_xowa/src/gplx/langs/gfs/Gfs_lxr_.java | 39 + 400_xowa/src/gplx/langs/gfs/Gfs_msg_bldr.java | 53 + .../src/gplx/langs/gfs/Gfs_msg_bldr_tst.java | 76 ++ 400_xowa/src/gplx/langs/gfs/Gfs_nde.java | 85 ++ 400_xowa/src/gplx/langs/gfs/Gfs_parser.java | 104 ++ .../src/gplx/langs/gfs/Gfs_parser_ctx.java | 127 ++ .../src/gplx/langs/gfs/Gfs_parser_tst.java | 196 +++ 400_xowa/src/gplx/langs/gfs/Gfs_wtr.java | 46 + .../gplx/langs/htmls/clses/Gfh_class_.java | 61 + .../langs/htmls/clses/Gfh_class__tst.java | 58 + .../langs/htmls/clses/Gfh_class_parser_.java | 49 + .../htmls/clses/Gfh_class_parser__tst.java | 44 + .../htmls/clses/Gfh_class_parser_wkr.java | 21 + .../src/gplx/langs/htmls/docs/Gfh_atr.java | 47 + .../gplx/langs/htmls/docs/Gfh_doc_parser.java | 54 + .../gplx/langs/htmls/docs/Gfh_doc_wkr.java | 22 + .../src/gplx/langs/htmls/docs/Gfh_tag.java | 164 +++ .../gplx/langs/htmls/docs/Gfh_tag_rdr.java | 360 ++++++ .../langs/htmls/docs/Gfh_tag_rdr_tst.java | 80 ++ .../gplx/langs/htmls/docs/Gfh_txt_wkr.java | 21 + 1351 files changed, 88510 insertions(+) create mode 100644 100_core/.classpath create mode 100644 100_core/src/gplx/Array_.java create mode 100644 100_core/src/gplx/Array__tst.java create mode 100644 100_core/src/gplx/Bool_.java create mode 100644 100_core/src/gplx/Bool__tst.java create mode 100644 100_core/src/gplx/Bry_.java create mode 100644 100_core/src/gplx/Bry__tst.java create mode 100644 100_core/src/gplx/Bry_bfr.java create mode 100644 100_core/src/gplx/Bry_bfr_.java create mode 100644 100_core/src/gplx/Bry_bfr_tst.java create mode 100644 100_core/src/gplx/Bry_find_.java create mode 100644 100_core/src/gplx/Bry_find__tst.java create mode 100644 100_core/src/gplx/Bry_fmt.java create mode 100644 100_core/src/gplx/Bry_split_.java create mode 100644 100_core/src/gplx/Bry_split__tst.java create mode 100644 100_core/src/gplx/Byte_.java create mode 100644 100_core/src/gplx/Byte__tst.java create mode 100644 100_core/src/gplx/Byte_ascii.java create mode 100644 100_core/src/gplx/Cancelable.java create mode 100644 100_core/src/gplx/Cancelable_.java create mode 100644 100_core/src/gplx/Char_.java create mode 100644 100_core/src/gplx/CompareAble.java create mode 100644 100_core/src/gplx/CompareAble_.java create mode 100644 100_core/src/gplx/CompareAble_tst.java create mode 100644 100_core/src/gplx/DateAdp.java create mode 100644 100_core/src/gplx/DateAdp_.java create mode 100644 100_core/src/gplx/DateAdp__tst.java create mode 100644 100_core/src/gplx/Datetime_now.java create mode 100644 100_core/src/gplx/Decimal_adp.java create mode 100644 100_core/src/gplx/Decimal_adp_.java create mode 100644 100_core/src/gplx/Decimal_adp__tst.java create mode 100644 100_core/src/gplx/Double_.java create mode 100644 100_core/src/gplx/Double__tst.java create mode 100644 100_core/src/gplx/Enm_.java create mode 100644 100_core/src/gplx/Err.java create mode 100644 100_core/src/gplx/Err_.java create mode 100644 100_core/src/gplx/Err_tst.java create mode 100644 100_core/src/gplx/Float_.java create mode 100644 100_core/src/gplx/GfoMsg.java create mode 100644 100_core/src/gplx/GfoMsgUtl.java create mode 100644 100_core/src/gplx/GfoMsg_.java create mode 100644 100_core/src/gplx/GfoMsg_tst.java create mode 100644 100_core/src/gplx/GfoTemplate.java create mode 100644 100_core/src/gplx/GfoTemplateFactory.java create mode 100644 100_core/src/gplx/Gfo_evt_itm.java create mode 100644 100_core/src/gplx/Gfo_evt_mgr.java create mode 100644 100_core/src/gplx/Gfo_evt_mgr_.java create mode 100644 100_core/src/gplx/Gfo_evt_mgr_owner.java create mode 100644 100_core/src/gplx/Gfo_evt_mgr_tst.java create mode 100644 100_core/src/gplx/Gfo_invk.java create mode 100644 100_core/src/gplx/Gfo_invk_.java create mode 100644 100_core/src/gplx/Gfo_invk_cmd.java create mode 100644 100_core/src/gplx/Gfo_invk_cmd_mgr.java create mode 100644 100_core/src/gplx/Gfo_invk_cmd_mgr_owner.java create mode 100644 100_core/src/gplx/Gfo_invk_root_wkr.java create mode 100644 100_core/src/gplx/Gfo_invk_to_str.java create mode 100644 100_core/src/gplx/Gfo_log.java create mode 100644 100_core/src/gplx/Gfo_log_.java create mode 100644 100_core/src/gplx/Gfo_log_bfr.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg_.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg__gui.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg__gui_.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg__gui_test.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg__log.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg__log_.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg__log_base.java create mode 100644 100_core/src/gplx/Gfo_usr_dlg_base.java create mode 100644 100_core/src/gplx/GfsCtx.java create mode 100644 100_core/src/gplx/Guid_adp.java create mode 100644 100_core/src/gplx/Guid_adp_.java create mode 100644 100_core/src/gplx/Guid_adp__tst.java create mode 100644 100_core/src/gplx/Hash_adp.java create mode 100644 100_core/src/gplx/Hash_adp_.java create mode 100644 100_core/src/gplx/Hash_adp_bry.java create mode 100644 100_core/src/gplx/Hash_adp_bry_tst.java create mode 100644 100_core/src/gplx/Int_.java create mode 100644 100_core/src/gplx/Int__tst.java create mode 100644 100_core/src/gplx/Internal.java create mode 100644 100_core/src/gplx/Io_mgr.java create mode 100644 100_core/src/gplx/Io_mgr__tst.java create mode 100644 100_core/src/gplx/Io_url.java create mode 100644 100_core/src/gplx/Io_url_.java create mode 100644 100_core/src/gplx/Io_url__tst.java create mode 100644 100_core/src/gplx/Keyval.java create mode 100644 100_core/src/gplx/Keyval_.java create mode 100644 100_core/src/gplx/Keyval_hash.java create mode 100644 100_core/src/gplx/Keyval_list.java create mode 100644 100_core/src/gplx/List_adp.java create mode 100644 100_core/src/gplx/List_adp_.java create mode 100644 100_core/src/gplx/List_adp_base.java create mode 100644 100_core/src/gplx/List_adp_tst.java create mode 100644 100_core/src/gplx/Long_.java create mode 100644 100_core/src/gplx/Long__tst.java create mode 100644 100_core/src/gplx/Math_.java create mode 100644 100_core/src/gplx/Math__tst.java create mode 100644 100_core/src/gplx/New.java create mode 100644 100_core/src/gplx/ObjAry.java create mode 100644 100_core/src/gplx/Object_.java create mode 100644 100_core/src/gplx/Object__tst.java create mode 100644 100_core/src/gplx/Ordered_hash.java create mode 100644 100_core/src/gplx/Ordered_hash_.java create mode 100644 100_core/src/gplx/Ordered_hash_base.java create mode 100644 100_core/src/gplx/Ordered_hash_tst.java create mode 100644 100_core/src/gplx/RandomAdp.java create mode 100644 100_core/src/gplx/RandomAdp_.java create mode 100644 100_core/src/gplx/Rls_able.java create mode 100644 100_core/src/gplx/Rls_able_.java create mode 100644 100_core/src/gplx/Short_.java create mode 100644 100_core/src/gplx/String_.java create mode 100644 100_core/src/gplx/String__tst.java create mode 100644 100_core/src/gplx/Tfds.java create mode 100644 100_core/src/gplx/TfdsTstr_fxt.java create mode 100644 100_core/src/gplx/Time_span.java create mode 100644 100_core/src/gplx/Time_span_.java create mode 100644 100_core/src/gplx/To_str_able.java create mode 100644 100_core/src/gplx/To_str_able_.java create mode 100644 100_core/src/gplx/Type_adp_.java create mode 100644 100_core/src/gplx/UsrDlg.java create mode 100644 100_core/src/gplx/UsrDlg_.java create mode 100644 100_core/src/gplx/UsrMsg.java create mode 100644 100_core/src/gplx/UsrMsgWkr.java create mode 100644 100_core/src/gplx/UsrMsgWkr_.java create mode 100644 100_core/src/gplx/UsrMsgWkr_console.java create mode 100644 100_core/src/gplx/UsrMsgWkr_test.java create mode 100644 100_core/src/gplx/Virtual.java create mode 100644 100_core/src/gplx/Yn.java create mode 100644 100_core/src/gplx/core/bits/Bitmask_.java create mode 100644 100_core/src/gplx/core/brys/Bfr_arg.java create mode 100644 100_core/src/gplx/core/brys/Bfr_arg_.java create mode 100644 100_core/src/gplx/core/brys/Bfr_arg_clearable.java create mode 100644 100_core/src/gplx/core/brys/Bry_bfr_able.java create mode 100644 100_core/src/gplx/core/brys/Bry_bfr_able_.java create mode 100644 100_core/src/gplx/core/brys/Bry_bfr_mkr.java create mode 100644 100_core/src/gplx/core/brys/Bry_bfr_mkr_mgr.java create mode 100644 100_core/src/gplx/core/brys/Bry_bfr_mkr_tst.java create mode 100644 100_core/src/gplx/core/brys/Bry_err_wkr.java create mode 100644 100_core/src/gplx/core/brys/Bry_rdr.java create mode 100644 100_core/src/gplx/core/brys/Bry_rdr_old.java create mode 100644 100_core/src/gplx/core/brys/Bry_rdr_tst.java create mode 100644 100_core/src/gplx/core/brys/Bry_split_wkr.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__bry.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__bry_ary.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmt.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmtr.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__byte.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__decimal_int.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__int.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__time.java create mode 100644 100_core/src/gplx/core/brys/args/Bfr_arg__time_tst.java create mode 100644 100_core/src/gplx/core/brys/fmtrs/Bry_fmtr.java create mode 100644 100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr.java create mode 100644 100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_.java create mode 100644 100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_gfs.java create mode 100644 100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_itm.java create mode 100644 100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_tst.java create mode 100644 100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_vals.java create mode 100644 100_core/src/gplx/core/brys/fmts/Bfr_fmt_arg.java create mode 100644 100_core/src/gplx/core/brys/fmts/Bry_fmt_itm.java create mode 100644 100_core/src/gplx/core/brys/fmts/Bry_fmt_parser_.java create mode 100644 100_core/src/gplx/core/brys/fmts/Bry_fmt_tst.java create mode 100644 100_core/src/gplx/core/brys/fmts/Bry_keys_parser_tst.java create mode 100644 100_core/src/gplx/core/btries/Btrie_bwd_mgr.java create mode 100644 100_core/src/gplx/core/btries/Btrie_bwd_mgr_tst.java create mode 100644 100_core/src/gplx/core/btries/Btrie_fast_mgr.java create mode 100644 100_core/src/gplx/core/btries/Btrie_fast_mgr_tst.java create mode 100644 100_core/src/gplx/core/btries/Btrie_itm_stub.java create mode 100644 100_core/src/gplx/core/btries/Btrie_mgr.java create mode 100644 100_core/src/gplx/core/btries/Btrie_rv.java create mode 100644 100_core/src/gplx/core/btries/Btrie_slim_itm.java create mode 100644 100_core/src/gplx/core/btries/Btrie_slim_itm_tst.java create mode 100644 100_core/src/gplx/core/btries/Btrie_slim_mgr.java create mode 100644 100_core/src/gplx/core/btries/Btrie_slim_mgr_tst.java create mode 100644 100_core/src/gplx/core/btries/Btrie_u8_itm.java create mode 100644 100_core/src/gplx/core/btries/Btrie_u8_mgr.java create mode 100644 100_core/src/gplx/core/consoles/Console_adp.java create mode 100644 100_core/src/gplx/core/consoles/Console_adp_.java create mode 100644 100_core/src/gplx/core/consoles/Console_adp__mem.java create mode 100644 100_core/src/gplx/core/consoles/Console_adp__sys.java create mode 100644 100_core/src/gplx/core/criterias/Criteria.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_between.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_bool_base.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_comp.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_eq.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_fld.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_in.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_ioItm_tst.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_ioMatch.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_like.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_not.java create mode 100644 100_core/src/gplx/core/criterias/Criteria_tst.java create mode 100644 100_core/src/gplx/core/encoders/B85_fp_.java create mode 100644 100_core/src/gplx/core/encoders/B85_fp__tst.java create mode 100644 100_core/src/gplx/core/encoders/Base85_.java create mode 100644 100_core/src/gplx/core/encoders/Base85__tst.java create mode 100644 100_core/src/gplx/core/encoders/Gfo_hzip_int_.java create mode 100644 100_core/src/gplx/core/encoders/Hex_utl_.java create mode 100644 100_core/src/gplx/core/encoders/Hex_utl__tst.java create mode 100644 100_core/src/gplx/core/envs/Env_.java create mode 100644 100_core/src/gplx/core/envs/Jar_adp_.java create mode 100644 100_core/src/gplx/core/envs/Op_sys.java create mode 100644 100_core/src/gplx/core/envs/Op_sys_.java create mode 100644 100_core/src/gplx/core/envs/Process_adp.java create mode 100644 100_core/src/gplx/core/envs/Process_adp_tst.java create mode 100644 100_core/src/gplx/core/envs/Runtime_.java create mode 100644 100_core/src/gplx/core/envs/System_.java create mode 100644 100_core/src/gplx/core/errs/Err_msg.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoFld.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoFldList.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoFldList_.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoNde.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoNdeFxt.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoNdeList.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoNdeList_.java create mode 100644 100_core/src/gplx/core/gfo_ndes/GfoNde_.java create mode 100644 100_core/src/gplx/core/gfo_regys/GfoMsgParser.java create mode 100644 100_core/src/gplx/core/gfo_regys/GfoRegy.java create mode 100644 100_core/src/gplx/core/gfo_regys/GfoRegyItm.java create mode 100644 100_core/src/gplx/core/gfo_regys/GfoRegy_RegDir_tst.java create mode 100644 100_core/src/gplx/core/gfo_regys/GfoRegy_basic_tst.java create mode 100644 100_core/src/gplx/core/interfaces/InjectAble.java create mode 100644 100_core/src/gplx/core/interfaces/ParseAble.java create mode 100644 100_core/src/gplx/core/interfaces/SrlAble.java create mode 100644 100_core/src/gplx/core/interfaces/SrlAble_.java create mode 100644 100_core/src/gplx/core/interfaces/SrlAble__tst.java create mode 100644 100_core/src/gplx/core/intls/Gfo_case_itm.java create mode 100644 100_core/src/gplx/core/intls/Gfo_case_mgr.java create mode 100644 100_core/src/gplx/core/intls/Gfo_case_mgr_.java create mode 100644 100_core/src/gplx/core/intls/Utf16_.java create mode 100644 100_core/src/gplx/core/intls/Utf16__tst.java create mode 100644 100_core/src/gplx/core/intls/Utf8_.java create mode 100644 100_core/src/gplx/core/intls/Utf8__tst.java create mode 100644 100_core/src/gplx/core/ios/IoEngine.java create mode 100644 100_core/src/gplx/core/ios/IoEngineFxt.java create mode 100644 100_core/src/gplx/core/ios/IoEnginePool.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_base.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_memory.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_system.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_deleteDir.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_deleteFil.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_downloadFil.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_fil_affects1_base.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_loadFilStr.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_openRead.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_openWrite.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_queryDir.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_recycleFil.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_saveFilStr.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_xferDir.java create mode 100644 100_core/src/gplx/core/ios/IoEngine_xrg_xferFil.java create mode 100644 100_core/src/gplx/core/ios/IoErr.java create mode 100644 100_core/src/gplx/core/ios/IoItmAttrib.java create mode 100644 100_core/src/gplx/core/ios/IoItmClassXtn.java create mode 100644 100_core/src/gplx/core/ios/IoItmDir.java create mode 100644 100_core/src/gplx/core/ios/IoItmDir_.java create mode 100644 100_core/src/gplx/core/ios/IoItmFil.java create mode 100644 100_core/src/gplx/core/ios/IoItmFil_.java create mode 100644 100_core/src/gplx/core/ios/IoItmFil_mem.java create mode 100644 100_core/src/gplx/core/ios/IoItmHash.java create mode 100644 100_core/src/gplx/core/ios/IoItmList.java create mode 100644 100_core/src/gplx/core/ios/IoItm_base.java create mode 100644 100_core/src/gplx/core/ios/IoItm_base_.java create mode 100644 100_core/src/gplx/core/ios/IoItm_fxt.java create mode 100644 100_core/src/gplx/core/ios/IoRecycleBin.java create mode 100644 100_core/src/gplx/core/ios/IoUrlInfo.java create mode 100644 100_core/src/gplx/core/ios/IoUrlInfoRegy.java create mode 100644 100_core/src/gplx/core/ios/IoUrlInfo_.java create mode 100644 100_core/src/gplx/core/ios/IoUrlTypeRegy.java create mode 100644 100_core/src/gplx/core/ios/IoZipWkr.java create mode 100644 100_core/src/gplx/core/ios/IoZipWkr_tst.java create mode 100644 100_core/src/gplx/core/ios/Io_download_fmt.java create mode 100644 100_core/src/gplx/core/ios/Io_download_fmt_tst.java create mode 100644 100_core/src/gplx/core/ios/Io_fil.java create mode 100644 100_core/src/gplx/core/ios/Io_fil_mkr.java create mode 100644 100_core/src/gplx/core/ios/Io_size_.java create mode 100644 100_core/src/gplx/core/ios/Io_size__tst.java create mode 100644 100_core/src/gplx/core/ios/Io_url_obj_ref.java create mode 100644 100_core/src/gplx/core/ios/drives/Io_drive.java create mode 100644 100_core/src/gplx/core/ios/drives/Io_drive_.java create mode 100644 100_core/src/gplx/core/ios/drives/Io_drive__jre.java create mode 100644 100_core/src/gplx/core/ios/loaders/Io_loader.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream_.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream_base.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream_mem.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream_mem_tst.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream_mock.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream_mock_tst.java create mode 100644 100_core/src/gplx/core/ios/streams/IoStream_stream_rdr.java create mode 100644 100_core/src/gplx/core/ios/streams/Io_stream_rdr.java create mode 100644 100_core/src/gplx/core/ios/streams/Io_stream_rdr_.java create mode 100644 100_core/src/gplx/core/ios/streams/Io_stream_rdr__tst.java create mode 100644 100_core/src/gplx/core/ios/streams/Io_stream_tid_.java create mode 100644 100_core/src/gplx/core/ios/streams/Io_stream_wtr.java create mode 100644 100_core/src/gplx/core/ios/streams/Io_stream_wtr_.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__adp.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__base.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__bzip2.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__gzip.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__noop.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__raw.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__xz.java create mode 100644 100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__zip.java create mode 100644 100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__base.java create mode 100644 100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__bzip2.java create mode 100644 100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__gzip.java create mode 100644 100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__raw.java create mode 100644 100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__xz.java create mode 100644 100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__zip.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_compress_cmd__jre.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd_.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__base.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__jre.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_mgr.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_mgr_base.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_mgr_mok.java create mode 100644 100_core/src/gplx/core/ios/zips/Io_zip_mgr_tst.java create mode 100644 100_core/src/gplx/core/js/Js_wtr.java create mode 100644 100_core/src/gplx/core/js/Js_wtr_tst.java create mode 100644 100_core/src/gplx/core/lists/ComparerAble.java create mode 100644 100_core/src/gplx/core/lists/ComparerAble_.java create mode 100644 100_core/src/gplx/core/lists/EnumerAble.java create mode 100644 100_core/src/gplx/core/lists/Hash_adp_base.java create mode 100644 100_core/src/gplx/core/lists/Hash_adp_list.java create mode 100644 100_core/src/gplx/core/lists/Iterator_null.java create mode 100644 100_core/src/gplx/core/lists/Iterator_objAry.java create mode 100644 100_core/src/gplx/core/lists/List_adp__getable.java create mode 100644 100_core/src/gplx/core/lists/List_adp_sorter.java create mode 100644 100_core/src/gplx/core/lists/List_adp_sorter_tst.java create mode 100644 100_core/src/gplx/core/lists/StackAdp.java create mode 100644 100_core/src/gplx/core/lists/StackAdp_.java create mode 100644 100_core/src/gplx/core/lists/StackAdp_tst.java create mode 100644 100_core/src/gplx/core/lists/rings/Ring__long.java create mode 100644 100_core/src/gplx/core/lists/rings/Ring__long__tst.java create mode 100644 100_core/src/gplx/core/lists/rings/Ring__string.java create mode 100644 100_core/src/gplx/core/lists/rings/Ring__string__tst.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_data.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_grp.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_grp_.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_itm.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_itm_.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_log.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_obj.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_root.java create mode 100644 100_core/src/gplx/core/log_msgs/Gfo_msg_root_tst.java create mode 100644 100_core/src/gplx/core/logs/Gfo_log__base.java create mode 100644 100_core/src/gplx/core/logs/Gfo_log__file.java create mode 100644 100_core/src/gplx/core/logs/Gfo_log__mem.java create mode 100644 100_core/src/gplx/core/logs/Gfo_log_itm.java create mode 100644 100_core/src/gplx/core/logs/Gfo_log_itm_wtr.java create mode 100644 100_core/src/gplx/core/logs/Gfo_log_itm_wtr__csv.java create mode 100644 100_core/src/gplx/core/memorys/Gfo_memory_itm.java create mode 100644 100_core/src/gplx/core/memorys/Gfo_memory_mgr.java create mode 100644 100_core/src/gplx/core/primitives/Bool_obj_ref.java create mode 100644 100_core/src/gplx/core/primitives/Bool_obj_val.java create mode 100644 100_core/src/gplx/core/primitives/Bry_obj_ref.java create mode 100644 100_core/src/gplx/core/primitives/Byte_obj_ref.java create mode 100644 100_core/src/gplx/core/primitives/Byte_obj_val.java create mode 100644 100_core/src/gplx/core/primitives/Double_obj_val.java create mode 100644 100_core/src/gplx/core/primitives/EnmMgr.java create mode 100644 100_core/src/gplx/core/primitives/EnmParser_tst.java create mode 100644 100_core/src/gplx/core/primitives/Int_ary_.java create mode 100644 100_core/src/gplx/core/primitives/Int_ary__tst.java create mode 100644 100_core/src/gplx/core/primitives/Int_list.java create mode 100644 100_core/src/gplx/core/primitives/Int_obj_ref.java create mode 100644 100_core/src/gplx/core/primitives/Int_obj_val.java create mode 100644 100_core/src/gplx/core/primitives/String_obj_ref.java create mode 100644 100_core/src/gplx/core/primitives/String_obj_val.java create mode 100644 100_core/src/gplx/core/progs/Gfo_prog_ui.java create mode 100644 100_core/src/gplx/core/progs/Gfo_prog_ui_.java create mode 100644 100_core/src/gplx/core/progs/Gfo_resume_wkr.java create mode 100644 100_core/src/gplx/core/security/Hash_algo.java create mode 100644 100_core/src/gplx/core/security/Hash_algo_.java create mode 100644 100_core/src/gplx/core/security/Hash_algo__md5__tst.java create mode 100644 100_core/src/gplx/core/security/Hash_algo__sha1__tst.java create mode 100644 100_core/src/gplx/core/security/Hash_algo__sha2_256__tst.java create mode 100644 100_core/src/gplx/core/security/Hash_algo__tth_192.java create mode 100644 100_core/src/gplx/core/security/Hash_algo__tth_192__tst.java create mode 100644 100_core/src/gplx/core/security/Hash_algo__tth_192_tree_tst.java create mode 100644 100_core/src/gplx/core/security/Hash_console_wtr_tst.java create mode 100644 100_core/src/gplx/core/stores/DataRdr.java create mode 100644 100_core/src/gplx/core/stores/DataRdr_.java create mode 100644 100_core/src/gplx/core/stores/DataRdr_base.java create mode 100644 100_core/src/gplx/core/stores/DataRdr_mem.java create mode 100644 100_core/src/gplx/core/stores/DataWtr.java create mode 100644 100_core/src/gplx/core/stores/DataWtr_.java create mode 100644 100_core/src/gplx/core/stores/DataWtr_base.java create mode 100644 100_core/src/gplx/core/stores/GfoNdeRdr.java create mode 100644 100_core/src/gplx/core/stores/GfoNdeRdr_.java create mode 100644 100_core/src/gplx/core/stores/SrlMgr.java create mode 100644 100_core/src/gplx/core/stores/SrlObj.java create mode 100644 100_core/src/gplx/core/stores/xmls/XmlDataRdr.java create mode 100644 100_core/src/gplx/core/stores/xmls/XmlDataRdr_.java create mode 100644 100_core/src/gplx/core/stores/xmls/XmlDataWtr_.java create mode 100644 100_core/src/gplx/core/strings/String_bldr.java create mode 100644 100_core/src/gplx/core/strings/String_bldr_.java create mode 100644 100_core/src/gplx/core/tests/Gftest.java create mode 100644 100_core/src/gplx/core/tests/PerfLogMgr_fxt.java create mode 100644 100_core/src/gplx/core/tests/TfdsEqListItmStr.java create mode 100644 100_core/src/gplx/core/texts/Base32Converter.java create mode 100644 100_core/src/gplx/core/texts/Base64Converter.java create mode 100644 100_core/src/gplx/core/texts/BaseXXConverter_tst.java create mode 100644 100_core/src/gplx/core/texts/CharStream.java create mode 100644 100_core/src/gplx/core/texts/CharStream_tst.java create mode 100644 100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch.java create mode 100644 100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_.java create mode 100644 100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_tst.java create mode 100644 100_core/src/gplx/core/texts/RegxPatn_cls_like.java create mode 100644 100_core/src/gplx/core/texts/RegxPatn_cls_like_.java create mode 100644 100_core/src/gplx/core/texts/RegxPatn_cls_like_tst.java create mode 100644 100_core/src/gplx/core/texts/StringTableBldr.java create mode 100644 100_core/src/gplx/core/texts/StringTableBldr_tst.java create mode 100644 100_core/src/gplx/core/texts/StringTableCol.java create mode 100644 100_core/src/gplx/core/texts/StringTableColAlign.java create mode 100644 100_core/src/gplx/core/threads/Gfo_lock.java create mode 100644 100_core/src/gplx/core/threads/Thread_adp.java create mode 100644 100_core/src/gplx/core/threads/Thread_adp_.java create mode 100644 100_core/src/gplx/core/threads/Thread_adp_mgr.java create mode 100644 100_core/src/gplx/core/threads/Thread_halt_cbk.java create mode 100644 100_core/src/gplx/core/threads/Thread_halt_cbk_.java create mode 100644 100_core/src/gplx/core/threads/Thread_halt_wkr.java create mode 100644 100_core/src/gplx/core/threads/poolables/Gfo_poolable_itm.java create mode 100644 100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr.java create mode 100644 100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_.java create mode 100644 100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_tst.java create mode 100644 100_core/src/gplx/core/threads/utils/Gfo_blocking_queue.java create mode 100644 100_core/src/gplx/core/threads/utils/Gfo_countdown_latch.java create mode 100644 100_core/src/gplx/core/times/DateAdp_parser.java create mode 100644 100_core/src/gplx/core/times/DateAdp_parser_tst.java create mode 100644 100_core/src/gplx/core/times/Time_span__basic_tst.java create mode 100644 100_core/src/gplx/core/times/Time_span__parse_tst.java create mode 100644 100_core/src/gplx/core/times/Time_span__to_str_tst.java create mode 100644 100_core/src/gplx/core/type_xtns/BoolClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/ByteClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/ClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/ClassXtnPool.java create mode 100644 100_core/src/gplx/core/type_xtns/ClassXtn_base.java create mode 100644 100_core/src/gplx/core/type_xtns/DateAdpClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/DateAdpClassXtn_tst.java create mode 100644 100_core/src/gplx/core/type_xtns/DecimalAdpClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/DoubleClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/FloatClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/IntClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/IoUrlClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/LongClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/ObjectClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/StringClassXtn.java create mode 100644 100_core/src/gplx/core/type_xtns/TimeSpanAdpClassXtn.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataRdrOpts.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataRdr_.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataRdr_csv_dat_tst.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_dat_tst.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_hdr_tst.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_misc_tst.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataRdr_layout_tst.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataWtr.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataWtr_.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataWtr_csv_tst.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvDataWtr_tbls_tst.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvHeaderList.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvStoreLayout.java create mode 100644 100_core/src/gplx/langs/dsvs/DsvSymbols.java create mode 100644 100_core/src/gplx/langs/gfs/GfsCore.java create mode 100644 100_core/src/gplx/langs/gfs/GfsCoreHelp.java create mode 100644 100_core/src/gplx/langs/gfs/GfsCore_.java create mode 100644 100_core/src/gplx/langs/gfs/GfsCore_tst.java create mode 100644 100_core/src/gplx/langs/gfs/GfsLibIni.java create mode 100644 100_core/src/gplx/langs/gfs/GfsLibIni_core.java create mode 100644 100_core/src/gplx/langs/gfs/GfsRegy.java create mode 100644 100_core/src/gplx/langs/gfs/GfsTypeNames.java create mode 100644 100_core/src/gplx/langs/gfs/Gfs_Date_tst.java create mode 100644 100_core/src/gplx/langs/htmls/Url_encoder_interface.java create mode 100644 100_core/src/gplx/langs/htmls/Url_encoder_interface_same.java create mode 100644 100_core/src/gplx/langs/htmls/entitys/Gfh_entity_.java create mode 100644 100_core/src/gplx/langs/htmls/entitys/Gfh_entity_itm.java create mode 100644 100_core/src/gplx/langs/htmls/entitys/Gfh_entity_trie.java create mode 100644 100_core/src/gplx/langs/regxs/Regx_adp.java create mode 100644 100_core/src/gplx/langs/regxs/Regx_adp_.java create mode 100644 100_core/src/gplx/langs/regxs/Regx_adp__tst.java create mode 100644 100_core/src/gplx/langs/regxs/Regx_bldr.java create mode 100644 100_core/src/gplx/langs/regxs/Regx_group.java create mode 100644 100_core/src/gplx/langs/regxs/Regx_match.java create mode 100644 100_core/src/gplx/langs/xmls/HierStrBldr.java create mode 100644 100_core/src/gplx/langs/xmls/HierStrBldr_tst.java create mode 100644 100_core/src/gplx/langs/xmls/XmlAtr.java create mode 100644 100_core/src/gplx/langs/xmls/XmlAtrList.java create mode 100644 100_core/src/gplx/langs/xmls/XmlDoc.java create mode 100644 100_core/src/gplx/langs/xmls/XmlDoc_.java create mode 100644 100_core/src/gplx/langs/xmls/XmlDoc_tst.java create mode 100644 100_core/src/gplx/langs/xmls/XmlFileSplitter.java create mode 100644 100_core/src/gplx/langs/xmls/XmlFileSplitterOpts.java create mode 100644 100_core/src/gplx/langs/xmls/XmlFileSplitter_tst.java create mode 100644 100_core/src/gplx/langs/xmls/XmlNde.java create mode 100644 100_core/src/gplx/langs/xmls/XmlNdeList.java create mode 100644 100_core/src/gplx/langs/xmls/XmlSplitRdr.java create mode 100644 100_core/src/gplx/langs/xmls/XmlSplitWtr.java create mode 100644 100_core/src/gplx/langs/xmls/Xpath_.java create mode 100644 100_core/src/gplx/langs/xmls/Xpath__tst.java create mode 100644 100_core/tst/gplx/GfoMsg_rdr_tst.java create mode 100644 100_core/tst/gplx/GfoTreeBldr_fxt.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_dir_basic_base.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_dir_basic_memory_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_dir_basic_system_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_dir_deep_base.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_dir_deep_memory_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_dir_deep_system_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_fil_basic_base.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_fil_basic_memory_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_fil_basic_system_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_fil_xfer_base.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_fil_xfer_memory_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_fil_xfer_system_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_stream_xfer_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_xrg_queryDir_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoEngine_xrg_recycleFil_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoItmDir_FetchDeepOrNull_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoUrlInfo_alias_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoUrl_lnx_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoUrl_map_tst.java create mode 100644 100_core/tst/gplx/core/ios/IoUrl_wnt_tst.java create mode 100644 100_core/tst/gplx/core/stores/GfoNdeRdr_read_tst.java create mode 100644 100_core/tst/gplx/core/stores/GfoNdeRdr_tst.java create mode 100644 100_core/tst/gplx/core/stores/xmls/XmlDataRdr_tst.java create mode 100644 100_core/tst/gplx/core/stores/xmls/XmlDataWtr_tst.java create mode 100644 110_gfml/.classpath create mode 100644 110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java create mode 100644 110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java create mode 100644 110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java create mode 100644 110_gfml/src_100_tkn/gplx/gfml/GfmlObjList.java create mode 100644 110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java create mode 100644 110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java create mode 100644 110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java create mode 100644 110_gfml/src_100_tkn/gplx/gfml/IntObjHash.java create mode 100644 110_gfml/src_200_type/gplx/gfml/GfmlFld.java create mode 100644 110_gfml/src_200_type/gplx/gfml/GfmlFldList.java create mode 100644 110_gfml/src_200_type/gplx/gfml/GfmlType.java create mode 100644 110_gfml/src_200_type/gplx/gfml/GfmlTypeCompiler.java create mode 100644 110_gfml/src_200_type/gplx/gfml/GfmlTypeHash.java create mode 100644 110_gfml/src_200_type/gplx/gfml/GfmlTypeMakr.java create mode 100644 110_gfml/src_200_type/gplx/gfml/GfmlTypeMgr.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlAtr.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlDoc.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlDocLxrs.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlDocPos.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlDocWtr_.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlDoc_.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlItm.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlItmHnds.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlItmKeys.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlNde.java create mode 100644 110_gfml/src_300_gdoc/gplx/gfml/GfmlScopeItm.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlPragma.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlPragmaDefault.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlPragmaLxrFrm.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlPragmaLxrSym.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlPragmaType.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlPragmaVar.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlVarCtx.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlVarItm.java create mode 100644 110_gfml/src_400_pragma/gplx/gfml/GfmlVarTkn.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlBldr.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlBldrCmd.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlBldrCmds.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlBldr_.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlFrame.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlFrame_.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlFrame_nde.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlFrame_ndeTknMgr.java create mode 100644 110_gfml/src_500_build/gplx/gfml/GfmlStringHighlighter.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/GfmlDataNde.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/GfmlDataRdr.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/GfmlDataRdr_base.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/GfmlDataWtr.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/GfmlDataWtrOpts.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/GfoMsgParser_gfml.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/SqlConsts.java create mode 100644 110_gfml/src_600_rdrWtr/gplx/gfml/SqlDoc.java create mode 100644 110_gfml/tst/gplx/gfml/GfmlDataRdr_tst.java create mode 100644 110_gfml/tst/gplx/gfml/yfxts_GfmlParse_fxt.java create mode 100644 110_gfml/tst/gplx/gfml/yfxts_GfmlTypeCompiler_fxt.java create mode 100644 110_gfml/tst/gplx/gfml/ymoks_GfmlAtr_GfmlNde_mok.java create mode 100644 110_gfml/tst/gplx/gfml/ymoks_GfmlTkn_mok.java create mode 100644 110_gfml/tst/gplx/gfml/ymoks_GfmlTyp_GfmlFld_mok.java create mode 100644 110_gfml/tst/gplx/gfml/ymoks_UsrMsg_mok.java create mode 100644 110_gfml/tst/gplx/gfml/z011_IntObjHash_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z012_GfmlTrie_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z015_GfmlDocPos_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z016_GfmlScopeList_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z017_GfmlStringHighlighter_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z051_GfmlFldPool_keyed_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z081_GfmlDataWtr_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z082_GfmlDataWtrOpts_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z091_GfmlLxr_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z101_core_ndeInline_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z102_core_whitespace_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z103_core_elmKey_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z111_core_comment0_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z112_core_comment1_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z120_quotes_eval0_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z121_quotes_quotes0_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z122_quotes_quote0_eval0_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z123_quotes_quoteBlock_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z124_quotes_quoteFold_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z151_ndeSubs_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z152_ndeSubs_data_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z161_ndeHdrs_inline_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z162_ndeHdrs_err_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z163_ndeHdrs_body_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z164_hdeHdrs_data_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z181_ndeDots_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z182_ndeDots_subs_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z183_ndeDots_parens_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z184_ndeDots_atrSpr_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z191_ndeProps_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z192_ndeProps_dots_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z400_GfmlTypeMakr_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z401_types_compile_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z402_types_compile_implicit_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z403_types_compile_default_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z411_types_apply_atrs_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z421_types_apply_ndes_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z422_types_apply_ndes_multi_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z423_types_apply_ndes_misc_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z424_types_apply_ndes_nest_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z441_types_parse_basic_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z442_types_parse_default_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z443_types_parse_keyd_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z450_fx_GfmlDefaultItem_fxt.java create mode 100644 110_gfml/tst/gplx/gfml/z451_dflts_compile_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z452_dflts_exec_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z455_dflts_scope_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z456_dflts_parse_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z481_vars_compile_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z482_vars_parse_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z501_lxr_parse_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z601_edit_atr_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z602_edit_nde_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z801_useCase_DataRdr_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z803_useCase_KbdKeyboard_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z811_useCase_GfmlIoSql_tst.java create mode 100644 110_gfml/tst/gplx/gfml/z901_perf_tst.java create mode 100644 140_dbs/.classpath create mode 100644 140_dbs/src/gplx/core/srls/Dbmeta_dat_itm.java create mode 100644 140_dbs/src/gplx/core/srls/Dbmeta_dat_mgr.java create mode 100644 140_dbs/src/gplx/core/srls/Gfo_srl_ctx.java create mode 100644 140_dbs/src/gplx/core/srls/Gfo_srl_itm.java create mode 100644 140_dbs/src/gplx/core/srls/Gfo_srl_mgr_rdr.java create mode 100644 140_dbs/src/gplx/core/srls/Gfo_srl_mgr_wtr.java create mode 100644 140_dbs/src/gplx/core/stores/DbMaprArg.java create mode 100644 140_dbs/src/gplx/core/stores/DbMaprItm.java create mode 100644 140_dbs/src/gplx/core/stores/DbMaprMgr.java create mode 100644 140_dbs/src/gplx/core/stores/DbMaprMgr_tst.java create mode 100644 140_dbs/src/gplx/core/stores/DbMaprRdr.java create mode 100644 140_dbs/src/gplx/core/stores/DbMaprWtr.java create mode 100644 140_dbs/src/gplx/core/stores/Db_data_rdr.java create mode 100644 140_dbs/src/gplx/core/stores/Db_data_rdr_.java create mode 100644 140_dbs/src/gplx/core/stores/MockDiscObj.java create mode 100644 140_dbs/src/gplx/dbs/Db_attach_itm.java create mode 100644 140_dbs/src/gplx/dbs/Db_attach_mgr.java create mode 100644 140_dbs/src/gplx/dbs/Db_attach_mgr__tst.java create mode 100644 140_dbs/src/gplx/dbs/Db_cmd_mode.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_bldr.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_bldr_data.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_bldr_wkr.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_info.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_info_.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_info__base.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_info_tst.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_pool.java create mode 100644 140_dbs/src/gplx/dbs/Db_conn_utl.java create mode 100644 140_dbs/src/gplx/dbs/Db_crt_.java create mode 100644 140_dbs/src/gplx/dbs/Db_crt_tst.java create mode 100644 140_dbs/src/gplx/dbs/Db_idx_itm.java create mode 100644 140_dbs/src/gplx/dbs/Db_mock_cell.java create mode 100644 140_dbs/src/gplx/dbs/Db_mock_row.java create mode 100644 140_dbs/src/gplx/dbs/Db_null.java create mode 100644 140_dbs/src/gplx/dbs/Db_qry.java create mode 100644 140_dbs/src/gplx/dbs/Db_qry_.java create mode 100644 140_dbs/src/gplx/dbs/Db_qry_fxt.java create mode 100644 140_dbs/src/gplx/dbs/Db_rdr.java create mode 100644 140_dbs/src/gplx/dbs/Db_rdr_.java create mode 100644 140_dbs/src/gplx/dbs/Db_rdr__basic.java create mode 100644 140_dbs/src/gplx/dbs/Db_sql_.java create mode 100644 140_dbs/src/gplx/dbs/Db_sql_select.java create mode 100644 140_dbs/src/gplx/dbs/Db_stmt.java create mode 100644 140_dbs/src/gplx/dbs/Db_stmt_.java create mode 100644 140_dbs/src/gplx/dbs/Db_stmt_bldr.java create mode 100644 140_dbs/src/gplx/dbs/Db_tbl.java create mode 100644 140_dbs/src/gplx/dbs/Db_tbl_.java create mode 100644 140_dbs/src/gplx/dbs/Db_tbl_owner.java create mode 100644 140_dbs/src/gplx/dbs/Dbmeta_fld_itm.java create mode 100644 140_dbs/src/gplx/dbs/Dbmeta_fld_list.java create mode 100644 140_dbs/src/gplx/dbs/Dbmeta_fld_tid.java create mode 100644 140_dbs/src/gplx/dbs/Dbmeta_idx_itm.java create mode 100644 140_dbs/src/gplx/dbs/Dbmeta_tbl_itm.java create mode 100644 140_dbs/src/gplx/dbs/conn_props/Db_conn_props_mgr.java create mode 100644 140_dbs/src/gplx/dbs/diffs/Gdif_core.java create mode 100644 140_dbs/src/gplx/dbs/diffs/Gdif_db.java create mode 100644 140_dbs/src/gplx/dbs/diffs/Gdif_db_.java create mode 100644 140_dbs/src/gplx/dbs/diffs/Gfdb_diff_cmd.java create mode 100644 140_dbs/src/gplx/dbs/diffs/Gfdb_diff_tbl.java create mode 100644 140_dbs/src/gplx/dbs/diffs/Gfdb_diff_tbl_mgr.java create mode 100644 140_dbs/src/gplx/dbs/diffs/Gfdb_rdr_utl_.java create mode 100644 140_dbs/src/gplx/dbs/diffs/builds/Gdif_bldr_ctx.java create mode 100644 140_dbs/src/gplx/dbs/diffs/builds/Gfdb_diff_bldr.java create mode 100644 140_dbs/src/gplx/dbs/diffs/builds/Gfdb_diff_bldr_tst.java create mode 100644 140_dbs/src/gplx/dbs/diffs/builds/Gfdb_diff_rdr_comparer.java create mode 100644 140_dbs/src/gplx/dbs/diffs/builds/Gfdb_diff_wkr.java create mode 100644 140_dbs/src/gplx/dbs/diffs/builds/Gfdb_diff_wkr__db.java create mode 100644 140_dbs/src/gplx/dbs/diffs/cmds/Gfdb_diff_cmd.java create mode 100644 140_dbs/src/gplx/dbs/diffs/cmds/Gfdb_diff_cmd__idx__create.java create mode 100644 140_dbs/src/gplx/dbs/diffs/cmds/Gfdb_diff_cmd_sql_bldr_tst.java create mode 100644 140_dbs/src/gplx/dbs/diffs/itms/Gdif_cmd_itm.java create mode 100644 140_dbs/src/gplx/dbs/diffs/itms/Gdif_cmd_tbl.java create mode 100644 140_dbs/src/gplx/dbs/diffs/itms/Gdif_job_itm.java create mode 100644 140_dbs/src/gplx/dbs/diffs/itms/Gdif_job_tbl.java create mode 100644 140_dbs/src/gplx/dbs/diffs/itms/Gdif_txn_itm.java create mode 100644 140_dbs/src/gplx/dbs/diffs/itms/Gdif_txn_tbl.java create mode 100644 140_dbs/src/gplx/dbs/engines/Db_engine.java create mode 100644 140_dbs/src/gplx/dbs/engines/Db_engine_sql_base.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_conn_info.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_db_fxt.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_engine.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_exec_select.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_exec_select_tst.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_qry_set.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_rdr.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_row.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_stmt.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_stmt_args.java create mode 100644 140_dbs/src/gplx/dbs/engines/mems/Mem_tbl.java create mode 100644 140_dbs/src/gplx/dbs/engines/mysql/Mysql_conn_info.java create mode 100644 140_dbs/src/gplx/dbs/engines/mysql/Mysql_engine.java create mode 100644 140_dbs/src/gplx/dbs/engines/noops/Noop_conn_info.java create mode 100644 140_dbs/src/gplx/dbs/engines/noops/Noop_engine.java create mode 100644 140_dbs/src/gplx/dbs/engines/postgres/Postgres_conn_info.java create mode 100644 140_dbs/src/gplx/dbs/engines/postgres/Postgres_engine.java create mode 100644 140_dbs/src/gplx/dbs/engines/sqlite/Sqlite_conn_info.java create mode 100644 140_dbs/src/gplx/dbs/engines/sqlite/Sqlite_engine.java create mode 100644 140_dbs/src/gplx/dbs/engines/sqlite/Sqlite_engine_.java create mode 100644 140_dbs/src/gplx/dbs/engines/sqlite/Sqlite_pragma.java create mode 100644 140_dbs/src/gplx/dbs/engines/sqlite/Sqlite_schema_mgr.java create mode 100644 140_dbs/src/gplx/dbs/engines/sqlite/Sqlite_tid.java create mode 100644 140_dbs/src/gplx/dbs/engines/sqlite/Sqlite_txn_mgr.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbConnectInfo_tst.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbDatabase.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbDbLoadMgr.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbDbLoadMgr_tst.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbDbSaveMgr.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbDbSaveMgr_tst.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbDelete.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbEngine.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbFile.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbFileList.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbFlush.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbFlush_tst.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbInsert.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbSelect.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbStores.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbTable.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbTableList.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/TdbUpdate.java create mode 100644 140_dbs/src/gplx/dbs/engines/tdbs/Tdb_conn_info.java create mode 100644 140_dbs/src/gplx/dbs/metas/Dbmeta_fld_mgr.java create mode 100644 140_dbs/src/gplx/dbs/metas/Dbmeta_idx_fld.java create mode 100644 140_dbs/src/gplx/dbs/metas/Dbmeta_idx_mgr.java create mode 100644 140_dbs/src/gplx/dbs/metas/Dbmeta_itm_tid.java create mode 100644 140_dbs/src/gplx/dbs/metas/Dbmeta_reload_cmd.java create mode 100644 140_dbs/src/gplx/dbs/metas/Dbmeta_reload_cmd_.java create mode 100644 140_dbs/src/gplx/dbs/metas/Dbmeta_tbl_mgr.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Dbmeta_fld_wkr__base.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Dbmeta_parser__fld.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Dbmeta_parser__fld_tst.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Dbmeta_parser__idx.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Dbmeta_parser__idx_tst.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Dbmeta_parser__tbl.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Dbmeta_parser__tbl_tst.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Sql_bry_rdr.java create mode 100644 140_dbs/src/gplx/dbs/metas/parsers/Sql_bry_rdr_tst.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_arg.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_arg_owner.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry__select_cmd.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry__select_in_tbl.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_delete.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_dml_tst.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_flush.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_insert.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_select_tst.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_sql.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_sql_tst.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_qry_update.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_stmt_cmd.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_stmt_sql.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_stmt_sql_tst.java create mode 100644 140_dbs/src/gplx/dbs/qrys/Db_val_type.java create mode 100644 140_dbs/src/gplx/dbs/qrys/bats/Db_batch__journal_off.java create mode 100644 140_dbs/src/gplx/dbs/qrys/bats/Db_batch__journal_wal.java create mode 100644 140_dbs/src/gplx/dbs/qrys/bats/Db_batch_grp.java create mode 100644 140_dbs/src/gplx/dbs/qrys/bats/Db_batch_itm.java create mode 100644 140_dbs/src/gplx/dbs/qrys/bats/Db_batch_mgr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/Sql_qry_wtr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/Sql_qry_wtr_.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Db_obj_ary_crt.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Db_obj_ary_fld.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Db_obj_ary_tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_from_clause.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_group_clause.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_join_fld.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_order_clause.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_order_fld.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_order_fld_sorter.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_select_clause.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_select_fld.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_select_fld_func.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_select_fld_list.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_tbl_itm.java create mode 100644 140_dbs/src/gplx/dbs/sqls/itms/Sql_where_clause.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_core_wtr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_core_wtr__mysql.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_core_wtr__sqlite.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_core_wtr_fxt.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_from_wtr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_from_wtr_tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_qry_wtr__ansi__tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_qry_wtr__iosql__tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_schema_wtr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_schema_wtr_tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_select_wtr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_select_wtr_tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_val_wtr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_val_wtr_tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_where_wtr.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_where_wtr_tst.java create mode 100644 140_dbs/src/gplx/dbs/sqls/wtrs/Sql_wtr_ctx.java create mode 100644 140_dbs/src/gplx/dbs/stmts/Db_stmt_arg.java create mode 100644 140_dbs/src/gplx/dbs/stmts/Db_stmt_arg_list.java create mode 100644 140_dbs/src/gplx/dbs/stmts/Db_stmt_mgr.java create mode 100644 140_dbs/src/gplx/dbs/sys/Db_sys_mgr.java create mode 100644 140_dbs/src/gplx/dbs/sys/Db_sys_mgr_tst.java create mode 100644 140_dbs/src/gplx/dbs/sys/Db_sys_tbl.java create mode 100644 140_dbs/src/gplx/dbs/utls/Db_cmd_backup.java create mode 100644 140_dbs/src/gplx/dbs/utls/Db_cmd_backup_tst.java create mode 100644 140_dbs/src/gplx/dbs/utls/Db_in_wkr__base.java create mode 100644 140_dbs/src/gplx/dbs/utls/Db_tbl__crud_.java create mode 100644 140_dbs/src/gplx/dbs/utls/PoolIds.java create mode 100644 140_dbs/src/gplx/dbs/utls/PoolIds_tst.java create mode 100644 140_dbs/tst/gplx/dbs/Db_conn_fxt.java create mode 100644 140_dbs/tst/gplx/dbs/GfoNdeTstr.java create mode 100644 140_dbs/tst/gplx/dbs/engines/db_CrudOps_tst.java create mode 100644 140_dbs/tst/gplx/dbs/engines/db_DataTypes_tst.java create mode 100644 140_dbs/tst/gplx/dbs/groupBys/GroupBys_base_tst.java create mode 100644 140_dbs/tst/gplx/dbs/groupBys/GroupBys_mysql_tst.java create mode 100644 140_dbs/tst/gplx/dbs/groupBys/GroupBys_tdb_tst.java create mode 100644 140_dbs/tst/gplx/dbs/insertIntos/InsertIntos_base_tst.java create mode 100644 140_dbs/tst/gplx/dbs/insertIntos/InsertIntos_mysql_tst.java create mode 100644 140_dbs/tst/gplx/dbs/insertIntos/InsertIntos_tdb_tst.java create mode 100644 140_dbs/tst/gplx/dbs/joins/Joins_base_tst.java create mode 100644 140_dbs/tst/gplx/dbs/joins/Joins_tdb_tst.java create mode 100644 140_dbs/tst/gplx/dbs/orderBys/OrderBys_base_tst.java create mode 100644 140_dbs/tst/gplx/dbs/orderBys/OrderBys_tdb_tst.java create mode 100644 140_dbs/xtn/gplx/dbs/Bug_Utf8.java create mode 100644 140_dbs/xtn/gplx/dbs/SqliteDbMain.java create mode 100644 150_gfui/.classpath create mode 100644 150_gfui/lib/swt.jar create mode 100644 150_gfui/src/gplx/gfui/DirInt.java create mode 100644 150_gfui/src/gplx/gfui/GfuiAlign.java create mode 100644 150_gfui/src/gplx/gfui/GfuiAlign_.java create mode 100644 150_gfui/src/gplx/gfui/GfuiAxisType.java create mode 100644 150_gfui/src/gplx/gfui/GfuiBorderEdge.java create mode 100644 150_gfui/src/gplx/gfui/PointAdp.java create mode 100644 150_gfui/src/gplx/gfui/PointAdp_.java create mode 100644 150_gfui/src/gplx/gfui/RectAdp.java create mode 100644 150_gfui/src/gplx/gfui/RectAdpF.java create mode 100644 150_gfui/src/gplx/gfui/RectAdp_.java create mode 100644 150_gfui/src/gplx/gfui/SizeAdp.java create mode 100644 150_gfui/src/gplx/gfui/SizeAdpF.java create mode 100644 150_gfui/src/gplx/gfui/SizeAdpF_.java create mode 100644 150_gfui/src/gplx/gfui/SizeAdp_.java create mode 100644 150_gfui/src/gplx/gfui/controls/GfuiBorderMgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/GfuiBorderMgr_tst.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/DataBndr_whenEvt_execCmd.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiBnd_box_status.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiCheckListBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiCheckListPanel.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiFormPanel.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiIoDialogUtl.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiIoUrlSelectBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiIoUrlSelectBox_.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiMoveElemBnd.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiMoveElemBtn.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiMoveElemBtn_tst.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiStatusBar.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiStatusBarBnd.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiStatusBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiStatusBoxBnd.java create mode 100644 150_gfui/src/gplx/gfui/controls/customs/GfuiStatusBox_.java create mode 100644 150_gfui/src/gplx/gfui/controls/elems/GfuiElem.java create mode 100644 150_gfui/src/gplx/gfui/controls/elems/GfuiElemBase.java create mode 100644 150_gfui/src/gplx/gfui/controls/elems/GfuiElemBase_.java create mode 100644 150_gfui/src/gplx/gfui/controls/elems/GfuiElemKeys.java create mode 100644 150_gfui/src/gplx/gfui/controls/elems/GfuiElemList.java create mode 100644 150_gfui/src/gplx/gfui/controls/elems/GfuiElem_.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwBoxListener.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwCbkHost.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwCbkHost_.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwCheckListBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwCheckListBox_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwComboBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwComboBox_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwCore_base.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwCore_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwCore_mock.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwElem.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwElemFactory_.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwElemFactory_base.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwElemFactory_cls_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwElemFactory_cls_mock.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwElem_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwElem_mock_base.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwListBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwListBox_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwTextBox_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwTextFld.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwTextHtml.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwTextHtml_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwTextMemo.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwTextMemo_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwWin.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/GxwWin_lang.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/Gxw_html.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/Gxw_html_load_tid_.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/Gxw_tab_itm.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/Gxw_tab_mgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/gxws/MockForm.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiBtn.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiBtnClickBnd.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiBtn_.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiChkBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiChkBox_.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiComboBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiLbl.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiLbl_.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiListBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiTextBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiTextBox_.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/GfuiTextMemo.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/Gfui_html.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/Gfui_tab_itm.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/Gfui_tab_itm_data.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/Gfui_tab_itm_data_tst.java create mode 100644 150_gfui/src/gplx/gfui/controls/standards/Gfui_tab_mgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabBnd.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabBox.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabBoxEvt_orderChanged.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabBoxEvt_tabSelect.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabBoxMgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabBox_.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabBox_tst.java create mode 100644 150_gfui/src/gplx/gfui/controls/tabs/TabPnlItm.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfoConsoleWin.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiBnd_win_host.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiCmdForm.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiFocusMgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiFocusOrderer.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiFocusOrderer_tst.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiFocusXferBnd.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiForm_menu.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiMenuBar.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiMenuFormUtl.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiQuitMode.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiTipTextMgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiWin.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiWinFocusMgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiWinKeyCmdMgr.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiWin_.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GfuiWin_toaster.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GxwBorderFactory.java create mode 100644 150_gfui/src/gplx/gfui/controls/windows/GxwWinNpi.java create mode 100644 150_gfui/src/gplx/gfui/draws/ColorAdp.java create mode 100644 150_gfui/src/gplx/gfui/draws/ColorAdpCache.java create mode 100644 150_gfui/src/gplx/gfui/draws/ColorAdp_.java create mode 100644 150_gfui/src/gplx/gfui/draws/ColorAdp__tst.java create mode 100644 150_gfui/src/gplx/gfui/draws/FontAdp.java create mode 100644 150_gfui/src/gplx/gfui/draws/FontAdpCache.java create mode 100644 150_gfui/src/gplx/gfui/draws/FontStyleAdp.java create mode 100644 150_gfui/src/gplx/gfui/draws/FontStyleAdp_.java create mode 100644 150_gfui/src/gplx/gfui/draws/PenAdp.java create mode 100644 150_gfui/src/gplx/gfui/draws/PenAdp_.java create mode 100644 150_gfui/src/gplx/gfui/draws/SolidBrushAdp.java create mode 100644 150_gfui/src/gplx/gfui/draws/SolidBrushAdp_.java create mode 100644 150_gfui/src/gplx/gfui/envs/ClipboardAdp_.java create mode 100644 150_gfui/src/gplx/gfui/envs/ClipboardAdp__tst.java create mode 100644 150_gfui/src/gplx/gfui/envs/CursorAdp.java create mode 100644 150_gfui/src/gplx/gfui/envs/ScreenAdp.java create mode 100644 150_gfui/src/gplx/gfui/envs/ScreenAdp_.java create mode 100644 150_gfui/src/gplx/gfui/envs/ScreenAdp_tst.java create mode 100644 150_gfui/src/gplx/gfui/envs/TimerAdp.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxAdp.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxAdpBase.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxAdpMok.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxAdp_.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxItm.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxItmList.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxItm_base.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxLineItm.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxRectItm.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxStringData.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/GfxStringItm.java create mode 100644 150_gfui/src/gplx/gfui/gfxs/PaintArgs.java create mode 100644 150_gfui/src/gplx/gfui/imgs/IconAdp.java create mode 100644 150_gfui/src/gplx/gfui/imgs/ImageAdp.java create mode 100644 150_gfui/src/gplx/gfui/imgs/ImageAdp_.java create mode 100644 150_gfui/src/gplx/gfui/imgs/ImageAdp_base.java create mode 100644 150_gfui/src/gplx/gfui/imgs/ImageAdp_null.java create mode 100644 150_gfui/src/gplx/gfui/imgs/ImageAdp_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/GfuiClickKeyMgr_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptArg.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptArgChainMgr_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptArg_.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptArg_parser_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBnd.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBndMgr.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBndMgr_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBnd_.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBnd_chkBox.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBnd_txt_cmd.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBnd_txt_range.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBnd_upDownRange.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptBndsOwner.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptCfg.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptCfgItm.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptCfgRegy.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptCfg_.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptCfg_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEventData.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEventMgr.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEventType.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEventType_.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEventType_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEvtDataKey.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEvtDataKeyHeld.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptEvtDataMouse.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptKey.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptKeyStrMgr.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptKeyStrMgr_tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptKey_.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptKey__tst.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptMouseBtn.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptMouseBtn_.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptMouseMove.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptMouseWheel.java create mode 100644 150_gfui/src/gplx/gfui/ipts/IptMouseWheel_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/GfoFactory_gfui.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/GfsLibIni_gfui.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/GfuiEnv_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/GfuiInvkCmd.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/GfuiInvkCmd_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_clipboard.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_clipboard_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_dlg_file.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_dlg_file_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_dlg_msg.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_dlg_msg_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_kit.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_kit_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_kit_base.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_mnu_grp.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_mnu_grp_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_mnu_itm.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Gfui_mnu_itm_.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Mem_html.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Mem_kit.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Swing_kit.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/Swt_kit.java create mode 100644 150_gfui/src/gplx/gfui/kits/core/TxtFindMgr.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_app_browser.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_app_main.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_btn.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_btn_no_border.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_clipboard.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_combo.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_combo_ctrl.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_control.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_control_.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_core_cmds.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_core_lnrs.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_dlg_file.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_dlg_msg.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_html.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_html_eval_rslt.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_img.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_lbl.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_lnr__menu_detect.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_popup_grp.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_tab_itm.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_tab_mgr.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_text.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_text_w_border.java create mode 100644 150_gfui/src/gplx/gfui/kits/swts/Swt_win.java create mode 100644 150_gfui/src/gplx/gfui/layouts/GftBand.java create mode 100644 150_gfui/src/gplx/gfui/layouts/GftBand_tst.java create mode 100644 150_gfui/src/gplx/gfui/layouts/GftCell.java create mode 100644 150_gfui/src/gplx/gfui/layouts/GftGrid.java create mode 100644 150_gfui/src/gplx/gfui/layouts/GftGrid_fx.java create mode 100644 150_gfui/src/gplx/gfui/layouts/GftItem.java create mode 100644 150_gfui/src/gplx/gfui/layouts/GftSizeCalc.java create mode 100644 400_xowa/.classpath create mode 100644 400_xowa/src/gplx/core/brys/Bit_.java create mode 100644 400_xowa/src/gplx/core/brys/Bit__tst.java create mode 100644 400_xowa/src/gplx/core/brys/Bit_heap_rdr.java create mode 100644 400_xowa/src/gplx/core/brys/Bit_heap_rdr_tst.java create mode 100644 400_xowa/src/gplx/core/brys/Bit_heap_wtr.java create mode 100644 400_xowa/src/gplx/core/brys/Bit_heap_wtr_tst.java create mode 100644 400_xowa/src/gplx/core/brys/Bry_bldr.java create mode 100644 400_xowa/src/gplx/core/brys/Bry_comparer.java create mode 100644 400_xowa/src/gplx/core/brys/Bry_diff_.java create mode 100644 400_xowa/src/gplx/core/brys/Bry_diff_tst.java create mode 100644 400_xowa/src/gplx/core/brys/Int_flag_bldr.java create mode 100644 400_xowa/src/gplx/core/brys/Int_flag_bldr_.java create mode 100644 400_xowa/src/gplx/core/brys/Int_flag_bldr__tst.java create mode 100644 400_xowa/src/gplx/core/brys/Mid_able.java create mode 100644 400_xowa/src/gplx/core/brys/evals/Bry_eval_mgr.java create mode 100644 400_xowa/src/gplx/core/brys/evals/Bry_eval_mgr__tst.java create mode 100644 400_xowa/src/gplx/core/brys/evals/Bry_eval_wkr.java create mode 100644 400_xowa/src/gplx/core/btries/Btrie_u8_mgr_tst.java create mode 100644 400_xowa/src/gplx/core/caches/Gfo_cache_data.java create mode 100644 400_xowa/src/gplx/core/caches/Gfo_cache_mgr.java create mode 100644 400_xowa/src/gplx/core/caches/Gfo_cache_mgr_base.java create mode 100644 400_xowa/src/gplx/core/caches/Gfo_cache_mgr_bry.java create mode 100644 400_xowa/src/gplx/core/caches/Gfo_cache_mgr_tst.java create mode 100644 400_xowa/src/gplx/core/consoles/Gfo_cmd_arg_itm.java create mode 100644 400_xowa/src/gplx/core/consoles/Gfo_cmd_arg_itm_.java create mode 100644 400_xowa/src/gplx/core/consoles/Gfo_cmd_arg_mgr.java create mode 100644 400_xowa/src/gplx/core/consoles/Gfo_cmd_arg_mgr_.java create mode 100644 400_xowa/src/gplx/core/consoles/Gfo_cmd_arg_mgr_printer.java create mode 100644 400_xowa/src/gplx/core/consoles/Gfo_cmd_arg_mgr_tst.java create mode 100644 400_xowa/src/gplx/core/enums/Gfo_enum_grp.java create mode 100644 400_xowa/src/gplx/core/flds/Gfo_fld_base.java create mode 100644 400_xowa/src/gplx/core/flds/Gfo_fld_rdr.java create mode 100644 400_xowa/src/gplx/core/flds/Gfo_fld_rdr_tst.java create mode 100644 400_xowa/src/gplx/core/flds/Gfo_fld_wtr.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_ary.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_fld.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_grp.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_grp_.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_nde.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_rdr__json.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_rdr__json_tst.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_wtr__json.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_wtr__json_fxt.java create mode 100644 400_xowa/src/gplx/core/gfobjs/Gfobj_wtr__json_tst.java create mode 100644 400_xowa/src/gplx/core/intls/String_surrogate_utl.java create mode 100644 400_xowa/src/gplx/core/intls/String_surrogate_utl_tst.java create mode 100644 400_xowa/src/gplx/core/intls/ucas/Uca_collator.java create mode 100644 400_xowa/src/gplx/core/intls/ucas/Uca_collator_.java create mode 100644 400_xowa/src/gplx/core/intls/ucas/Uca_collator__icu__4_8.java create mode 100644 400_xowa/src/gplx/core/intls/ucas/Uca_ltr_extractor.java create mode 100644 400_xowa/src/gplx/core/ios/BinaryHeap_Io_line_rdr.java create mode 100644 400_xowa/src/gplx/core/ios/BinaryHeap_Io_line_rdr_tst.java create mode 100644 400_xowa/src/gplx/core/ios/Io_buffer_rdr.java create mode 100644 400_xowa/src/gplx/core/ios/Io_buffer_rdr_tst.java create mode 100644 400_xowa/src/gplx/core/ios/Io_fil_chkr.java create mode 100644 400_xowa/src/gplx/core/ios/Io_line_rdr.java create mode 100644 400_xowa/src/gplx/core/ios/Io_line_rdr_key_gen.java create mode 100644 400_xowa/src/gplx/core/ios/Io_line_rdr_key_gen_.java create mode 100644 400_xowa/src/gplx/core/ios/Io_line_rdr_tst.java create mode 100644 400_xowa/src/gplx/core/ios/Io_make_cmd.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort_cmd.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort_filCmd.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort_fil_basic.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort_misc_tst.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort_split_itm.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort_split_itm_sorter.java create mode 100644 400_xowa/src/gplx/core/ios/Io_sort_tst.java create mode 100644 400_xowa/src/gplx/core/ios/Io_stream_rdr_process.java create mode 100644 400_xowa/src/gplx/core/ios/Io_stream_zip_mgr.java create mode 100644 400_xowa/src/gplx/core/ios/Io_url_gen.java create mode 100644 400_xowa/src/gplx/core/ios/Io_url_gen_.java create mode 100644 400_xowa/src/gplx/core/lists/StatRng.java create mode 100644 400_xowa/src/gplx/core/lists/StatRng_tst.java create mode 100644 400_xowa/src/gplx/core/lists/binary_searches/Binary_comparer.java create mode 100644 400_xowa/src/gplx/core/lists/binary_searches/Binary_search_.java create mode 100644 400_xowa/src/gplx/core/lists/binary_searches/Binary_search__tst.java create mode 100644 400_xowa/src/gplx/core/lists/binary_searches/Binary_search_cmp.java create mode 100644 400_xowa/src/gplx/core/lists/binary_searches/Binary_search_grp.java create mode 100644 400_xowa/src/gplx/core/lists/hashs/Hash_adp__int.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_inet_conn.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_inet_conn_.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_inet_conn__http.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_protocol_itm.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_url.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_url_parser.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_url_parser_fxt.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_url_parser_tst.java create mode 100644 400_xowa/src/gplx/core/net/Gfo_url_site_data.java create mode 100644 400_xowa/src/gplx/core/net/Http_client_rdr.java create mode 100644 400_xowa/src/gplx/core/net/Http_client_rdr_.java create mode 100644 400_xowa/src/gplx/core/net/Http_client_rdr__stream.java create mode 100644 400_xowa/src/gplx/core/net/Http_client_wtr.java create mode 100644 400_xowa/src/gplx/core/net/Http_client_wtr_.java create mode 100644 400_xowa/src/gplx/core/net/Http_client_wtr__stream.java create mode 100644 400_xowa/src/gplx/core/net/Http_post_data_hash.java create mode 100644 400_xowa/src/gplx/core/net/Http_post_data_itm.java create mode 100644 400_xowa/src/gplx/core/net/Http_request_itm.java create mode 100644 400_xowa/src/gplx/core/net/Http_request_parser.java create mode 100644 400_xowa/src/gplx/core/net/Http_request_parser_tst.java create mode 100644 400_xowa/src/gplx/core/net/Http_server_wtr.java create mode 100644 400_xowa/src/gplx/core/net/Http_server_wtr_.java create mode 100644 400_xowa/src/gplx/core/net/Http_server_wtr__console.java create mode 100644 400_xowa/src/gplx/core/net/Local_host_.java create mode 100644 400_xowa/src/gplx/core/net/Server_socket_adp.java create mode 100644 400_xowa/src/gplx/core/net/Server_socket_adp__base.java create mode 100644 400_xowa/src/gplx/core/net/Socket_adp.java create mode 100644 400_xowa/src/gplx/core/net/Socket_adp__base.java create mode 100644 400_xowa/src/gplx/core/net/downloads/Http_download_wkr.java create mode 100644 400_xowa/src/gplx/core/net/downloads/Http_download_wkr_.java create mode 100644 400_xowa/src/gplx/core/net/downloads/Http_download_wkr__base.java create mode 100644 400_xowa/src/gplx/core/net/downloads/Http_download_wkr__jre.java create mode 100644 400_xowa/src/gplx/core/net/emails/Gfo_email_mgr.java create mode 100644 400_xowa/src/gplx/core/net/emails/Gfo_email_mgr_.java create mode 100644 400_xowa/src/gplx/core/net/emails/Gfo_email_mgr__apache.java create mode 100644 400_xowa/src/gplx/core/net/emails/Gfo_email_mgr__jre.java create mode 100644 400_xowa/src/gplx/core/net/qargs/Gfo_qarg_enum_itm.java create mode 100644 400_xowa/src/gplx/core/net/qargs/Gfo_qarg_enum_mgr.java create mode 100644 400_xowa/src/gplx/core/net/qargs/Gfo_qarg_itm.java create mode 100644 400_xowa/src/gplx/core/net/qargs/Gfo_qarg_mgr.java create mode 100644 400_xowa/src/gplx/core/net/qargs/Gfo_qarg_mgr_old.java create mode 100644 400_xowa/src/gplx/core/primitives/Bry_ary.java create mode 100644 400_xowa/src/gplx/core/primitives/Bry_cache.java create mode 100644 400_xowa/src/gplx/core/primitives/Gfo_number_parser.java create mode 100644 400_xowa/src/gplx/core/primitives/Gfo_number_parser_tst.java create mode 100644 400_xowa/src/gplx/core/primitives/Hash_adp__primitive.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_2_ref.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_2_val.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_ary.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_ary_bldr.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_ary_parser.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_ary_parser_tst.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_pool.java create mode 100644 400_xowa/src/gplx/core/primitives/Int_pool_tst.java create mode 100644 400_xowa/src/gplx/core/primitives/Obj_ary_parser_base.java create mode 100644 400_xowa/src/gplx/core/progs/rates/Gfo_rate_list.java create mode 100644 400_xowa/src/gplx/core/progs/rates/Gfo_rate_list_tst.java create mode 100644 400_xowa/src/gplx/core/progs/rates/Gfo_rate_mgr.java create mode 100644 400_xowa/src/gplx/core/scripts/Gfo_script_engine.java create mode 100644 400_xowa/src/gplx/core/scripts/Gfo_script_engine_.java create mode 100644 400_xowa/src/gplx/core/scripts/Gfo_script_engine__javascript.java create mode 100644 400_xowa/src/gplx/core/scripts/Gfo_script_engine__luaj.java create mode 100644 400_xowa/src/gplx/core/scripts/Gfo_script_engine__noop.java create mode 100644 400_xowa/src/gplx/core/security/files/Cksum_itm.java create mode 100644 400_xowa/src/gplx/core/security/files/Cksum_list.java create mode 100644 400_xowa/src/gplx/core/security/files/Cksum_list_tst.java create mode 100644 400_xowa/src/gplx/core/tests/Tst_chkr.java create mode 100644 400_xowa/src/gplx/core/tests/Tst_mgr.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_async_cmd_itm.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_async_cmd_mkr.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_async_mgr.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_cmd.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_cmd_.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_cmd_base.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_cmd_download.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_cmd_replace.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_cmd_unzip.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_grp.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_itm.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_mgr.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_pool.java create mode 100644 400_xowa/src/gplx/core/threads/Gfo_thread_wkr.java create mode 100644 400_xowa/src/gplx/dbs/Db_diff_bldr.java create mode 100644 400_xowa/src/gplx/dbs/bulks/Db_bulk_exec_.java create mode 100644 400_xowa/src/gplx/dbs/bulks/Db_bulk_prog.java create mode 100644 400_xowa/src/gplx/dbs/bulks/Db_tbl_copy.java create mode 100644 400_xowa/src/gplx/dbs/bulks/Db_tbl_copy_tst.java create mode 100644 400_xowa/src/gplx/dbs/cfgs/Db_cfg_hash.java create mode 100644 400_xowa/src/gplx/dbs/cfgs/Db_cfg_itm.java create mode 100644 400_xowa/src/gplx/dbs/cfgs/Db_cfg_tbl.java create mode 100644 400_xowa/src/gplx/dbs/metas/Schema_db_mgr.java create mode 100644 400_xowa/src/gplx/dbs/metas/Schema_loader_mgr.java create mode 100644 400_xowa/src/gplx/dbs/metas/Schema_loader_mgr_.java create mode 100644 400_xowa/src/gplx/dbs/metas/updates/Schema_update_cmd.java create mode 100644 400_xowa/src/gplx/dbs/metas/updates/Schema_update_cmd_.java create mode 100644 400_xowa/src/gplx/dbs/metas/updates/Schema_update_mgr.java create mode 100644 400_xowa/src/gplx/dbs/metas/updates/Schema_update_mgr_tst.java create mode 100644 400_xowa/src/gplx/dbs/percentiles/Log_tbl_fmtr.java create mode 100644 400_xowa/src/gplx/dbs/percentiles/Percentile_rng.java create mode 100644 400_xowa/src/gplx/dbs/percentiles/Percentile_rng_log.java create mode 100644 400_xowa/src/gplx/dbs/percentiles/Percentile_rng_tst.java create mode 100644 400_xowa/src/gplx/dbs/percentiles/Percentile_select_base.java create mode 100644 400_xowa/src/gplx/dbs/updates/Sql_runner.java create mode 100644 400_xowa/src/gplx/fsdb/Fsdb_db_file.java create mode 100644 400_xowa/src/gplx/fsdb/Fsdb_db_mgr.java create mode 100644 400_xowa/src/gplx/fsdb/Fsdb_db_mgr_.java create mode 100644 400_xowa/src/gplx/fsdb/Fsdb_db_mgr__v1.java create mode 100644 400_xowa/src/gplx/fsdb/Fsdb_db_mgr__v2.java create mode 100644 400_xowa/src/gplx/fsdb/Fsdb_db_mgr__v2_bldr.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_bin_itm.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_bin_tbl.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_dir_itm.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_dir_tbl.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_fil_itm.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_fil_tbl.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_img_itm.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_thm_itm.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_thm_tbl.java create mode 100644 400_xowa/src/gplx/fsdb/data/Fsd_thm_tbl_tst.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_atr_fil.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_atr_mgr.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_atr_tbl.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_bin_fil.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_bin_mgr.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_bin_tbl.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_cfg_mgr.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_id_itm.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_mnt_itm.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_mnt_mgr.java create mode 100644 400_xowa/src/gplx/fsdb/meta/Fsm_mnt_tbl.java create mode 100644 400_xowa/src/gplx/gfui/Gfui_bnd_parser.java create mode 100644 400_xowa/src/gplx/gfui/Gfui_bnd_parser_tst.java create mode 100644 400_xowa/src/gplx/langs/dsvs/Dsv_fld_parser.java create mode 100644 400_xowa/src/gplx/langs/dsvs/Dsv_fld_parser_.java create mode 100644 400_xowa/src/gplx/langs/dsvs/Dsv_tbl_parser.java create mode 100644 400_xowa/src/gplx/langs/dsvs/Dsv_tbl_parser_int_tst.java create mode 100644 400_xowa/src/gplx/langs/dsvs/Dsv_tbl_parser_str_tst.java create mode 100644 400_xowa/src/gplx/langs/dsvs/Dsv_wkr_base.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_lxr.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_lxr_.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_msg_bldr.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_msg_bldr_tst.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_nde.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_parser.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_parser_ctx.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_parser_tst.java create mode 100644 400_xowa/src/gplx/langs/gfs/Gfs_wtr.java create mode 100644 400_xowa/src/gplx/langs/htmls/clses/Gfh_class_.java create mode 100644 400_xowa/src/gplx/langs/htmls/clses/Gfh_class__tst.java create mode 100644 400_xowa/src/gplx/langs/htmls/clses/Gfh_class_parser_.java create mode 100644 400_xowa/src/gplx/langs/htmls/clses/Gfh_class_parser__tst.java create mode 100644 400_xowa/src/gplx/langs/htmls/clses/Gfh_class_parser_wkr.java create mode 100644 400_xowa/src/gplx/langs/htmls/docs/Gfh_atr.java create mode 100644 400_xowa/src/gplx/langs/htmls/docs/Gfh_doc_parser.java create mode 100644 400_xowa/src/gplx/langs/htmls/docs/Gfh_doc_wkr.java create mode 100644 400_xowa/src/gplx/langs/htmls/docs/Gfh_tag.java create mode 100644 400_xowa/src/gplx/langs/htmls/docs/Gfh_tag_rdr.java create mode 100644 400_xowa/src/gplx/langs/htmls/docs/Gfh_tag_rdr_tst.java create mode 100644 400_xowa/src/gplx/langs/htmls/docs/Gfh_txt_wkr.java diff --git a/100_core/.classpath b/100_core/.classpath new file mode 100644 index 000000000..072132222 --- /dev/null +++ b/100_core/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/100_core/src/gplx/Array_.java b/100_core/src/gplx/Array_.java new file mode 100644 index 000000000..732329566 --- /dev/null +++ b/100_core/src/gplx/Array_.java @@ -0,0 +1,117 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.lang.reflect.Array; +import gplx.core.strings.*; import gplx.core.lists.*; +public class Array_ { + public static Object cast(Object o) {return (Object)o;} + public static void Sort(Object[] obj) {List_adp_sorter.new_().Sort(obj, obj.length);} + public static void Sort(Object[] obj, gplx.core.lists.ComparerAble comparer) {List_adp_sorter.new_().Sort(obj, obj.length, true, comparer);} + public static Object[] Insert(Object[] cur, Object[] add, int add_pos) { + int cur_len = cur.length, add_len = add.length; + Object[] rv = (Object[])Array_.Create(Array_.Component_type(cur), cur_len + add_len); + for (int i = 0; i < add_pos; i++) // copy old up to add_pos + rv[i] = cur[i]; + for (int i = 0; i < add_len; i++) // insert add + rv[i + add_pos] = add[i]; + for (int i = add_pos; i < cur_len; i++) // copy old after add_pos + rv[i + add_len] = cur[i]; + return rv; + } + public static Object[] Replace_insert(Object[] cur, Object[] add, int curReplacePos, int addInsertPos) { + int curLen = cur.length, addLen = add.length; int newLen = addLen - addInsertPos; + Object[] rv = (Object[])Array_.Create(Array_.Component_type(cur), curLen + newLen); + for (int i = 0; i < curReplacePos; i++) // copy old up to curInsertPos; EX: curReplacePos=5, addInsertPos=2; copy up to element 3; 4, 5 are dropped + rv[i] = cur[i]; + for (int i = 0; i < addLen; i++) // insert add + rv[i + curReplacePos] = add[i]; + for (int i = curReplacePos + addInsertPos; i < curLen; i++) // copy old after curReplacePos + rv[i + newLen] = cur[i]; + return rv; + } + public static Object Resize_add_one(Object src, int src_len, Object new_obj) { + Object rv = Resize(src, src_len + 1); + Set_at(rv, src_len, new_obj); + return rv; + } + public static Object Resize(Object src, int trg_len) { + Object trg = Create(Component_type(src), trg_len); + int src_len = Array.getLength(src); + int copy_len = src_len > trg_len ? trg_len : src_len; // trg_len can either expand or shrink + Copy_to(src, 0, trg, 0, copy_len); + return trg; + } + public static List_adp To_list(Object ary) { + int aryLen = Array_.Len(ary); + List_adp rv = List_adp_.New(); + for (int i = 0; i < aryLen; i++) + rv.Add(Array_.Get_at(ary, i)); + return rv; + } + public static String To_str_nested_obj(Object o) { + Bry_bfr bfr = Bry_bfr_.New(); + To_str_nested_ary(bfr, (Object)o, 0); + return bfr.To_str_and_clear(); + } + private static void To_str_nested_ary(Bry_bfr bfr, Object ary, int indent) { + int len = Len(ary); + for (int i = 0; i < len; i++) { + Object itm = Get_at(ary, i); + if (itm != null && Type_adp_.Is_array(itm.getClass())) + To_str_nested_ary(bfr, (Object)itm, indent + 1); + else { + if (indent > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent * 2); + bfr.Add_str_u8(Object_.Xto_str_strict_or_null_mark(itm)).Add_byte_nl(); + } + } + } + public static String To_str_obj(Object o) {return To_str((Object)o);} + public static String To_str(Object ary) { + String_bldr sb = String_bldr_.new_(); + int ary_len = Len(ary); + for (int i = 0; i < ary_len; i++) + sb.Add_obj(Get_at(ary, i)).Add_char_nl(); + return sb.To_str(); + } + public static int Len(Object ary) {return Array.getLength(ary);} + public static final int Len_obj(Object[] ary) {return ary == null ? 0 : ary.length;} + public static Object Get_at(Object ary, int i) {return Array.get(ary, i);} + public static void Set_at(Object ary, int i, Object o) {Array.set(ary, i, o);} + public static Object Create(Class t, int count) {return Array.newInstance(t, count);} + public static Object Expand(Object src, Object trg, int src_len) { + try {System.arraycopy(src, 0, trg, 0, src_len);} + catch (Exception e) {throw Err_.new_exc(e, "core", "Array_.Expand failed", "src_len", src_len);} + return trg; + } + public static void Copy(Object src, Object trg) {System.arraycopy(src, 0, trg, 0, Len(src));} + public static void Copy_to(Object src, Object trg, int trgPos) {System.arraycopy(src, 0, trg, trgPos, Len(src));} + public static void Copy_to(Object src, int srcBgn, Object trg, int trgBgn, int srcLen) {System.arraycopy(src, srcBgn, trg, trgBgn, srcLen);} + private static Class Component_type(Object ary) { + if (ary == null) throw Err_.new_null(); + return ary.getClass().getComponentType(); + } + public static Object Resize_add(Object src, Object add) { + int srcLen = Len(src); + int trgLen = srcLen + Len(add); + Object trg = Create(Component_type(src), trgLen); + Copy(src, trg); + for (int i = srcLen; i < trgLen; i++) + Set_at(trg, i, Get_at(add, i - srcLen)); + return trg; + } + } diff --git a/100_core/src/gplx/Array__tst.java b/100_core/src/gplx/Array__tst.java new file mode 100644 index 000000000..c4f6bea4f --- /dev/null +++ b/100_core/src/gplx/Array__tst.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Array__tst { + @Test public void Resize_add() { + tst_Resize_add(ary_(), ary_(1), ary_(1)); // 0 + 1 = 1 + tst_Resize_add(ary_(0), ary_(), ary_(0)); // 1 + 0 = 1 + tst_Resize_add(ary_(0), ary_(1), ary_(0, 1)); // 1 + 1 = 2 + } void tst_Resize_add(int[] source, int[] added, int[] expd) {Tfds.Eq_ary(expd, (int[])Array_.Resize_add(source, added));} + @Test public void Resize() { + tst_Resize(ary_(0), 0, ary_()); // 1 -> 0 + tst_Resize(ary_(0, 1), 1, ary_(0)); // 2 -> 1 + } void tst_Resize(int[] source, int length, int[] expd) {Tfds.Eq_ary(expd, (int[])Array_.Resize(source, length));} + @Test public void Insert() { + tst_Insert(ary_obj(0, 1, 4, 5), ary_obj(2, 3), 2, ary_obj(0, 1, 2, 3, 4, 5)); + } void tst_Insert(Object[] cur, Object[] add, int addPos, Object[] expd) {Tfds.Eq_ary(expd, Array_.Insert(cur, add, addPos));} + @Test public void ReplaceInsert() { + tst_ReplaceInsert(ary_obj(0, 1, 4, 5) , ary_obj(1, 2, 3), 1, 1, ary_obj(0, 1, 2, 3, 4, 5)); + tst_ReplaceInsert(ary_obj(0, 1, 2, 4, 5) , ary_obj(1, 2, 3), 1, 2, ary_obj(0, 1, 2, 3, 4, 5)); + tst_ReplaceInsert(ary_obj(0, 1, 2, 3, 4, 5) , ary_obj(1, 2, 3), 1, 3, ary_obj(0, 1, 2, 3, 4, 5)); + tst_ReplaceInsert(ary_obj(0, 1, 9, 4, 5) , ary_obj(2, 3) , 2, 1, ary_obj(0, 1, 2, 3, 4, 5)); + } void tst_ReplaceInsert(Object[] cur, Object[] add, int curReplacePos, int addInsertPos, Object[] expd) {Tfds.Eq_ary(expd, Array_.Replace_insert(cur, add, curReplacePos, addInsertPos));} + Object[] ary_obj(Object... ary) {return ary;} + int[] ary_(int... ary) {return ary;} +} diff --git a/100_core/src/gplx/Bool_.java b/100_core/src/gplx/Bool_.java new file mode 100644 index 000000000..0272ca065 --- /dev/null +++ b/100_core/src/gplx/Bool_.java @@ -0,0 +1,64 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.langs.gfs.*; +public class Bool_ implements Gfo_invk { + public static final String Cls_val_name = "boolean"; + public static final Class Cls_ref_type = Boolean.class; + public static final boolean N = false , Y = true; + public static final byte N_byte = 0 , Y_byte = 1 , __byte = 127; + public static final int N_int = 0 , Y_int = 1 , __int = -1; + public static final byte[] N_bry = new byte[] {Byte_ascii.Ltr_n}, Y_bry = new byte[] {Byte_ascii.Ltr_y}; + public static final String True_str = "true", False_str = "false"; + public static final byte[] True_bry = Bry_.new_a7(True_str), False_bry = Bry_.new_a7(False_str); + public static boolean cast(Object obj) {try {return (Boolean)obj;} catch (Exception e) {throw Err_.new_type_mismatch_w_exc(e, boolean.class, obj);}} + public static boolean cast_or(Object obj, boolean v) {try {return (Boolean)obj;} catch (Exception e) {Err_.Noop(e); return v;}} + public static boolean parse(String raw) { + if ( String_.Eq(raw, True_str) + || String_.Eq(raw, "True") // needed for Store_Wtr(){boolVal.toString();} + ) + return true; + else if ( String_.Eq(raw, "false") + || String_.Eq(raw, False_str) + ) + return false; + throw Err_.new_parse_type(boolean.class, raw); + } + public static int Compare(boolean lhs, boolean rhs) { + if ( lhs == rhs) return CompareAble_.Same; + else if (!lhs && rhs) return CompareAble_.Less; + else /*lhs && !rhs*/ return CompareAble_.More; + } + public static boolean By_int(int v) {return v == Y_int;} + public static int To_int(boolean v) {return v ? Y_int : N_int;} + public static byte To_byte(boolean v) {return v ? Y_byte : N_byte;} + public static String To_str_lower(boolean v) {return v ? True_str : False_str;} + + public static final Bool_ Gfs = new Bool_(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_to_str)) { + boolean v = m.ReadBool(GfsCore_.Arg_primitive); + String fmt = m.ReadStrOr("fmt", null); + if (fmt == null) return v ? "true" : "false"; + else if (String_.Eq(fmt, "yn")) return v ? "y" : "n"; + else if (String_.Eq(fmt, "yes_no")) return v ? "yes" : "no"; + else return v ? "true" : "false"; + } + else return Gfo_invk_.Rv_unhandled; + } public static final String Invk_to_str = "to_str"; +} diff --git a/100_core/src/gplx/Bool__tst.java b/100_core/src/gplx/Bool__tst.java new file mode 100644 index 000000000..eda10a59f --- /dev/null +++ b/100_core/src/gplx/Bool__tst.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Bool__tst { + private final Bool__fxt fxt = new Bool__fxt(); + @Test public void Compare() { + fxt.Test__compare(Bool_.Y, Bool_.Y, CompareAble_.Same); + fxt.Test__compare(Bool_.N, Bool_.N, CompareAble_.Same); + fxt.Test__compare(Bool_.N, Bool_.Y, CompareAble_.Less); + fxt.Test__compare(Bool_.Y, Bool_.N, CompareAble_.More); + } +} +class Bool__fxt { + public void Test__compare(boolean lhs, boolean rhs, int expd) {Tfds.Eq(expd, Bool_.Compare(lhs, rhs));} +} diff --git a/100_core/src/gplx/Bry_.java b/100_core/src/gplx/Bry_.java new file mode 100644 index 000000000..864d8652f --- /dev/null +++ b/100_core/src/gplx/Bry_.java @@ -0,0 +1,1061 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.lang.*; +import gplx.core.brys.*; import gplx.core.primitives.*; import gplx.core.ios.*; +public class Bry_ { + public static final String Cls_val_name = "byte[]"; + public static final byte[] Empty = new byte[0]; + public static final byte[][] Ary_empty = new byte[0][]; + public static final Class Cls_ref_type = byte[].class; + public static byte[] cast(Object val) {return (byte[])val;} + public static byte[] New_by_byte(byte b) {return new byte[] {b};} + public static byte[] New_by_ints(int... ary) { + int len = ary.length; + byte[] rv = new byte[len]; + for (int i = 0; i < len; i++) + rv[i] = (byte)ary[i]; + return rv; + } + public static byte[] New_by_objs(Bry_bfr bfr, Object... ary) { + int len = ary.length; + for (int i = 0; i < len; ++i) { + Object itm = ary[i]; + Class type = Type_adp_.ClassOf_obj(itm); + if (Type_adp_.Eq(type, int.class)) bfr.Add_byte((byte)Int_.cast(itm)); + else if (Type_adp_.Eq(type, String.class)) bfr.Add_str_u8((String)itm); + else if (Type_adp_.Eq(type, byte[].class)) bfr.Add((byte[])itm); + else throw Err_.new_unhandled(Type_adp_.FullNameOf_type(type)); + } + return bfr.To_bry_and_clear(); + } + public static byte[] Coalesce_to_empty(byte[] v) {return v == null ? Bry_.Empty : v;} + public static byte[] Coalesce(byte[] v, byte[] or) {return v == null ? or : v;} + public static byte[] new_a7(String str) { + if (str == null) return null; + int str_len = str.length(); + if (str_len == 0) return Bry_.Empty; + byte[] rv = new byte[str_len]; + for (int i = 0; i < str_len; ++i) { + char c = str.charAt(i); + if (c > 128) c = '?'; + rv[i] = (byte)c; + } + return rv; + } + public static byte[] new_u8_safe(String str) {return str == null ? null : new_u8(str);} + public static byte[] new_u8(String str) { + try { + int str_len = str.length(); + int bry_len = new_u8__by_len(str, str_len); + byte[] rv = new byte[bry_len]; + new_u8__write(str, str_len, rv, 0); + return rv; + } + catch (Exception e) {throw Err_.new_exc(e, "core", "invalid UTF-8 sequence", "s", str);} + } + public static int new_u8__by_len(String s, int s_len) { + int rv = 0; + for (int i = 0; i < s_len; ++i) { + char c = s.charAt(i); + int c_len = 0; + if ( c < 128) c_len = 1; // 1 << 7 + else if ( c < 2048) c_len = 2; // 1 << 11 + else if ( (c > 55295) // 0xD800 + && (c < 56320)) c_len = 4; // 0xDFFF + else c_len = 3; // 1 << 16 + if (c_len == 4) ++i; // surrogate is 2 wide, not 1 + rv += c_len; + } + return rv; + } + public static byte[] New_u8_nl_apos(String... lines) { + Bry_bfr bfr = Bry_bfr_.Get(); + try { + New_u8_nl_apos(bfr, lines); + return bfr.To_bry_and_clear(); + } + finally {bfr.Mkr_rls();} + } + public static void New_u8_nl_apos(Bry_bfr bfr, String... lines) { + int lines_len = lines.length; + for (int i = 0; i < lines_len; ++i) { + if (i != 0) bfr.Add_byte_nl(); + byte[] line = Bry_.new_u8(lines[i]); + boolean dirty = false; + int prv = 0; + int line_len = line.length; + for (int j = 0; j < line_len; ++j) { + byte b = line[j]; + if (b == Byte_ascii.Apos) { + bfr.Add_mid(line, prv, j); + bfr.Add_byte(Byte_ascii.Quote); + dirty = true; + prv = j + 1; + } + } + if (dirty) + bfr.Add_mid(line, prv, line_len); + else + bfr.Add(line); + } + } + public static void new_u8__write(String str, int str_len, byte[] bry, int bry_pos) { + for (int i = 0; i < str_len; ++i) { + char c = str.charAt(i); + if ( c < 128) { + bry[bry_pos++] = (byte)c; + } + else if ( c < 2048) { + bry[bry_pos++] = (byte)(0xC0 | (c >> 6)); + bry[bry_pos++] = (byte)(0x80 | (c & 0x3F)); + } + else if ( (c > 55295) // 0xD800 + && (c < 56320)) { // 0xDFFF + if (i >= str_len) throw Err_.new_wo_type("incomplete surrogate pair at end of String", "char", c); + char nxt_char = str.charAt(i + 1); + int v = 0x10000 + (c - 0xD800) * 0x400 + (nxt_char - 0xDC00); + bry[bry_pos++] = (byte)(0xF0 | (v >> 18)); + bry[bry_pos++] = (byte)(0x80 | (v >> 12) & 0x3F); + bry[bry_pos++] = (byte)(0x80 | (v >> 6) & 0x3F); + bry[bry_pos++] = (byte)(0x80 | (v & 0x3F)); + ++i; + } + else { + bry[bry_pos++] = (byte)(0xE0 | (c >> 12)); + bry[bry_pos++] = (byte)(0x80 | (c >> 6) & 0x3F); + bry[bry_pos++] = (byte)(0x80 | (c & 0x3F)); + } + } + } + public static byte[] Copy(byte[] src) { + int src_len = src.length; + byte[] trg = new byte[src_len]; + for (int i = 0; i < src_len; ++i) + trg[i] = src[i]; + return trg; + } + public static byte[] Resize(byte[] src, int trg_len) {return Resize(src, 0, trg_len);} + public static byte[] Resize(byte[] src, int src_bgn, int trg_len) { + byte[] trg = new byte[trg_len]; + int src_len = src.length; if (src_len > trg_len) src_len = trg_len; // trg_len can be less than src_len + Copy_by_len(src, src_bgn, src_len, trg, 0); + return trg; + } + public static byte[] Repeat_space(int len) {return Repeat(Byte_ascii.Space, len);} + public static byte[] Repeat(byte b, int len) { + byte[] rv = new byte[len]; + for (int i = 0; i < len; i++) + rv[i] = b; + return rv; + } + public static byte[] Add(byte[] src, byte b) { + int src_len = src.length; + byte[] rv = new byte[src_len + 1]; + Copy_by_pos(src, 0, src_len, rv, 0); + rv[src_len] = b; + return rv; + } + public static byte[] Add(byte b, byte[] src) { + int src_len = src.length; + byte[] rv = new byte[src_len + 1]; + Copy_by_pos(src, 0, src_len, rv, 1); + rv[0] = b; + return rv; + } + public static byte[] Add(byte[]... all) { + int all_len = all.length, rv_len = 0; + for (int i = 0; i < all_len; ++i) { + byte[] cur = all[i]; if (cur == null) continue; + rv_len += cur.length; + } + byte[] rv = new byte[rv_len]; + int rv_idx = 0; + for (int i = 0; i < all_len; ++i) { + byte[] cur = all[i]; if (cur == null) continue; + int cur_len = cur.length; + for (int j = 0; j < cur_len; ++j) + rv[rv_idx++] = cur[j]; + } + return rv; + } + public static byte[] Add_w_dlm(byte[] dlm, byte[]... ary) { + int ary_len = ary.length; + if (ary_len == 0) return Bry_.Empty; + int dlm_len = dlm.length; + int rv_len = dlm_len * (ary_len - 1); // rv will have at least as many dlms as itms - 1 + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (itm != null) rv_len += itm.length; + } + int rv_pos = 0; + byte[] rv = new byte[rv_len]; + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (i != 0) { + for (int j = 0; j < dlm_len; j++) { + rv[rv_pos++] = dlm[j]; + } + } + if (itm == null) continue; + int itm_len = itm.length; + for (int j = 0; j < itm_len; j++) { + rv[rv_pos++] = itm[j]; + } + } + return rv; + } + public static byte[] Add_w_dlm(byte dlm, byte[]... ary) { + int ary_len = ary.length; + if (ary_len == 0) return Bry_.Empty; + int rv_len = ary_len - 1; // rv will have at least as many dlms as itms - 1 + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (itm != null) rv_len += itm.length; + } + int rv_pos = 0; + byte[] rv = new byte[rv_len]; + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (i != 0) rv[rv_pos++] = dlm; + if (itm == null) continue; + int itm_len = itm.length; + for (int j = 0; j < itm_len; j++) { + rv[rv_pos++] = itm[j]; + } + } + return rv; + } + public static int Len(byte[] v) {return v == null ? 0 : v.length;} + public static boolean Len_gt_0(byte[] v) {return v != null && v.length > 0;} + public static boolean Len_eq_0(byte[] v) {return v == null || v.length == 0;} + public static byte Get_at_end(byte[] bry) {return bry[bry.length - 1];} // don't bother checking for errors; depend on error trace + public static boolean Has_at(byte[] src, int src_len, int pos, byte b) {return (pos < src_len) && (src[pos] == b);} + public static boolean Has(byte[] src, byte[] lkp) {return Bry_find_.Find_fwd(src, lkp) != Bry_find_.Not_found;} + public static boolean Has(byte[] src, byte lkp) { + if (src == null) return false; + int len = src.length; + for (int i = 0; i < len; i++) + if (src[i] == lkp) return true; + return false; + } + public static boolean Has_at_bgn(byte[] src, byte lkp) {return Has_at_bgn(src, lkp, 0);} + public static boolean Has_at_bgn(byte[] src, byte lkp, int src_bgn) {return src_bgn < src.length ? src[src_bgn] == lkp : false;} + public static boolean Has_at_bgn(byte[] src, byte[] lkp) {return Has_at_bgn(src, lkp, 0, src.length);} + public static boolean Has_at_bgn(byte[] src, byte[] lkp, int src_bgn, int src_end) { + int lkp_len = lkp.length; + if (lkp_len + src_bgn > src_end) return false; // lkp is longer than src + for (int i = 0; i < lkp_len; i++) { + if (lkp[i] != src[i + src_bgn]) return false; + } + return true; + } + public static boolean Has_at_end(byte[] src, byte lkp) { + if (src == null) return false; + int src_len = src.length; + if (src_len == 0) return false; + return src[src_len - 1] == lkp; + } + public static boolean Has_at_end(byte[] src, byte[] lkp) {int src_len = src.length; return Has_at_end(src, lkp, src_len - lkp.length, src_len);} + public static boolean Has_at_end(byte[] src, byte[] lkp, int src_bgn, int src_end) { + int lkp_len = lkp.length; + if (src_bgn < 0) return false; + int pos = src_end - lkp_len; if (pos < src_bgn) return false; // lkp is longer than src + for (int i = 0; i < lkp_len; i++) { + if (lkp[i] != src[i + pos]) return false; + } + return true; + } + public static void Set(byte[] src, int bgn, int end, byte[] repl) { + int repl_len = repl.length; + for (int i = 0; i < repl_len; i++) + src[i + bgn] = repl[i]; + } + public static void Copy_by_pos(byte[] src, int src_bgn, int src_end, byte[] trg, int trg_bgn) { + int trg_adj = trg_bgn - src_bgn; + for (int i = src_bgn; i < src_end; i++) + trg[i + trg_adj] = src[i]; + } + private static void Copy_by_len(byte[] src, int src_bgn, int src_len, byte[] trg, int trg_bgn) { + for (int i = 0; i < src_len; i++) + trg[i + trg_bgn] = src[i + src_bgn]; + } + public static byte[] Replace_one(byte[] src, byte[] find, byte[] repl) { + int src_len = src.length; + int findPos = Bry_find_.Find(src, find, 0, src_len, true); if (findPos == Bry_find_.Not_found) return src; + int findLen = find.length, replLen = repl.length; + int rvLen = src_len + replLen - findLen; + byte[] rv = new byte[rvLen]; + Copy_by_len(src , 0 , findPos , rv, 0 ); + Copy_by_len(repl, 0 , replLen , rv, findPos ); + Copy_by_len(src , findPos + findLen , src_len - findPos - findLen , rv, findPos + replLen); + return rv; + } + public static void Replace_all_direct(byte[] src, byte find, byte repl) {Replace_all_direct(src, find, repl, 0, src.length);} + public static void Replace_all_direct(byte[] src, byte find, byte repl, int bgn, int end) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + if (b == find) src[i] = repl; + } + } + public static byte[] Limit(byte[] src, int len) { + if (src == null) return null; + int src_len = src.length; + return len < src_len ? Bry_.Mid(src, 0, len) : src; + } + public static byte[] Mid_by_nearby(byte[] src, int pos, int around) { + int bgn = pos - around; if (bgn < 0) bgn = 0; + int src_len = src.length; + int end = pos + around; if (end > src_len) end = src_len; + return Mid(src, bgn, end); + } + public static byte[] Mid_by_len(byte[] src, int bgn, int len) {return Mid(src, bgn, bgn + len);} + public static byte[] Mid_by_len_safe(byte[] src, int bgn, int len) { + int src_len = src.length; + if (bgn < 0) bgn = 0; + if (len + bgn > src_len) len = (src_len - bgn); + return Mid(src, bgn, bgn + len); + } + public static String MidByLenToStr(byte[] src, int bgn, int len) { + int end = bgn + len; end = Int_.BoundEnd(end, src.length); + byte[] ary = Bry_.Mid(src, bgn, end); + return String_.new_u8(ary); + } + public static byte[] Mid_safe(byte[] src, int bgn, int end) { + if (src == null) return null; + int src_len = src.length; + if (bgn < 0) bgn = 0; + if (end >= src_len) end = src_len; + if (bgn > end) bgn = end; + else if (end < bgn) end = bgn; + return Mid(src, bgn, end); + } + public static byte[] Mid(byte[] src, int bgn) {return Mid(src, bgn, src.length);} + public static byte[] Mid_or(byte[] src, int bgn, int end, byte[] or) { + int src_len = src.length; + if ( src == null + || (bgn < 0 || bgn > src_len) + || (end < 0 || end > src_len) + || (end < bgn) + ) + return or; + return Mid(src, bgn, src.length); + } + public static byte[] Mid(byte[] src, int bgn, int end) { + try { + int len = end - bgn; if (len == 0) return Bry_.Empty; + byte[] rv = new byte[len]; + for (int i = bgn; i < end; i++) + rv[i - bgn] = src[i]; + return rv; + } catch (Exception e) {Err_.Noop(e); throw Err_.new_("Bry_", "mid failed", "bgn", bgn, "end", end);} + } + public static byte[] Mid_w_trim(byte[] src, int bgn, int end) { + int len = end - bgn; if (len == 0) return Bry_.Empty; + int actl_bgn = bgn, actl_end = end; + // trim at bgn + boolean chars_seen = false; + for (int i = bgn; i < end; ++i) { + switch (src[i]) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + break; + default: + chars_seen = true; + actl_bgn = i; + i = end; + break; + } + } + if (!chars_seen) return Bry_.Empty; // all ws + // trim at end + for (int i = end - 1; i >= actl_bgn; --i) { + switch (src[i]) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + break; + default: + actl_end = i + 1; + i = -1; + break; + } + } + // extract mid + len = actl_end - actl_bgn; if (len == 0) return Bry_.Empty; + byte[] rv = new byte[len]; + for (int i = actl_bgn; i < actl_end; ++i) + rv[i - actl_bgn] = src[i]; + return rv; + } + public static byte[] mask_(int len, byte... itms) { + byte[] rv = new byte[len]; + int itms_len = itms.length; + for (int i = 0; i < itms_len; i++) { + byte itm = itms[i]; + rv[itm & 0xFF] = itm; // PATCH.JAVA:need to convert to unsigned byte + } + return rv; + } + public static final byte[] Trim_ary_ws = mask_(256, Byte_ascii.Tab, Byte_ascii.Nl, Byte_ascii.Cr, Byte_ascii.Space); + public static byte[] Trim(byte[] src) {return Trim(src, 0, src.length, true, true, Trim_ary_ws);} + public static byte[] Trim(byte[] src, int bgn, int end) {return Trim(src, bgn, end, true, true, Trim_ary_ws);} + public static byte[] Trim(byte[] src, int bgn, int end, boolean trim_bgn, boolean trim_end, byte[] trim_ary) { + int txt_bgn = bgn, txt_end = end; + boolean all_ws = true; + if (trim_bgn) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Null) { + txt_bgn = i; + i = end; + all_ws = false; + } + } + if (all_ws) return Bry_.Empty; + } + if (trim_end) { + for (int i = end - 1; i > -1; i--) { + byte b = src[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Null) { + txt_end = i + 1; + i = -1; + all_ws = false; + } + } + if (all_ws) return Bry_.Empty; + } + return Bry_.Mid(src, txt_bgn, txt_end); + } + public static byte[] Trim_end(byte[] v, byte trim, int end) { + boolean trimmed = false; + int pos = end - 1; // NOTE: -1 b/c callers will always be passing pos + 1; EX: src, src_len + for (; pos > -1; pos--) { + if (v[pos] == trim) { + trimmed = true; + } + else + break; + } + return trimmed ? Bry_.Mid(v, 0, pos + 1) : v; + } + public static int Compare(byte[] lhs, byte[] rhs) { + if (lhs == null) return CompareAble_.More; + else if (rhs == null) return CompareAble_.Less; + else return Compare(lhs, 0, lhs.length, rhs, 0, rhs.length); + } + public static int Compare(byte[] lhs, int lhs_bgn, int lhs_end, byte[] rhs, int rhs_bgn, int rhs_end) { + int lhs_len = lhs_end - lhs_bgn, rhs_len = rhs_end - rhs_bgn; + int min = lhs_len < rhs_len ? lhs_len : rhs_len; + int rv = CompareAble_.Same; + for (int i = 0; i < min; i++) { + rv = (lhs[i + lhs_bgn] & 0xff) - (rhs[i + rhs_bgn] & 0xff); // PATCH.JAVA:need to convert to unsigned byte + if (rv != CompareAble_.Same) return rv > CompareAble_.Same ? CompareAble_.More : CompareAble_.Less; // NOTE: changed from if (rv != CompareAble_.Same) return rv; DATE:2013-04-25 + } + return Int_.Compare(lhs_len, rhs_len); // lhs and rhs share same beginning bytes; return len comparisons + } + public static boolean Eq(byte[] src, byte[] val) {return Eq(src, 0, src == null ? 0 : src.length, val);} + public static boolean Eq(byte[] src, int src_bgn, int src_end, byte[] val) { + if (src == null && val == null) return true; + else if (src == null || val == null) return false; + if (src_bgn < 0) return false; + int val_len = val.length; + if (val_len != src_end - src_bgn) return false; + int src_len = src.length; + for (int i = 0; i < val_len; i++) { + int src_pos = i + src_bgn; + if (src_pos == src_len) return false; + if (val[i] != src[src_pos]) return false; + } + return true; + } + public static boolean Eq_ci_a7(byte[] lhs, byte[] rhs, int rhs_bgn, int rhs_end) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + int lhs_len = lhs.length; + int rhs_len = rhs_end - rhs_bgn; + if (lhs_len != rhs_len) return false; + for (int i = 0; i < lhs_len; i++) { + byte lhs_b = lhs[i]; if (lhs_b > 64 && lhs_b < 91) lhs_b += 32; // lowercase + byte rhs_b = rhs[i + rhs_bgn]; if (rhs_b > 64 && rhs_b < 91) rhs_b += 32; // lowercase + if (lhs_b != rhs_b) return false; + } + return true; + } + public static boolean Match(byte[] src, byte[] find) {return Match(src, 0, src.length, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, byte[] find) {return Match(src, src_bgn, src.length, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find) {return Match(src, src_bgn, src_end, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find, int find_bgn, int find_end) { + if (src_bgn == -1) return false; + int src_len = src.length; + if (src_end > src_len) src_end = src_len; // must limit src_end to src_len, else ArrayIndexOutOfBounds below; DATE:2015-01-31 + int find_len = find_end - find_bgn; + if (find_len != src_end - src_bgn) return false; + if (find_len == 0) return src_end - src_bgn == 0; // "" only matches "" + for (int i = 0; i < find_len; i++) { + int pos = src_bgn + i; + if (pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + if (src[pos] != find[i + find_bgn]) return false; + } + return true; + } + public static boolean Match_w_swap(byte[] src, int src_bgn, int src_end, byte[] find, int find_bgn, int find_end, byte swap_src, byte swap_trg) {// same as above, but used by XOWA for ttl matches; + int src_len = src.length; + if (src_end > src_len) src_end = src_len; // must limit src_end to src_len, else ArrayIndexOutOfBounds below; DATE:2015-01-31 + int find_len = find_end - find_bgn; + if (find_len != src_end - src_bgn) return false; + if (find_len == 0) return src_end - src_bgn == 0; // "" only matches "" + for (int i = 0; i < find_len; i++) { + int pos = src_bgn + i; + if (pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + byte src_byte = src[pos]; if (src_byte == swap_src) src_byte = swap_trg; + byte trg_byte = find[i + find_bgn]; if (trg_byte == swap_src) trg_byte = swap_trg; + if (src_byte != trg_byte) return false; + } + return true; + } + public static boolean Match_bwd_any(byte[] src, int src_end, int src_bgn, byte[] find) { // NOTE: utf8 doesn't matter (matching byte for byte) + int find_len = find.length; + for (int i = 0; i < find_len; i++) { + int src_pos = src_end - i; + int find_pos = find_len - i - 1; + if (src_pos < src_bgn) return false; // ran out of src; exit; EX: src=ab; find=abc + if (src[src_pos] != find[find_pos]) return false; + } + return true; + } + public static int To_int_by_a7(byte[] v) { + int v_len = v.length; + int mod = 8 * (v_len - 1); + int rv = 0; + for (int i = 0; i < v_len; i++) { + rv |= v[i] << mod; + mod -= 8; + } + return rv; +// return ((0xFF & v[0]) << 24) +// | ((0xFF & v[1]) << 16) +// | ((0xFF & v[2]) << 8) +// | (0xFF & v[3]); + } + public static byte[] To_a7_bry(int val, int pad_len) {return To_a7_bry(val, null, 0, pad_len);} + public static byte[] To_a7_bry(int val, byte[] ary, int aryPos, int pad_len) { + int neg = 0; + if (val < 0) { + val *= -1; + neg = 1; + } + int digits = val == 0 ? 0 : Math_.Log10(val); + digits += 1; // digits = log + 1; EX: Log(1-9) = 0, Log(10-99) = 1 + int ary_len = digits + neg, aryBgn = aryPos, pad = 0; + if (ary_len < pad_len) { // padding specified + pad = pad_len - ary_len; + ary_len = pad_len; + } + if (ary == null) ary = new byte[ary_len]; + long factor = 1; // factor needs to be long to handle 1 billion (for which factor would be 10 billion) + for (int i = 0; i < digits; i++) // calc maxFactor + factor *= 10; + if (neg == 1) ary[0] = Byte_NegSign; + + for (int i = 0; i < pad; i++) // fill ary with pad + ary[i + aryBgn] = Byte_ascii.To_a7_str(0); + aryBgn += pad; // advance aryBgn by pad + for (int i = neg; i < ary_len - pad; i++) { + int denominator = (int)(factor / 10); // cache denominator to check for divide by 0 + int digit = denominator == 0 ? 0 : (int)((val % factor) / denominator); + ary[aryBgn + i] = Byte_ascii.To_a7_str(digit); + factor /= 10; + } + return ary; + } + public static byte[] new_by_int(int v) { + byte b0 = (byte)(v >> 24); + byte b1 = (byte)(v >> 16); + byte b2 = (byte)(v >> 8); + byte b3 = (byte)(v); + if (b0 != 0) return new byte[] {b0, b1, b2, b3}; + else if (b1 != 0) return new byte[] {b1, b2, b3}; + else if (b2 != 0) return new byte[] {b2, b3}; + else return new byte[] {b3}; + } + public static boolean To_bool_or(byte[] raw, boolean or) { + return Bry_.Eq(raw, Bool_.True_bry) ? true : or; + } + public static boolean To_bool_by_int(byte[] ary) { + int rv = To_int_or(ary, 0, ary.length, Int_.Min_value, Bool_.Y, null); + switch (rv) { + case 0: return false; + case 1: return true; + default: throw Err_.new_wo_type("could not parse to boolean int", "val", String_.new_u8(ary)); + } + } + public static byte To_int_as_byte(byte[] ary, int bgn, int end, byte or) {return (byte)To_int_or(ary, bgn, end, or);} + public static int To_int(byte[] ary) {return To_int_or_fail(ary, 0, ary.length);} + public static int To_int_or_fail(byte[] ary, int bgn, int end) { + int rv = To_int_or(ary, bgn, end, Int_.Min_value, Bool_.Y, null); + if (rv == Int_.Min_value) throw Err_.new_wo_type("could not parse to int", "val", String_.new_u8(ary, bgn, end)); + return rv; + } + public static int To_int_or_neg1(byte[] ary) {return To_int_or(ary, 0 , ary.length, -1, Bool_.Y, null);} + public static int To_int_or(byte[] ary, int or) {return To_int_or(ary, 0 , ary.length, or, Bool_.Y, null);} + public static int To_int_or(byte[] ary, int bgn, int end, int or) {return To_int_or(ary, bgn , end , or, Bool_.Y, null);} + public static int To_int_or__strict(byte[] ary, int or) {return To_int_or(ary, 0 , ary.length, or, Bool_.N, null);} + private static int To_int_or(byte[] ary, int bgn, int end, int or, boolean sign_is_valid, byte[] ignore_ary) { + if ( ary == null + || end == bgn // null-len + ) return or; + int rv = 0, multiple = 1; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + break; + case Byte_ascii.Dash: + return i == bgn && sign_is_valid ? rv * -1 : or; + case Byte_ascii.Plus: + return i == bgn && sign_is_valid ? rv : or; + default: + boolean invalid = true; + if (ignore_ary != null) { + int ignore_ary_len = ignore_ary.length; + for (int j = 0; j < ignore_ary_len; j++) { + if (b == ignore_ary[j]) { + invalid = false; + break; + } + } + } + if (invalid) return or; + break; + } + } + return rv; + } + public static int To_int_or__trim_ws(byte[] ary, int bgn, int end, int or) { // NOTE: same as To_int_or, except trims ws at bgn / end; DATE:2014-02-09 + if (end == bgn) return or; // null len + int rv = 0, multiple = 1; + boolean numbers_seen = false, ws_seen = false; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + if (ws_seen) // "number ws number" pattern; invalid ws in middle; see tests + return or; + numbers_seen = true; + break; + case Byte_ascii.Dash: + return i == bgn ? rv * -1 : or; + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + if (numbers_seen) + ws_seen = true; + break; + default: return or; + } + } + return rv; + } + public static int To_int_or__lax(byte[] ary, int bgn, int end, int or) { + if (end == bgn) return or; // null-len + int end_num = end; + for (int i = bgn; i < end; i++) { + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + case Byte_ascii.Dash: + if (i != bgn) { + end_num = i; + i = end; + } + break; + default: + end_num = i; + i = end; + break; + } + } + return To_int_or(ary, bgn, end_num, or); + } + public static long To_long_or(byte[] ary, long or) {return To_long_or(ary, null, 0, ary.length, or);} + public static long To_long_or(byte[] ary, byte[] ignore_ary, int bgn, int end, long or) { + if ( ary == null + || end == bgn // null-len + ) return or; + long rv = 0, multiple = 1; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + break; + case Byte_ascii.Dash: + return i == bgn ? rv * -1 : or; + case Byte_ascii.Plus: + return i == bgn ? rv : or; + default: + boolean invalid = true; + if (ignore_ary != null) { + int ignore_ary_len = ignore_ary.length; + for (int j = 0; j < ignore_ary_len; j++) { + if (b == ignore_ary[j]) { + invalid = false; + break; + } + } + } + if (invalid) return or; + break; + } + } + return rv; + } + public static double To_double(byte[] ary, int bgn, int end) {return Double_.parse(String_.new_u8(ary, bgn, end));} + public static double To_double_or(byte[] bry, double or) {return Double_.parse_or(String_.new_u8(bry, 0, bry.length), or);} + public static double To_double_or(byte[] ary, int bgn, int end, double or) {return Double_.parse_or(String_.new_u8(ary, bgn, end), or);} + public static Decimal_adp To_decimal(byte[] ary, int bgn, int end) {return Decimal_adp_.parse(String_.new_u8(ary, bgn, end));} + public static byte[][] Ary_add(byte[][] lhs, byte[][] rhs) { + int lhs_len = lhs.length, rhs_len = rhs.length; + if (lhs_len == 0) return rhs; + else if (rhs_len == 0) return lhs; + else { + byte[][] rv = new byte[lhs_len + rhs_len][]; + for (int i = 0; i < lhs_len; i++) + rv[i] = lhs[i]; + for (int i = 0; i < rhs_len; i++) + rv[i + lhs_len] = rhs[i]; + return rv; + } + } + public static byte[][] Ary(byte[]... ary) {return ary;} + public static byte[][] Ary(String... ary) { + int ary_len = ary.length; + byte[][] rv = new byte[ary_len][]; + for (int i = 0; i < ary_len; i++) { + String itm = ary[i]; + rv[i] = itm == null ? null : Bry_.new_u8(itm); + } + return rv; + } + public static byte[][] Ary_obj(Object... ary) { + if (ary == null) return Bry_.Ary_empty; + int ary_len = ary.length; + byte[][] rv = new byte[ary_len][]; + for (int i = 0; i < ary_len; i++) { + Object itm = ary[i]; + rv[i] = itm == null ? null : Bry_.new_u8(Object_.Xto_str_strict_or_empty(itm)); + } + return rv; + } + public static boolean Ary_eq(byte[][] lhs, byte[][] rhs) { + int lhs_len = lhs.length; + int rhs_len = rhs.length; + if (lhs_len != rhs_len) return false; + for (int i = 0; i < lhs_len; ++i) + if (!Bry_.Eq(lhs[i], rhs[i])) return false; + return true; + } + public static final byte Dlm_fld = (byte)'|', Dlm_row = (byte)'\n', Dlm_quote = (byte)'"', Dlm_null = 0, Ascii_zero = 48; + public static final String Fmt_csvDte = "yyyyMMdd HHmmss.fff"; + public static DateAdp ReadCsvDte(byte[] ary, Int_obj_ref posRef, byte lkp) {// ASSUME: fmt = yyyyMMdd HHmmss.fff + int y = 0, M = 0, d = 0, H = 0, m = 0, s = 0, f = 0; + int bgn = posRef.Val(); + y += (ary[bgn + 0] - Ascii_zero) * 1000; + y += (ary[bgn + 1] - Ascii_zero) * 100; + y += (ary[bgn + 2] - Ascii_zero) * 10; + y += (ary[bgn + 3] - Ascii_zero); + M += (ary[bgn + 4] - Ascii_zero) * 10; + M += (ary[bgn + 5] - Ascii_zero); + d += (ary[bgn + 6] - Ascii_zero) * 10; + d += (ary[bgn + 7] - Ascii_zero); + H += (ary[bgn + 9] - Ascii_zero) * 10; + H += (ary[bgn + 10] - Ascii_zero); + m += (ary[bgn + 11] - Ascii_zero) * 10; + m += (ary[bgn + 12] - Ascii_zero); + s += (ary[bgn + 13] - Ascii_zero) * 10; + s += (ary[bgn + 14] - Ascii_zero); + f += (ary[bgn + 16] - Ascii_zero) * 100; + f += (ary[bgn + 17] - Ascii_zero) * 10; + f += (ary[bgn + 18] - Ascii_zero); + if (ary[bgn + 19] != lkp) throw Err_.new_wo_type("csv date is invalid", "txt", String_.new_u8__by_len(ary, bgn, 20)); + posRef.Val_add(19 + 1); // +1=lkp.len + return DateAdp_.new_(y, M, d, H, m, s, f); + } + public static String ReadCsvStr(byte[] ary, Int_obj_ref posRef, byte lkp) {return String_.new_u8(ReadCsvBry(ary, posRef, lkp, true));} + public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp) {return ReadCsvBry(ary, posRef, lkp, true);} + public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp, boolean make) { + int bgn = posRef.Val(), aryLen = ary.length; + Bry_bfr bb = null; + if (aryLen > 0 && ary[0] == Dlm_quote) { + int pos = bgn + 1; // +1 to skip quote + if (make) bb = Bry_bfr_.New(); + while (true) { + if (pos == aryLen) throw Err_.new_wo_type("endOfAry reached, but no quote found", "txt", String_.new_u8__by_len(ary, bgn, pos)); + byte b = ary[pos]; + if (b == Dlm_quote) { + if (pos == aryLen - 1) throw Err_.new_wo_type("endOfAry reached, quote found but lkp not", "txt", String_.new_u8__by_len(ary, bgn, pos)); + byte next = ary[pos + 1]; + if (next == Dlm_quote) { // byte followed by quote + if (make) bb.Add_byte(b); + pos += 2; + } + else if (next == lkp) { + posRef.Val_(pos + 2); // 1=endQuote;1=lkp; + return make ? bb.To_bry() : Bry_.Empty; + } + else throw Err_.new_wo_type("quote found, but not doubled", "txt", String_.new_u8__by_len(ary, bgn, pos + 1)); + } + else { + if (make) bb.Add_byte(b); + pos++; + } + } + } + else { + for (int i = bgn; i < aryLen; i++) { + if (ary[i] == lkp) { + posRef.Val_(i + 1); // +1 = lkp.Len + return make ? Bry_.Mid(ary, bgn, i) : Bry_.Empty; + } + } + throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "txt", String_.new_u8__by_len(ary, bgn, aryLen)); + } + } + public static int ReadCsvInt(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); + if (pos == Bry_find_.Not_found) throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "bgn", bgn); + int rv = Bry_.To_int_or(ary, posRef.Val(), pos, -1); + posRef.Val_(pos + 1); // +1 = lkp.Len + return rv; + } + public static double ReadCsvDouble(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); + if (pos == Bry_find_.Not_found) throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "bgn", bgn); + double rv = Bry_.To_double(ary, posRef.Val(), pos); + posRef.Val_(pos + 1); // +1 = lkp.Len + return rv; + } + public static void ReadCsvNext(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); + posRef.Val_(pos + 1); // +1 = lkp.Len + } + public static byte Byte_NegSign = (byte)'-'; + public static byte[] Replace_create(byte[] src, byte find, byte replace) { + byte[] rv = Bry_.Copy(src); + Replace_reuse(rv, find, replace); + return rv; + } + public static void Replace_reuse(byte[] src, byte find, byte replace) { + int src_len = src.length; + for (int i = 0; i < src_len; i++) { + if (src[i] == find) src[i] = replace; + } + } + public static byte[] Replace(byte[] src, byte find, byte replace) {return Replace(src, 0, src.length, find, replace);} + public static byte[] Replace(byte[] src, int bgn, int end, byte find, byte replace) { + int src_len = src.length; + byte[] rv = new byte[src_len]; + for (int i = bgn; i < end; ++i) { + byte b = src[i]; + rv[i] = b == find ? replace : b; + } + for (int i = end; i < src_len; ++i) + rv[i] = src[i]; + return rv; + } + public static byte[] Replace_safe(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) { + if (src == null || find == null || repl == null) return null; + return Replace(bfr, src, find, repl, 0, src.length); + } + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) {return Replace(bfr, src, find, repl, 0, src.length);} + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl, int src_bgn, int src_end) {return Replace(bfr, src, find, repl, src_bgn, src_end, Int_.Max_value);} + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl, int src_bgn, int src_end, int limit) { + int pos = src_bgn; + boolean dirty = false; + int find_len = find.length; + int bfr_bgn = pos; + int replace_count = 0; + while (pos < src_end) { + int find_pos = Bry_find_.Find_fwd(src, find, pos); + if (find_pos == Bry_find_.Not_found) break; + dirty = true; + bfr.Add_mid(src, bfr_bgn, find_pos); + bfr.Add(repl); + pos = find_pos + find_len; + bfr_bgn = pos; + ++replace_count; + if (replace_count == limit) break; + } + if (dirty) + bfr.Add_mid(src, bfr_bgn, src_end); + return dirty ? bfr.To_bry_and_clear() : src; + } + public static byte[] Replace(byte[] src, byte[] find, byte[] replace) {return Replace_between(src, find, null, replace);} + public static byte[] Replace_between(byte[] src, byte[] bgn, byte[] end, byte[] replace) { + Bry_bfr bfr = Bry_bfr_.New(); + boolean replace_all = end == null; + int src_len = src.length, bgn_len = bgn.length, end_len = replace_all ? 0 : end.length; + int pos = 0; + while (true) { + if (pos >= src_len) break; + int bgn_pos = Bry_find_.Find_fwd(src, bgn, pos); + if (bgn_pos == Bry_find_.Not_found) { + bfr.Add_mid(src, pos, src_len); + break; + } + else { + int bgn_rhs = bgn_pos + bgn_len; + int end_pos = replace_all ? bgn_rhs : Bry_find_.Find_fwd(src, end, bgn_rhs); + if (end_pos == Bry_find_.Not_found) { + bfr.Add_mid(src, pos, src_len); + break; + } + else { + bfr.Add_mid(src, pos, bgn_pos); + bfr.Add(replace); + pos = end_pos + end_len; + } + } + } + return bfr.To_bry_and_clear(); + } + public static int Trim_end_pos(byte[] src, int end) { + for (int i = end - 1; i > -1; i--) { + switch (src[i]) { + case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: case Byte_ascii.Space: + break; + default: + return i + 1; + } + } + return 0; + } + public static byte[] Increment_last(byte[] ary) {return Increment_last(ary, ary.length - 1);} + public static byte[] Increment_last(byte[] ary, int end_idx) { + for (int i = end_idx; i > -1; i--) { + byte end_val_old = ary[i]; + byte end_val_new = (byte)(end_val_old + 1); + ary[i] = end_val_new; + if (end_val_new > (end_val_old & 0xff)) break; // PATCH.JAVA:need to convert to unsigned byte + } + return ary; + } + public static byte[] Ucase__all(byte[] src) {return Xcase__all(Bool_.Y, src, 0, -1);} + public static byte[] Lcase__all(byte[] src) {return Xcase__all(Bool_.N, src, 0, -1);} + public static byte[] Lcase__all(byte[] src, int bgn, int end) {return Xcase__all(Bool_.N, src, bgn, end);} + private static byte[] Xcase__all(boolean upper, byte[] src, int bgn, int end) { + if (src == null) return null; + int len = end == -1 ? src.length : end - bgn; if (len == 0) return src; + byte[] rv = new byte[len]; + for (int i = 0; i < len; ++i) { + byte b = src[i + bgn]; + if (upper) { + if (b > 96 && b < 123) b -= 32; + } + else { + if (b > 64 && b < 91) b += 32; + } + rv[i] = b; + } + return rv; + } + public static byte[] Ucase__1st(byte[] src) {return Xcase__1st(Bool_.Y, src);} + public static byte[] Lcase__1st(byte[] src) {return Xcase__1st(Bool_.N, src);} + private static byte[] Xcase__1st(boolean upper, byte[] src) { + if (src == null) return null; + int len = src.length; if (len == 0) return src; + byte[] rv = new byte[len]; + byte b = src[0]; + if (upper) { + if (b > 96 && b < 123) b -= 32; + } + else { + if (b > 64 && b < 91) b += 32; + } + rv[0] = b; + for (int i = 1; i < len; ++i) { + rv[i] = src[i]; + } + return rv; + } + public static byte[] Null_if_empty(byte[] v) {return Len_eq_0(v) ? null : v;} + + public static byte[] Escape_ws(byte[] bry) {Bry_bfr bfr = Bry_bfr_.Get(); byte[] rv = Escape_ws(bfr, bry); bfr.Mkr_rls(); return rv;} + public static byte[] Escape_ws(Bry_bfr bfr, byte[] src) { + boolean dirty = false; + int len = src.length; + for (int i = 0; i < len; ++i) { + byte b = src[i]; + byte escape = Byte_.Zero; + switch (b) { + case Byte_ascii.Tab: escape = Byte_ascii.Ltr_t; break; + case Byte_ascii.Nl: escape = Byte_ascii.Ltr_n; break; + case Byte_ascii.Cr: escape = Byte_ascii.Ltr_r; break; + default: if (dirty) bfr.Add_byte(b); break; + } + if (escape != Byte_.Zero) { + if (!dirty) { + dirty = true; + bfr.Add_mid(src, 0, i); + } + bfr.Add_byte_backslash().Add_byte(escape); + } + } + return dirty ? bfr.To_bry_and_clear() : src; + } + public static byte[] Resolve_escape(Bry_bfr bfr, byte escape, byte[] raw, int bgn, int end) { + int pos = bgn; + boolean dirty = false; + while (pos < end) { + byte b = raw[pos]; + if (b == escape) { + if (!dirty) { + dirty = true; + bfr.Add_mid(raw, bgn, pos); + } + ++pos; + if (pos < end) { // check for eos; note that this ignores trailing "\"; EX: "a\" -> "a" + bfr.Add_byte(raw[pos]); + ++pos; + } + } + else { + if (dirty) bfr.Add_byte(b); + ++pos; + } + } + return dirty ? bfr.To_bry_and_clear() : raw; + } + public static void Clear(byte[] bry) { + int len = bry.length; + for (int i = 0; i < len; ++i) + bry[i] = Byte_.Zero; + } + public static byte[] Replace_nl_w_tab(byte[] src, int bgn, int end) { + return Bry_.Replace(Bry_.Mid(src, bgn, end), Byte_ascii.Nl, Byte_ascii.Tab); + } +} diff --git a/100_core/src/gplx/Bry__tst.java b/100_core/src/gplx/Bry__tst.java new file mode 100644 index 000000000..79d381a15 --- /dev/null +++ b/100_core/src/gplx/Bry__tst.java @@ -0,0 +1,295 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; import gplx.core.primitives.*; import gplx.core.brys.*; +public class Bry__tst { + private final Bry__fxt fxt = new Bry__fxt(); + @Test public void new_ascii_() { + fxt.Test_new_a7("a" , Bry_.New_by_ints(97)); // one + fxt.Test_new_a7("abc" , Bry_.New_by_ints(97, 98, 99)); // many + fxt.Test_new_a7("" , Bry_.Empty); // none + fxt.Test_new_a7("¢€𤭢" , Bry_.New_by_ints(63, 63, 63, 63)); // non-ascii -> ? + } + @Test public void new_u8() { + fxt.Test_new_u8("a" , Bry_.New_by_ints(97)); // one + fxt.Test_new_u8("abc" , Bry_.New_by_ints(97, 98, 99)); // many + fxt.Test_new_u8("¢" , Bry_.New_by_ints(194, 162)); // bry_len=2; cent + fxt.Test_new_u8("€" , Bry_.New_by_ints(226, 130, 172)); // bry_len=3; euro + fxt.Test_new_u8("𤭢" , Bry_.New_by_ints(240, 164, 173, 162)); // bry_len=3; example from en.w:UTF-8 + } + @Test public void Add__bry_plus_byte() { + fxt.Test_add("a" , Byte_ascii.Pipe , "a|"); // basic + fxt.Test_add("" , Byte_ascii.Pipe , "|"); // empty String + } + @Test public void Add__byte_plus_bry() { + fxt.Test_add(Byte_ascii.Pipe , "a" , "|a"); // basic + fxt.Test_add(Byte_ascii.Pipe , "" , "|"); // empty String + } + @Test public void Add_w_dlm() { + fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", "b", "c") , "a|b|c"); // basic + fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a") , "a"); // one item + fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", null, "c") , "a||c"); // null + } + @Test public void Add_w_dlm_bry() { + fxt.Test_add_w_dlm("<>", String_.Ary("a","b","c"), "a<>b<>c"); + } + @Test public void MidByPos() { + tst_MidByPos("abcba", 0, 1, "a"); + tst_MidByPos("abcba", 0, 2, "ab"); + tst_MidByPos("abcba", 1, 4, "bcb"); + } void tst_MidByPos(String src, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Mid(Bry_.new_u8(src), bgn, end)));} + @Test public void Replace_one() { + tst_ReplaceOne("a" , "b" , "c" , "a"); + tst_ReplaceOne("b" , "b" , "c" , "c"); + tst_ReplaceOne("bb" , "b" , "c" , "cb"); + tst_ReplaceOne("abcd" , "bc" , "" , "ad"); + tst_ReplaceOne("abcd" , "b" , "ee" , "aeecd"); + } void tst_ReplaceOne(String src, String find, String repl, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Replace_one(Bry_.new_u8(src), Bry_.new_u8(find), Bry_.new_u8(repl))));} + @Test public void XtoStrBytesByInt() { + tst_XtoStrBytesByInt(0, 0); + tst_XtoStrBytesByInt(9, 9); + tst_XtoStrBytesByInt(10, 1, 0); + tst_XtoStrBytesByInt(321, 3, 2, 1); + tst_XtoStrBytesByInt(-321, Bry_.Byte_NegSign, 3, 2, 1); + tst_XtoStrBytesByInt(Int_.Max_value, 2,1,4,7,4,8,3,6,4,7); + } + void tst_XtoStrBytesByInt(int val, int... expdAryAsInt) { + byte[] expd = new byte[expdAryAsInt.length]; + for (int i = 0; i < expd.length; i++) { + int expdInt = expdAryAsInt[i]; + expd[i] = expdInt == Bry_.Byte_NegSign ? Bry_.Byte_NegSign : Byte_ascii.To_a7_str(expdAryAsInt[i]); + } + Tfds.Eq_ary(expd, Bry_.To_a7_bry(val, Int_.DigitCount(val))); + } + @Test public void Has_at_end() { + tst_HasAtEnd("a|bcd|e", "d" , 2, 5, true); // y_basic + tst_HasAtEnd("a|bcd|e", "bcd" , 2, 5, true); // y_many + tst_HasAtEnd("a|bcd|e", "|bcd" , 2, 5, false); // n_long + tst_HasAtEnd("a|bcd|e", "|bc" , 2, 5, false); // n_pos + tst_HasAtEnd("abc", "bc", true); // y + tst_HasAtEnd("abc", "bd", false); // n + tst_HasAtEnd("a", "ab", false); // exceeds_len + } + void tst_HasAtEnd(String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_end(Bry_.new_u8(src), Bry_.new_u8(find), bgn, end));} + void tst_HasAtEnd(String src, String find, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_end(Bry_.new_u8(src), Bry_.new_u8(find)));} + @Test public void Has_at_bgn() { + tst_HasAtBgn("y_basic" , "a|bcd|e", "b" , 2, 5, true); + tst_HasAtBgn("y_many" , "a|bcd|e", "bcd" , 2, 5, true); + tst_HasAtBgn("n_long" , "a|bcd|e", "bcde" , 2, 5, false); + tst_HasAtBgn("n_pos" , "a|bcd|e", "|bc" , 2, 5, false); + } void tst_HasAtBgn(String tst, String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_bgn(Bry_.new_u8(src), Bry_.new_u8(find), bgn, end), tst);} + @Test public void Match() { + tst_Match("abc", 0, "abc", true); + tst_Match("abc", 2, "c", true); + tst_Match("abc", 0, "cde", false); + tst_Match("abc", 2, "abc", false); // bounds check + tst_Match("abc", 0, "abcd", false); + tst_Match("a" , 0, "", false); + tst_Match("" , 0, "a", false); + tst_Match("" , 0, "", true); + tst_Match("ab", 0, "a", false); // FIX: "ab" should not match "a" b/c .length is different + } void tst_Match(String src, int srcPos, String find, boolean expd) {Tfds.Eq(expd, Bry_.Match(Bry_.new_u8(src), srcPos, Bry_.new_u8(find)));} + @Test public void ReadCsvStr() { + tst_ReadCsvStr("a|" , "a"); + tst_ReadCsvStr("|a|", 1 , "a"); + Int_obj_ref bgn = Int_obj_ref.New_zero(); tst_ReadCsvStr("a|b|c|", bgn, "a"); tst_ReadCsvStr("a|b|c|", bgn, "b"); tst_ReadCsvStr("a|b|c|", bgn, "c"); + tst_ReadCsvStr("|", ""); + tst_ReadCsvStr_err("a"); + + tst_ReadCsvStr("'a'|" , "a"); + tst_ReadCsvStr("'a''b'|" , "a'b"); + tst_ReadCsvStr("'a|b'|" , "a|b"); + tst_ReadCsvStr("''|", ""); + tst_ReadCsvStr_err("''"); + tst_ReadCsvStr_err("'a'b'"); + tst_ReadCsvStr_err("'a"); + tst_ReadCsvStr_err("'a|"); + tst_ReadCsvStr_err("'a'"); + } + @Test public void XtoIntBy4Bytes() { // test len=1, 2, 3, 4 + tst_XtoIntBy4Bytes(32, (byte)32); // space + tst_XtoIntBy4Bytes(8707, (byte)34, (byte)3); // ∃ + tst_XtoIntBy4Bytes(6382179, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c); + tst_XtoIntBy4Bytes(1633837924, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c, Byte_ascii.Ltr_d); + } + @Test public void XtoInt() { + tst_XtoInt("1", 1); + tst_XtoInt("123", 123); + tst_XtoInt("a", Int_.Min_value, Int_.Min_value); + tst_XtoInt("-1", Int_.Min_value, -1); + tst_XtoInt("-123", Int_.Min_value, -123); + tst_XtoInt("123-1", Int_.Min_value, Int_.Min_value); + tst_XtoInt("+123", Int_.Min_value, 123); + tst_XtoInt("", -1); + } + void tst_XtoInt(String val, int expd) {tst_XtoInt(val, -1, expd);} + void tst_XtoInt(String val, int or, int expd) {Tfds.Eq(expd, Bry_.To_int_or(Bry_.new_u8(val), or));} + void tst_XtoIntBy4Bytes(int expd, byte... ary) {Tfds.Eq(expd, Bry_.To_int_by_a7(ary), "XtoInt"); Tfds.Eq_ary(ary, Bry_.new_by_int(expd), "XbyInt");} + void tst_ReadCsvStr(String raw, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.New_zero() , expd);} + void tst_ReadCsvStr(String raw, int bgn, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.New(bgn), expd);} + void tst_ReadCsvStr(String raw, Int_obj_ref bgnRef, String expd) { + int bgn = bgnRef.Val(); + boolean rawHasQuotes = String_.CharAt(raw, bgn) == '\''; + String actl = String_.Replace(Bry_.ReadCsvStr(Bry_.new_u8(String_.Replace(raw, "'", "\"")), bgnRef, (byte)'|'), "\"", "'"); + Tfds.Eq(expd, actl, "rv"); + if (rawHasQuotes) { + int quoteAdj = String_.Count(actl, "'"); + Tfds.Eq(bgn + 1 + String_.Len(actl) + 2 + quoteAdj, bgnRef.Val(), "pos_quote"); // +1=lkp.Len; +2=bgn/end quotes + } + else + Tfds.Eq(bgn + 1 + String_.Len(actl), bgnRef.Val(), "pos"); // +1=lkp.Len + } + void tst_ReadCsvStr_err(String raw) { + try {Bry_.ReadCsvStr(Bry_.new_u8(String_.Replace(raw, "'", "\"")), Int_obj_ref.New_zero(), (byte)'|');} + catch (Exception e) {Err_.Noop(e); return;} + Tfds.Fail_expdError(); + } + @Test public void ReadCsvDte() { + tst_ReadCsvDte("20110801 221435.987"); + } void tst_ReadCsvDte(String raw) {Tfds.Eq_date(DateAdp_.parse_fmt(raw, Bry_.Fmt_csvDte), Bry_.ReadCsvDte(Bry_.new_u8(raw + "|"), Int_obj_ref.New_zero(), (byte)'|'));} + @Test public void ReadCsvInt() { + tst_ReadCsvInt("1234567890"); + } void tst_ReadCsvInt(String raw) {Tfds.Eq(Int_.parse(raw), Bry_.ReadCsvInt(Bry_.new_u8(raw + "|"), Int_obj_ref.New_zero(), (byte)'|'));} + @Test public void Trim() { + Trim_tst("a b c", 1, 4, "b"); + Trim_tst("a c", 1, 3, ""); + Trim_tst(" ", 0, 2, ""); + } void Trim_tst(String raw, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Trim(Bry_.new_u8(raw), bgn, end)));} + @Test public void Xto_int_lax() { + tst_Xto_int_lax("12a", 12); + tst_Xto_int_lax("1", 1); + tst_Xto_int_lax("123", 123); + tst_Xto_int_lax("a", 0); + tst_Xto_int_lax("-1", -1); + } + private void tst_Xto_int_lax(String val, int expd) {Tfds.Eq(expd, Bry_.To_int_or__lax(Bry_.new_u8(val), 0, String_.Len(val), 0));} + @Test public void To_int_or__trim_ws() { + tst_Xto_int_trim("123 " , 123); + tst_Xto_int_trim(" 123" , 123); + tst_Xto_int_trim(" 123 " , 123); + tst_Xto_int_trim(" 1 3 " , -1); + } + private void tst_Xto_int_trim(String val, int expd) {Tfds.Eq(expd, Bry_.To_int_or__trim_ws(Bry_.new_u8(val), 0, String_.Len(val), -1));} + @Test public void Compare() { + tst_Compare("abcde", 0, 1, "abcde", 0, 1, CompareAble_.Same); + tst_Compare("abcde", 0, 1, "abcde", 1, 2, CompareAble_.Less); + tst_Compare("abcde", 1, 2, "abcde", 0, 1, CompareAble_.More); + tst_Compare("abcde", 0, 1, "abcde", 0, 2, CompareAble_.Less); + tst_Compare("abcde", 0, 2, "abcde", 0, 1, CompareAble_.More); + tst_Compare("abcde", 2, 3, "abçde", 2, 3, CompareAble_.Less); + } void tst_Compare(String lhs, int lhs_bgn, int lhs_end, String rhs, int rhs_bgn, int rhs_end, int expd) {Tfds.Eq(expd, Bry_.Compare(Bry_.new_u8(lhs), lhs_bgn, lhs_end, Bry_.new_u8(rhs), rhs_bgn, rhs_end));} + @Test public void Increment_last() { + tst_IncrementLast(ary_(0), ary_(1)); + tst_IncrementLast(ary_(0, 255), ary_(1, 0)); + tst_IncrementLast(ary_(104, 111, 112, 101), ary_(104, 111, 112, 102)); + } + byte[] ary_(int... ary) { + byte[] rv = new byte[ary.length]; + for (int i = 0; i < ary.length; i++) + rv[i] = Byte_.By_int(ary[i]); + return rv; + } + void tst_IncrementLast(byte[] ary, byte[] expd) {Tfds.Eq_ary(expd, Bry_.Increment_last(Bry_.Copy(ary)));} + @Test public void Replace_between() { + tst_Replace_between("a[0]b" , "[", "]", "0", "a0b"); + tst_Replace_between("a[0]b[1]c" , "[", "]", "0", "a0b0c"); + tst_Replace_between("a[0b" , "[", "]", "0", "a[0b"); + } public void tst_Replace_between(String src, String bgn, String end, String repl, String expd) {Tfds.Eq(expd, String_.new_a7(Bry_.Replace_between(Bry_.new_a7(src), Bry_.new_a7(bgn), Bry_.new_a7(end), Bry_.new_a7(repl))));} + @Test public void Replace() { + Bry_bfr tmp_bfr = Bry_bfr_.New(); + tst_Replace(tmp_bfr, "a0b" , "0", "00", "a00b"); // 1 -> 1 + tst_Replace(tmp_bfr, "a0b0c" , "0", "00", "a00b00c"); // 1 -> 2 + tst_Replace(tmp_bfr, "a00b00c" , "00", "0", "a0b0c"); // 2 -> 1 + tst_Replace(tmp_bfr, "a0b0" , "0", "00", "a00b00"); // 1 -> 2; EOS + tst_Replace(tmp_bfr, "a00b00" , "00", "0", "a0b0"); // 2 -> 1; EOS + tst_Replace(tmp_bfr, "a0b0" , "1", "2", "a0b0"); // no match + tst_Replace(tmp_bfr, "a0b0" , "b1", "b2", "a0b0"); // false match; EOS + } + public void tst_Replace(Bry_bfr tmp_bfr, String src, String bgn, String repl, String expd) { + Tfds.Eq(expd, String_.new_a7(Bry_.Replace(tmp_bfr, Bry_.new_a7(src), Bry_.new_a7(bgn), Bry_.new_a7(repl)))); + } + @Test public void Split_bry() { + Split_bry_tst("a|b|c|" , "|" , String_.Ary("a", "b", "c")); + Split_bry_tst("a|" , "|" , String_.Ary("a")); + } + void Split_bry_tst(String src, String dlm, String[] expd) { + String[] actl = String_.Ary(Bry_split_.Split(Bry_.new_a7(src), Bry_.new_a7(dlm))); + Tfds.Eq_ary_str(expd, actl); + } + @Test public void Split_lines() { + Tst_split_lines("a\nb" , "a", "b"); // basic + Tst_split_lines("a\nb\n" , "a", "b"); // do not create empty trailing lines + Tst_split_lines("a\r\nb" , "a", "b"); // crlf + Tst_split_lines("a\rb" , "a", "b"); // cr only + } + void Tst_split_lines(String src, String... expd) { + Tfds.Eq_ary(expd, New_ary(Bry_split_.Split_lines(Bry_.new_a7(src)))); + } + String[] New_ary(byte[][] lines) { + int len = lines.length; + String[] rv = new String[len]; + for (int i = 0; i < len; i++) + rv[i] = String_.new_u8(lines[i]); + return rv; + } + @Test public void Match_bwd_any() { + Tst_match_bwd_any("abc", 2, 0, "c", true); + Tst_match_bwd_any("abc", 2, 0, "b", false); + Tst_match_bwd_any("abc", 2, 0, "bc", true); + Tst_match_bwd_any("abc", 2, 0, "abc", true); + Tst_match_bwd_any("abc", 2, 0, "zabc", false); + Tst_match_bwd_any("abc", 1, 0, "ab", true); + } + void Tst_match_bwd_any(String src, int src_end, int src_bgn, String find, boolean expd) { + Tfds.Eq(expd, Bry_.Match_bwd_any(Bry_.new_a7(src), src_end, src_bgn, Bry_.new_a7(find))); + } + @Test public void Trim_end() { + fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.one + fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.many + fxt.Test_trim_end("a" , Byte_ascii.Space, "a"); // trim.none + fxt.Test_trim_end("" , Byte_ascii.Space, ""); // empty + } + @Test public void Mid_w_trim() { + fxt.Test_Mid_w_trim("abc", "abc"); // no ws + fxt.Test_Mid_w_trim(" a b c ", "a b c"); // ws at bgn and end + fxt.Test_Mid_w_trim("\r\n\t a\r\n\t b \r\n\t ", "a\r\n\t b"); // space at bgn and end + fxt.Test_Mid_w_trim("", ""); // handle 0 bytes + fxt.Test_Mid_w_trim(" ", ""); // handle all ws + } + @Test public void New_u8_nl_apos() { + fxt.Test__new_u8_nl_apos(String_.Ary("a"), "a"); + fxt.Test__new_u8_nl_apos(String_.Ary("a", "b"), "a\nb"); + fxt.Test__new_u8_nl_apos(String_.Ary("a", "b'c", "d"), "a\nb\"c\nd"); + } +} +class Bry__fxt { + public void Test_trim_end(String raw, byte trim, String expd) { + byte[] raw_bry = Bry_.new_a7(raw); + Tfds.Eq(expd, String_.new_u8(Bry_.Trim_end(raw_bry, trim, raw_bry.length))); + } + public void Test_new_u8(String raw, byte[] expd) {Tfds.Eq_ary(expd, Bry_.new_u8(raw));} + public void Test_new_a7(String raw, byte[] expd) {Tfds.Eq_ary(expd, Bry_.new_a7(raw));} + public void Test_add(String s, byte b, String expd) {Tfds.Eq_str(expd, String_.new_u8(Bry_.Add(Bry_.new_u8(s), b)));} + public void Test_add(byte b, String s, String expd) {Tfds.Eq_str(expd, String_.new_u8(Bry_.Add(b, Bry_.new_u8(s))));} + public void Test_add_w_dlm(String dlm, String[] itms, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Add_w_dlm(Bry_.new_u8(dlm), Bry_.Ary(itms))));} + public void Test_add_w_dlm(byte dlm, String[] itms, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Add_w_dlm(dlm, Bry_.Ary(itms))));} + public void Test_Mid_w_trim(String src, String expd) {byte[] bry = Bry_.new_u8(src); Tfds.Eq(expd, String_.new_u8(Bry_.Mid_w_trim(bry, 0, bry.length)));} + public void Test__new_u8_nl_apos(String[] ary, String expd) { + Tfds.Eq_str_lines(expd, String_.new_u8(Bry_.New_u8_nl_apos(ary))); + } +} diff --git a/100_core/src/gplx/Bry_bfr.java b/100_core/src/gplx/Bry_bfr.java new file mode 100644 index 000000000..b22047c9a --- /dev/null +++ b/100_core/src/gplx/Bry_bfr.java @@ -0,0 +1,654 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; import gplx.core.brys.*; import gplx.core.encoders.*; +public class Bry_bfr { + private Bry_bfr_mkr_mgr mkr_mgr; private int reset; + public byte[] Bfr() {return bfr;} private byte[] bfr; + public int Len() {return bfr_len;} private int bfr_len; + public boolean Len_eq_0() {return bfr_len == 0;} + public boolean Len_gt_0() {return bfr_len > 0;} + public void Bfr_init(byte[] bfr, int bfr_len) { + synchronized (this) { + this.bfr = bfr; + this.bfr_len = bfr_len; + this.bfr_max = bfr.length; // NOTE: must sync bfr_max, else will fail later during add; bfr will think bfr has .length of bfr_max, when it actually has .length of bfr_len; DATE:2014-03-09 + } + } + public Bry_bfr Mkr_rls() { + if (mkr_mgr != null) { + synchronized (this) { + mkr_mgr.Rls(mkr_idx); + this.mkr_mgr = null; + this.mkr_idx = -1; + } + } + return this; + } + public void Clear_and_rls() { + this.Clear(); + this.Mkr_rls(); + } + public String To_str_and_rls() {return String_.new_u8(To_bry_and_rls());} + public byte[] To_bry_and_rls() { + byte[] rv = null; + synchronized (bfr) { + rv = To_bry(); + this.Clear(); + if (reset > 0) Reset_if_gt(reset); + synchronized (this) { // SAME: Mkr_rls() + mkr_mgr.Rls(mkr_idx); + mkr_idx = -1; // TS: DATE:2016-07-06 + mkr_mgr = null; + } + } + return rv; + } + public Bry_bfr Reset_(int v) {reset = v; return this;} + public Bry_bfr Reset_if_gt(int limit) { + if (bfr_max > limit) { + this.bfr_max = limit; + this.bfr = new byte[limit]; + } + bfr_len = 0; + return this; + } + public Bry_bfr Clear() { + synchronized (this) { + this.bfr_len = 0; + } + return this; + } + public Bry_bfr ClearAndReset() {bfr_len = 0; if (reset > 0) Reset_if_gt(reset); return this;} + public byte Get_at_last_or_nil_if_empty() {return bfr_len == 0 ? Byte_ascii.Null : bfr[bfr_len - 1];} + public Bry_bfr Add_safe(byte[] val) {return val == null ? this : Add(val);} + public Bry_bfr Add(byte[] val) { + int val_len = val.length; + if (bfr_len + val_len > bfr_max) Resize((bfr_max + val_len) * 2); + Bry_.Copy_by_pos(val, 0, val_len, bfr, bfr_len); + // Array_.Copy_to(val, 0, bfr, bfr_len, val_len); + bfr_len += val_len; + return this; + } + public Bry_bfr Add_mid(byte[] val, int bgn, int end) { + int len = end - bgn; + if (len < 0) throw Err_.new_wo_type("negative len", "bgn", bgn, "end", end, "excerpt", String_.new_u8__by_len(val, bgn, bgn + 16)); // NOTE: check for invalid end < bgn, else difficult to debug errors later; DATE:2014-05-11 + if (bfr_len + len > bfr_max) Resize((bfr_max + len) * 2); + Bry_.Copy_by_pos(val, bgn, end, bfr, bfr_len); + // Array_.Copy_to(val, bgn, bfr, bfr_len, len); + bfr_len += len; + return this; + } + public Bry_bfr Add_mid_w_swap(byte[] val, int bgn, int end, byte swap_src, byte swap_trg) { + int len = end - bgn; + if (len < 0) throw Err_.new_wo_type("negative len", "bgn", bgn, "end", end, "excerpt", String_.new_u8__by_len(val, bgn, bgn + 16)); // NOTE: check for invalid end < bgn, else difficult to debug errors later; DATE:2014-05-11 + if (bfr_len + len > bfr_max) Resize((bfr_max + len) * 2); + int val_len = end - bgn; + for (int i = 0; i < val_len; ++i) { + byte b = val[i + bgn]; if (b == swap_src) b = swap_trg; + bfr[i + bfr_len] = b; + } + bfr_len += len; + return this; + } + public Bry_bfr Add_bry_ref_obj(Bry_obj_ref v) {v.Bfr_arg__add(this); return this;} + public Bry_bfr Add_bfr_and_preserve(Bry_bfr src) { + int len = src.bfr_len; + if (bfr_len + len > bfr_max) Resize((bfr_max + len) * 2); + Bry_.Copy_by_pos(src.bfr, 0, len, bfr, bfr_len); + // Array_.Copy_to(src.bfr, 0, bfr, bfr_len, len); + bfr_len += len; + return this; + } + public Bry_bfr Add_bfr_and_clear(Bry_bfr src) { + Add_bfr_and_preserve(src); + src.ClearAndReset(); + return this; + } + public Bry_bfr Add_bfr_or_mid(boolean escaped, Bry_bfr tmp_bfr, byte[] src, int src_bgn, int src_end) { + return escaped + ? this.Add_bfr_and_clear(tmp_bfr) + : this.Add_mid(src, src_bgn, src_end); + } + public Bry_bfr Add_bfr_trim_and_clear(Bry_bfr src, boolean trim_bgn, boolean trim_end) {return Add_bfr_trim_and_clear(src, trim_bgn, trim_end, Bry_.Trim_ary_ws);} + public Bry_bfr Add_bfr_trim_and_clear(Bry_bfr src, boolean trim_bgn, boolean trim_end, byte[] trim_ary) { + int src_len = src.bfr_len; + if (bfr_len + src_len > bfr_max) Resize((bfr_max + src_len) * 2); + byte[] src_bry = src.Bfr(); + int src_bgn = 0, src_end = src_len; + boolean all_ws = true; + if (trim_bgn) { + for (int i = 0; i < src_len; i++) { + byte b = src_bry[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Null) { + src_bgn = i; + i = src_len; + all_ws = false; + } + } + if (all_ws) return this; + } + if (trim_end) { + for (int i = src_len - 1; i > -1; i--) { + byte b = src_bry[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Null) { + src_end = i + 1; + i = -1; + all_ws = false; + } + } + if (all_ws) return this; + } + src_len = src_end - src_bgn; + Bry_.Copy_by_pos(src.bfr, src_bgn, src_end, bfr, bfr_len); + // Array_.Copy_to(src.bfr, src_bgn, bfr, bfr_len, src_len); + bfr_len += src_len; + src.Clear(); + return this; + } + public Bry_bfr Add_byte_as_a7(byte v) {return Add_byte((byte)(v + Byte_ascii.Num_0));} + public Bry_bfr Add_byte_eq() {return Add_byte(Byte_ascii.Eq);} + public Bry_bfr Add_byte_pipe() {return Add_byte(Byte_ascii.Pipe);} + public Bry_bfr Add_byte_comma() {return Add_byte(Byte_ascii.Comma);} + public Bry_bfr Add_byte_semic() {return Add_byte(Byte_ascii.Semic);} + public Bry_bfr Add_byte_apos() {return Add_byte(Byte_ascii.Apos);} + public Bry_bfr Add_byte_slash() {return Add_byte(Byte_ascii.Slash);} + public Bry_bfr Add_byte_backslash() {return Add_byte(Byte_ascii.Backslash);} + public Bry_bfr Add_byte_quote() {return Add_byte(Byte_ascii.Quote);} + public Bry_bfr Add_byte_space() {return Add_byte(Byte_ascii.Space);} + public Bry_bfr Add_byte_nl() {return Add_byte(Byte_ascii.Nl);} + public Bry_bfr Add_byte_dot() {return Add_byte(Byte_ascii.Dot);} + public Bry_bfr Add_byte_colon() {return Add_byte(Byte_ascii.Colon);} + public Bry_bfr Add_byte(byte val) { + int new_pos = bfr_len + 1; + if (new_pos > bfr_max) Resize(bfr_len * 2); + bfr[bfr_len] = val; + bfr_len = new_pos; + return this; + } + public Bry_bfr Add_byte_repeat(byte b, int len) { + if (bfr_len + len > bfr_max) Resize((bfr_max + len) * 2); + for (int i = 0; i < len; i++) + bfr[i + bfr_len] = b; + bfr_len += len; + return this; + } + public Bry_bfr Add_byte_if_not_last(byte b) { + if (bfr_len == 0 || (bfr_len > 0 && bfr[bfr_len - 1] == b)) return this; + this.Add_byte(b); + return this; + } + public Bry_bfr Add_byte_variable(byte v) {return Add_int_variable(v);} + public Bry_bfr Add_short_variable(short v) {return Add_int_variable(v);} + public Bry_bfr Add_u8_int(int val) { + if (bfr_len + 4 > bfr_max) Resize((bfr_max + 4) * 2); + int utf8_len = gplx.core.intls.Utf16_.Encode_int(val, bfr, bfr_len); + bfr_len += utf8_len; + return this; + } + public Bry_bfr Add_bool(boolean v) {return Add(v ? Bool_.True_bry : Bool_.False_bry);} + public Bry_bfr Add_int_bool(boolean v) {return Add_int_fixed(v ? 1 : 0, 1);} + public Bry_bfr Add_int_variable(int val) { + if (val < 0) { + this.Add(Int_.To_bry(val)); + return this; + } + int log10 = Int_.Log10(val); + int slots = val > -1 ? log10 + 1 : log10 * -1 + 2; + return Add_int(val, log10, slots); + } + public Bry_bfr Add_int_pad_bgn(byte pad_byte, int str_len, int val) { + int digit_len = Int_.DigitCount(val); + int pad_len = str_len - digit_len; + if (pad_len > 0) // note that this skips pad_len == 0, as well as guarding against negative pad_len; EX: pad(" ", 3, 1234) -> "1234" + Add_byte_repeat(pad_byte, pad_len); + Add_int_fixed(val, digit_len); + return this; + } + public Bry_bfr Add_int_digits(int digits, int val) {return Add_int(val, Int_.Log10(val), digits);} + public Bry_bfr Add_int_fixed(int val, int digits) {return Add_int(val, Int_.Log10(val), digits);} + public Bry_bfr Add_int(int val, int valLog, int arySlots) { + int aryBgn = bfr_len, aryEnd = bfr_len + arySlots; + if (aryEnd > bfr_max) Resize((aryEnd) * 2); + if (val < 0) { + bfr[aryBgn++] = Byte_ascii.Dash; + val *= -1; // make positive + valLog *= -1; // valLog will be negative; make positive + arySlots -= 1; // reduce slot by 1 + } + if (valLog >= arySlots) { + val %= Int_.Log10Ary[arySlots]; + } + for (int i = 0; i < arySlots; i++) { + int logIdx = arySlots - i - 1; + int div = logIdx < Int_.Log10AryLen ? Int_.Log10Ary[logIdx] : Int_.Max_value; + bfr[aryBgn + i] = (byte)((val / div) + 48); + val %= div; + } + bfr_len = aryEnd; + return this; + } + public Bry_bfr Add_long_variable(long v) {int digitCount = Long_.DigitCount(v); return Add_long(v, digitCount, digitCount);} + public Bry_bfr Add_long_fixed(long val, int digits) {return Add_long(val, Long_.DigitCount(val), digits);} + protected Bry_bfr Add_long(long val, int digitCount, int arySlots) { + int aryBgn = bfr_len, aryEnd = bfr_len + arySlots; + if (aryEnd > bfr_max) Resize((aryEnd) * 2); + if (val < 0) { + bfr[aryBgn++] = Byte_ascii.Dash; + val *= -1; // make positive + arySlots -= 1; // reduce slot by 1 + } + if (digitCount >= arySlots) { + val %= Long_.Log10Ary[arySlots]; + } + for (int i = 0; i < arySlots; i++) { + int logIdx = arySlots - i - 1; + long div = logIdx < Long_.Log10Ary_len ? Long_.Log10Ary[logIdx] : Long_.Max_value; + bfr[aryBgn + i] = (byte)((val / div) + 48); + val %= div; + } + bfr_len = aryEnd; + return this; + } + public Bry_bfr Add_bry_comma(byte[] v) {return Add_bry(Byte_ascii.Comma, v);} + public Bry_bfr Add_bry(byte dlm, byte[] v) { + if (v == null) return this; + int v_len = v.length; + for (int i = 0; i < v_len; i++) { + if (i != 0) this.Add_byte(dlm); + this.Add_int_variable(v[i]); + } + return this; + } + public Bry_bfr Add_bry_escape(byte quote_byte, byte[] escape, byte[] val, int bgn, int end) { // used for xml_wtr; DATE:2015-04-09 + boolean clean = true; // add with chunks of bytes instead of one-by-one + for (int i = bgn; i < end; ++i) { + byte b = val[i]; + if (clean) { + if (b == quote_byte) { + clean = false; + this.Add_mid(val, bgn, i); + this.Add(escape); + } + else {} + } + else { + if (b == quote_byte) this.Add(escape); + else this.Add_byte(b); + } + } + if (clean) + Add(val); + return this; + } + public Bry_bfr Add_str_u8_w_nl(String s) {Add_str_u8(s); return Add_byte_nl();} + public Bry_bfr Add_str_u8_null(String s) {return Add_str_u8(s == null ? String_.Null_mark : s);} + public Bry_bfr Add_str_u8(String str) { + try { + int str_len = str.length(); + int bry_len = Bry_.new_u8__by_len(str, str_len); + if (bfr_len + bry_len > bfr_max) Resize((bfr_max + bry_len) * 2); + Bry_.new_u8__write(str, str_len, bfr, bfr_len); + bfr_len += bry_len; + return this; + } + catch (Exception e) {throw Err_.new_exc(e, "core", "invalid UTF-8 sequence", "s", str);} + } + public Bry_bfr Add_str_u8_many(String... ary) { + int len = ary.length; + for (int i = 0; i < len; ++i) + Add_str_u8(ary[i]); + return this; + } + public Bry_bfr Add_str_u8_fmt(String fmt, Object... args) { + Add_str_u8(String_.Format(fmt, args)); + return this; + } + public Bry_bfr Add_str_a7_null(String s) {return Add_str_a7(s == null ? String_.Null_mark : s);} + public Bry_bfr Add_str_a7_w_nl(String s) {Add_str_a7(s); return Add_byte_nl();} + public Bry_bfr Add_str_a7(String str) { + try { + int bry_len = str.length(); + if (bfr_len + bry_len > bfr_max) Resize((bfr_max + bry_len) * 2); + for (int i = 0; i < bry_len; ++i) { + char c = str.charAt(i); + if (c > 128) c = '?'; + bfr[i + bfr_len] = (byte)c; + } + bfr_len += bry_len; + return this; + } + catch (Exception e) {throw Err_.new_exc(e, "core", "invalid UTF-8 sequence", "s", str);} + } + public Bry_bfr Add_kv_dlm(boolean line, String key, Object val) { + this.Add_str_a7(key).Add_byte_colon().Add_byte_space(); + this.Add(Bry_.new_u8(Object_.Xto_str_strict_or_null_mark(val))); + this.Add_byte(line ? Byte_ascii.Nl : Byte_ascii.Tab); + return this; + } + public Bry_bfr Add_float(float f) {Add_str_a7(Float_.To_str(f)); return this;} + public Bry_bfr Add_double(double v) {Add_str_a7(Double_.To_str(v)); return this;} + public Bry_bfr Add_dte(DateAdp val) {return Add_dte_segs(val.Year(), val.Month(),val.Day(), val.Hour(), val.Minute(), val.Second(), val.Frac());} + public Bry_bfr Add_dte_segs(int y, int M, int d, int H, int m, int s, int f) { // yyyyMMdd HHmmss.fff + if (bfr_len + 19 > bfr_max) Resize((bfr_len + 19) * 2); + bfr[bfr_len + 0] = (byte)((y / 1000) + Bry_.Ascii_zero); y %= 1000; + bfr[bfr_len + 1] = (byte)((y / 100) + Bry_.Ascii_zero); y %= 100; + bfr[bfr_len + 2] = (byte)((y / 10) + Bry_.Ascii_zero); y %= 10; + bfr[bfr_len + 3] = (byte)( y + Bry_.Ascii_zero); + bfr[bfr_len + 4] = (byte)((M / 10) + Bry_.Ascii_zero); M %= 10; + bfr[bfr_len + 5] = (byte)( M + Bry_.Ascii_zero); + bfr[bfr_len + 6] = (byte)((d / 10) + Bry_.Ascii_zero); d %= 10; + bfr[bfr_len + 7] = (byte)( d + Bry_.Ascii_zero); + bfr[bfr_len + 8] = Byte_ascii.Space; + bfr[bfr_len + 9] = (byte)((H / 10) + Bry_.Ascii_zero); H %= 10; + bfr[bfr_len + 10] = (byte)( H + Bry_.Ascii_zero); + bfr[bfr_len + 11] = (byte)((m / 10) + Bry_.Ascii_zero); m %= 10; + bfr[bfr_len + 12] = (byte)( m + Bry_.Ascii_zero); + bfr[bfr_len + 13] = (byte)((s / 10) + Bry_.Ascii_zero); s %= 10; + bfr[bfr_len + 14] = (byte)( s + Bry_.Ascii_zero); + bfr[bfr_len + 15] = Byte_ascii.Dot; + bfr[bfr_len + 16] = (byte)((f / 100) + Bry_.Ascii_zero); f %= 100; + bfr[bfr_len + 17] = (byte)((f / 10) + Bry_.Ascii_zero); f %= 10; + bfr[bfr_len + 18] = (byte)( f + Bry_.Ascii_zero); + bfr_len += 19; + return this; + } + public Bry_bfr Add_dte_utc(int y, int M, int d, int H, int m, int s, int f) { // yyyy-MM-ddTHH:mm:ssZ + if (bfr_len + 20 > bfr_max) Resize((bfr_len + 20) * 2); + bfr[bfr_len + 0] = (byte)((y / 1000) + Bry_.Ascii_zero); y %= 1000; + bfr[bfr_len + 1] = (byte)((y / 100) + Bry_.Ascii_zero); y %= 100; + bfr[bfr_len + 2] = (byte)((y / 10) + Bry_.Ascii_zero); y %= 10; + bfr[bfr_len + 3] = (byte)( y + Bry_.Ascii_zero); + bfr[bfr_len + 4] = Byte_ascii.Dash; + bfr[bfr_len + 5] = (byte)((M / 10) + Bry_.Ascii_zero); M %= 10; + bfr[bfr_len + 6] = (byte)( M + Bry_.Ascii_zero); + bfr[bfr_len + 7] = Byte_ascii.Dash; + bfr[bfr_len + 8] = (byte)((d / 10) + Bry_.Ascii_zero); d %= 10; + bfr[bfr_len + 9] = (byte)( d + Bry_.Ascii_zero); + bfr[bfr_len + 10] = Byte_ascii.Ltr_T; + bfr[bfr_len + 11] = (byte)((H / 10) + Bry_.Ascii_zero); H %= 10; + bfr[bfr_len + 12] = (byte)( H + Bry_.Ascii_zero); + bfr[bfr_len + 13] = Byte_ascii.Colon; + bfr[bfr_len + 14] = (byte)((m / 10) + Bry_.Ascii_zero); m %= 10; + bfr[bfr_len + 15] = (byte)( m + Bry_.Ascii_zero); + bfr[bfr_len + 16] = Byte_ascii.Colon; + bfr[bfr_len + 17] = (byte)((s / 10) + Bry_.Ascii_zero); s %= 10; + bfr[bfr_len + 18] = (byte)( s + Bry_.Ascii_zero); + bfr[bfr_len + 19] = Byte_ascii.Ltr_Z; + bfr_len += 20; + return this; + } + public Bry_bfr Add_swap_ws(byte[] src) {return Add_swap_ws(src, 0, src.length);} + public Bry_bfr Add_swap_ws(byte[] src, int bgn, int end) { + int len = end - bgn; + if (bfr_len + (len * 2) > bfr_max) Resize((bfr_max + (len * 2)) * 2); + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Nl: bfr[bfr_len] = Byte_ascii.Backslash; bfr[bfr_len + 1] = Byte_ascii.Ltr_n; bfr_len += 2; break; + case Byte_ascii.Tab: bfr[bfr_len] = Byte_ascii.Backslash; bfr[bfr_len + 1] = Byte_ascii.Ltr_t; bfr_len += 2; break; + case Byte_ascii.Backslash: bfr[bfr_len] = Byte_ascii.Backslash; bfr[bfr_len + 1] = Byte_ascii.Backslash; bfr_len += 2; break; + default: bfr[bfr_len] = b; ++bfr_len; break; + } + } + return this; + } + public Bry_bfr Add_str_pad_space_bgn(String v, int pad_max) {return Add_str_pad_space(v, pad_max, Bool_.N);} + public Bry_bfr Add_str_pad_space_end(String v, int pad_max) {return Add_str_pad_space(v, pad_max, Bool_.Y);} + Bry_bfr Add_str_pad_space(String v, int pad_max, boolean pad_end) { + byte[] v_bry = Bry_.new_u8(v); + if (pad_end) Add(v_bry); + int pad_len = pad_max - v_bry.length; + if (pad_len > 0) + Add_byte_repeat(Byte_ascii.Space, pad_len); + if (!pad_end) Add(v_bry); + return this; + } + + public Bry_bfr Add_obj(Object o) { + if (o == null) return this; // treat null as empty String; + Class o_type = o.getClass(); + if (o_type == byte[].class) Add((byte[])o); + else if (o_type == Integer.class) Add_int_variable(Int_.cast(o)); + else if (o_type == Byte.class) Add_byte(Byte_.cast(o)); + else if (o_type == Long.class) Add_long_variable(Long_.cast(o)); + else if (o_type == String.class) Add_str_u8((String)o); + else if (o_type == Bry_bfr.class) Add_bfr_and_preserve((Bry_bfr)o); + else if (o_type == DateAdp.class) Add_dte((DateAdp)o); + else if (o_type == Io_url.class) Add(((Io_url)o).RawBry()); + else if (o_type == Boolean.class) Add_yn(Bool_.cast(o)); + else if (o_type == Double.class) Add_double(Double_.cast(o)); + else if (o_type == Float.class) Add_float(Float_.cast(o)); + else ((Bfr_arg)o).Bfr_arg__add(this); + return this; + } + public Bry_bfr Add_obj_strict(Object o) { + if (o == null) return this; // treat null as empty String; + Class o_type = o.getClass(); + if (o_type == byte[].class) Add((byte[])o); + else if (o_type == Integer.class) Add_int_variable(Int_.cast(o)); + else if (o_type == Byte.class) Add_byte(Byte_.cast(o)); + else if (o_type == Long.class) Add_long_variable(Long_.cast(o)); + else if (o_type == String.class) Add_str_u8((String)o); + else if (o_type == Bry_bfr.class) Add_bfr_and_preserve((Bry_bfr)o); + else if (o_type == DateAdp.class) Add_dte((DateAdp)o); + else if (o_type == Io_url.class) Add(((Io_url)o).RawBry()); + else if (o_type == Boolean.class) Add_bool(Bool_.cast(o)); + else if (o_type == Double.class) Add_double(Double_.cast(o)); + else if (o_type == Float.class) Add_float(Float_.cast(o)); + else ((Bfr_arg)o).Bfr_arg__add(this); + return this; + } + public Bry_bfr Add_yn(boolean v) {Add_byte(v ? Byte_ascii.Ltr_y : Byte_ascii.Ltr_n); return this;} + public Bry_bfr Add_base85_len_5(int v) {return Add_base85(v, 5);} + public Bry_bfr Add_base85(int v, int pad) { + int new_len = bfr_len + pad; + if (new_len > bfr_max) Resize((new_len) * 2); + Base85_.Set_bry(v, bfr, bfr_len, pad); + bfr_len = new_len; + return this; + } + public boolean Match_end_byt(byte b) {return bfr_len == 0 ? false : bfr[bfr_len - 1] == b;} + public boolean Match_end_byt_nl_or_bos() {return bfr_len == 0 ? true : bfr[bfr_len - 1] == Byte_ascii.Nl;} + public boolean Match_end_ary(byte[] ary) {return Bry_.Match(bfr, bfr_len - ary.length, bfr_len, ary);} + public Bry_bfr Insert_at(int add_pos, byte[] add_bry) {return Insert_at(add_pos, add_bry, 0, add_bry.length);} + public Bry_bfr Insert_at(int add_pos, byte[] add_bry, int add_bgn, int add_end) { + int add_len = add_end - add_bgn; + int new_max = bfr_max + add_len; + byte[] new_bfr = new byte[new_max]; + if (add_pos > 0) + Bry_.Copy_by_pos (bfr , 0, add_pos, new_bfr, 0); + Bry_.Copy_by_pos (add_bry, add_bgn, add_end, new_bfr, add_pos); + Bry_.Copy_by_pos (bfr , add_pos, bfr_len, new_bfr, add_pos + add_len); + bfr = new_bfr; + bfr_len += add_len; + bfr_max = new_max; + return this; + } + public Bry_bfr Delete_rng_to_bgn(int pos) {return Delete_rng(0, pos);} + public Bry_bfr Delete_rng_to_end(int pos) {return Delete_rng(pos, bfr_len);} + public Bry_bfr Delete_rng(int rng_bgn, int rng_end) { + int rng_len = rng_end - rng_bgn; + Bry_.Copy_by_pos(bfr, rng_end, bfr_len, bfr, rng_bgn); + bfr_len -= rng_len; + return this; + } + public Bry_bfr Del_by_1() { + bfr_len -= 1; bfr[bfr_len] = 0; return this; + } + public Bry_bfr Del_by(int count) { + int new_len = bfr_len - count; + if (new_len > -1) bfr_len = new_len; + return this; + } + public Bry_bfr Trim_end(byte trim_byte) { + if (bfr_len == 0) return this; + int count = 0; + for (int i = bfr_len - 1; i > -1; --i) { + byte b = bfr[i]; + if (b == trim_byte) + ++count; + else + break; + } + if (count > 0) + this.Del_by(count); + return this; + } + public Bry_bfr Concat_skip_empty(byte[] dlm, byte[]... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + boolean itm_has_bytes = Bry_.Len_gt_0(itm); + if ( i != 0 + && itm_has_bytes + && bfr_len > 0 + ) + this.Add(dlm); + if (itm_has_bytes) + this.Add(itm); + } + return this; + } + public boolean Eq(byte b) {return bfr_len == 1 && bfr[0] == b;} + public byte[] To_bry(int bgn, int end) {return bfr_len == 0 ? Bry_.Empty : Bry_.Mid(bfr, bgn, end);} + public byte[] To_bry() {return bfr_len == 0 ? Bry_.Empty : Bry_.Mid(bfr, 0, bfr_len);} + public byte[] To_bry_and_clear_and_trim() {return To_bry_and_clear_and_trim(true, true, Bry_.Trim_ary_ws);} + public byte[] To_bry_and_clear_and_trim(boolean trim_bgn, boolean trim_end, byte[] trim_bry) { + byte[] rv = Bry_.Trim(bfr, 0, bfr_len, trim_bgn, trim_end, trim_bry); + this.Clear(); + return rv; + } + public byte[] To_bry_and_clear() { + byte[] rv = To_bry(); + this.Clear(); + if (reset > 0) Reset_if_gt(reset); + return rv; + } + public byte[] To_bry_and_clear_and_rls() { + byte[] rv = To_bry_and_clear(); + this.Mkr_rls(); + return rv; + } + public String To_str() {return String_.new_u8(To_bry());} + public String To_str_by_pos(int bgn, int end) {return String_.new_u8(To_bry(), bgn, end);} + public String To_str_and_clear() {return String_.new_u8(To_bry_and_clear());} + public String To_str_and_clear_and_trim() {return String_.new_u8(To_bry_and_clear_and_trim());} + public int To_int_and_clear(int or) {int rv = To_int(or); this.Clear(); return rv;} + public int To_int(int or) { + switch (bfr_len) { + case 0: return or; + case 1: { + byte b = bfr[0]; + return Byte_ascii.Is_num(b) ? b - Byte_ascii.Num_0 : or; + } + default: + long rv = 0, mult = 1; + for (int i = bfr_len - 1; i > -1; i--) { + byte b = bfr[i]; + if (!Byte_ascii.Is_num(b)) return or; + long dif = (b - Byte_ascii.Num_0 ) * mult; + long new_val = rv + dif; + if (new_val > Int_.Max_value) return or; // if number is > 2^32 consider error (int overflow); return or; DATE:2014-06-10 + rv = new_val; + mult *= 10; + } + return (int)rv; + } + } + public void Rls() { + bfr = null; + this.Mkr_rls(); + } + public byte[][] To_bry_ary_and_clear() { + if (bfr_len == 0) return Bry_.Ary_empty; + Int_list line_ends = Find_all(Byte_ascii.Nl); + + // create lines + int lines_len = line_ends.Len(); + byte[][] rv = new byte[lines_len][]; + int line_bgn = 0; + for (int i = 0; i < lines_len; ++i) { + int line_end = line_ends.Get_at(i); + rv[i] = Bry_.Mid(bfr, line_bgn, line_end); + line_bgn = line_end + 1; + } + this.ClearAndReset(); + return rv; + } + public String[] To_str_ary_and_clear() { + if (bfr_len == 0) return String_.Ary_empty; + Int_list line_ends = Find_all(Byte_ascii.Nl); + + // create lines + int lines_len = line_ends.Len(); + String[] rv = new String[lines_len]; + int line_bgn = 0; + for (int i = 0; i < lines_len; ++i) { + int line_end = line_ends.Get_at(i); + rv[i] = String_.new_u8(bfr, line_bgn, line_end); + line_bgn = line_end + 1; + } + this.ClearAndReset(); + return rv; + } + private Int_list Find_all(byte find) { + Int_list rv = new Int_list(); + // scan bfr for nl + int line_bgn = 0, line_end = 0; + while (line_bgn < bfr_len) { + line_end = Bry_find_.Find_fwd(bfr, find, line_bgn, bfr_len); + if (line_end == Bry_find_.Not_found) { // no more \n; add bfr_end + rv.Add(bfr_len); + break; + } + else { // \n found; add it + rv.Add(line_end); + line_bgn = line_end + 1; + } + } + return rv; + } + @Override public int hashCode() {return Bry_obj_ref.CalcHashCode(bfr, 0, bfr_len);} + @Override public boolean equals(Object obj) { + if (obj == null) return false; // NOTE: strange, but null check needed; throws null error; EX.WP: File:Eug�ne Delacroix - La libert� guidant le peuple.jpg + Bry_obj_ref comp = (Bry_obj_ref)obj; + return Bry_.Match(bfr, 0, bfr_len, comp.Val(), comp.Val_bgn(), comp.Val_end()); + } + public void Resize(int v) { + bfr_max = v; + bfr = Bry_.Resize(bfr, 0, v); + } + public int Mkr_idx() {return mkr_idx;} private int mkr_idx = -1; + public boolean Mkr_idx_is_null() {return mkr_idx == -1;} + public int Bfr_max() {return bfr_max;} private int bfr_max; + public Bry_bfr Mkr_init(Bry_bfr_mkr_mgr mkr_mgr, int itm) { + synchronized (this) { + this.mkr_mgr = mkr_mgr; this.mkr_idx = itm; + } + return this; + } + protected Bry_bfr() {} + public Bry_bfr(int bfr_max) { + Init(bfr_max); + } + protected void Init(int bfr_max) { + this.bfr_max = bfr_max; + this.bfr = new byte[bfr_max]; + } +} diff --git a/100_core/src/gplx/Bry_bfr_.java b/100_core/src/gplx/Bry_bfr_.java new file mode 100644 index 000000000..45904aad6 --- /dev/null +++ b/100_core/src/gplx/Bry_bfr_.java @@ -0,0 +1,45 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.brys.*; +public class Bry_bfr_ { + public static Bry_bfr New() {return new Bry_bfr(16);} + public static Bry_bfr New_w_size(int v) {return new Bry_bfr(v);} + public static Bry_bfr Reset(int v) {return new Bry_bfr(16).Reset_(v);} // PERF: set initial size to 16, not reset val; allows for faster "startup"; DATE:2014-06-14 + + public static final Bry_bfr[] Ary_empty = new Bry_bfr[0]; + private static Bry_bfr_mkr_mgr dflt; + public static Bry_bfr Get() {if (dflt == null) dflt = new Bry_bfr_mkr_mgr(Bry_bfr_mkr.Tid_b128, 128); return dflt.Get();} // NOTE: lazy else "Object synchronization" error; DATE:2015-11-18 + public static void Assert_at_end(Bry_bfr bfr, byte assert_byte) { + int len = bfr.Len(); if (len == 0) return; + int assert_count = 0; + byte[] bfr_bry = bfr.Bfr(); + for (int i = len - 1; i > -1; --i) { + byte b = bfr_bry[i]; + if (b == assert_byte) + ++assert_count; + else + break; + } + switch (assert_count) { + case 0: bfr.Add_byte(assert_byte); break; + case 1: break; + default: bfr.Del_by(assert_count - 1); break; + } + } +} diff --git a/100_core/src/gplx/Bry_bfr_tst.java b/100_core/src/gplx/Bry_bfr_tst.java new file mode 100644 index 000000000..e3a7b474c --- /dev/null +++ b/100_core/src/gplx/Bry_bfr_tst.java @@ -0,0 +1,236 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Bry_bfr_tst { + private Bry_bfr bb = Bry_bfr_.New(); + @Before public void setup() {bb.Clear();} private ByteAryBfr_fxt fxt = new ByteAryBfr_fxt(); + @Test public void AddByte() { + bb = Bry_bfr_.New_w_size(2); // NOTE: make sure auto-expands + tst_AddByte("a", "a", 2); + tst_AddByte("b", "ab", 2); + tst_AddByte("c", "abc", 4); + } + @Test public void AddAry() { // NOTE: make sure auto-expands + bb = Bry_bfr_.New_w_size(2); + tst_AddByte("abcd", "abcd", 12); + } + @Test public void Add_byte_repeat() { // NOTE: make sure auto-expands + bb = Bry_bfr_.New_w_size(2); + tst_Add_byte_repeat(Byte_ascii.Space, 12, String_.Repeat(" ", 12)); + } void tst_Add_byte_repeat(byte b, int len, String expd) {Tfds.Eq(expd, bb.Add_byte_repeat(b, len).To_str_and_clear());} + void tst_AddByte(String s, String expdStr, int expdLen) { + if (String_.Len(s) == 1) + bb.Add_byte((byte)String_.CharAt(s, 0)); + else + bb.Add(Bry_.new_u8(s)); + Tfds.Eq(expdStr, String_.new_u8(bb.To_bry())); + Tfds.Eq(expdLen, bb.Bfr_max()); + } + @Test public void Add_dte() { + tst_AddDte("20110801 221435.987"); + } + void tst_AddDte(String raw) { + bb.Add_dte(DateAdp_.parse_fmt(raw, Bry_.Fmt_csvDte)); + Tfds.Eq(raw, String_.new_u8(bb.To_bry())); + } + @Test public void Add_int_variable() { + Add_int_variable(-1); + Add_int_variable(-12); + Add_int_variable(-1234); + Add_int_variable(2); + Add_int_variable(12); + Add_int_variable(1234); + Add_int_variable(123456789); + } + @Test public void Add_float() { + tst_Add_float(1 / 3); + tst_Add_float(-1 / 3); + } + void tst_Add_float(float v) { + bb.Add_float(v); + Tfds.Eq(v, Float_.parse(String_.new_u8(bb.To_bry()))); + } + void Add_int_variable(int val) { + bb.Clear(); + bb.Add_int_variable(val); + Tfds.Eq(val, Int_.parse(String_.new_u8(bb.To_bry()))); + } + @Test public void Add_int_fixed_len3() {tst_Add_int_fixed(123, 3, "123");} + @Test public void Add_int_fixed_pad_1() {tst_Add_int_fixed(2, 1, "2");} + @Test public void Add_int_fixed_pad_2() {tst_Add_int_fixed(2, 2, "02");} + @Test public void Add_int_fixed_pad_16() {tst_Add_int_fixed(2, 16, "0000000000000002");} // test overflows int + @Test public void Add_int_fixed_neg() {tst_Add_int_fixed(-2, 2, "-2");} + @Test public void Add_int_fixed_neg_pad1() {tst_Add_int_fixed(-2, 1, "-");} + @Test public void Add_int_fixed_chop_1() {tst_Add_int_fixed(123, 1, "3");} + @Test public void Add_int_fixed_chop_neg() {tst_Add_int_fixed(-21, 2, "-1");} + void tst_Add_int_fixed(int val, int digits, String expd) {Tfds.Eq(expd, String_.new_u8(bb.Add_int_fixed(val, digits).To_bry()));} + @Test public void Add_long_fixed_len3() {tst_Add_long_fixed(123, 3, "123");} + @Test public void Add_long_fixed_pad_1() {tst_Add_long_fixed(2, 1, "2");} + @Test public void Add_long_fixed_pad_2() {tst_Add_long_fixed(2, 2, "02");} + @Test public void Add_long_fixed_pad_16() {tst_Add_long_fixed(2, 16, "0000000000000002");} // test overflows long + @Test public void Add_long_fixed_neg() {tst_Add_long_fixed(-2, 2, "-2");} + @Test public void Add_long_fixed_neg_pad1() {tst_Add_long_fixed(-2, 1, "-");} + @Test public void Add_long_fixed_chop_1() {tst_Add_long_fixed(123, 1, "3");} + @Test public void Add_long_fixed_chop_neg() {tst_Add_long_fixed(-21, 2, "-1");} + @Test public void Add_long_fixed_large() {tst_Add_long_fixed(123456789012345L, 15, "123456789012345");} + void tst_Add_long_fixed(long val, int digits, String expd) {Tfds.Eq(expd, String_.new_u8(bb.Add_long_fixed(val, digits).To_bry()));} + @Test public void AddDte_short() { + tst_AddDte_short("2010-08-26T22:38:36Z"); + } + void tst_AddDte_short(String raw) { +// byte[] ary = String_.XtoByteAryAscii(raw); +// Bry_fmtr_IntBldr ib = new Bry_fmtr_IntBldr(); +// int y = 0, m = 0, d = 0, h = 0, n = 0, s = 0, aryLen = ary.length; +// for (int i = 0; i < aryLen; i++) { +// byte b = ary[i]; +// switch (i) { +// case 4: y = ib.To_int_and_clear(); break; +// case 7: m = ib.To_int_and_clear(); break; +// case 10: d = ib.To_int_and_clear(); break; +// case 13: h = ib.To_int_and_clear(); break; +// case 16: n = ib.To_int_and_clear(); break; +// case 19: s = ib.To_int_and_clear(); break; +// default: ib.Add(b); break; +// } +// } +// long l = Pow38_to(y, m, d, h, n, s); +//// Base85_.Set_bry(l, bb. +// bb.Add_int(l); + } +// @Test public void InsertAt_str() { +// tst_InsertAt_str("", 0, "c", "c"); +// tst_InsertAt_str("ab", 0, "c", "cab"); +// tst_InsertAt_str("ab", 0, "cdefghij", "cdefghijab"); +// } +// void tst_InsertAt_str(String orig, int insertAt, String insertStr, String expd) { +// bb = Bry_bfr_.New(16); +// bb.Add_str(orig); +// bb.InsertAt_str(insertAt, insertStr); +// String actl = bb.To_str_and_clear(); +// Tfds.Eq(expd, actl); +// } + @Test public void To_bry_and_clear_and_trim() { + tst_XtoAryAndClearAndTrim("a" , "a"); + tst_XtoAryAndClearAndTrim(" a " , "a"); + tst_XtoAryAndClearAndTrim(" a b " , "a b"); + tst_XtoAryAndClearAndTrim(" " , ""); + } + void tst_XtoAryAndClearAndTrim(String raw, String expd) { + bb.Add_str_u8(raw); + Tfds.Eq(expd, String_.new_u8(bb.To_bry_and_clear_and_trim())); + } + @Test public void XtoInt() { + tst_XtoInt("123", 123); + tst_XtoInt("a", Int_.Min_value); + tst_XtoInt("9999999999", Int_.Min_value); + } + void tst_XtoInt(String raw, int expd) { + bb.Add_str_u8(raw); + Tfds.Eq(expd, bb.To_int_and_clear(Int_.Min_value)); + } + static long Pow38_to(int year, int month, int day, int hour, int minute, int second, int frac) { + return ((long)year) << 26 + | ((long)month & 0x0f) << 22 // 16 + | ((long)day & 0x1f) << 17 // 32 + | ((long)hour & 0x1f) << 12 // 32 + | ((long)minute & 0x3f) << 6 // 64 + | ((long)second & 0x3f) // 64 + ; + } + static DateAdp Pow38_by(long v) { + int year = (int) (v >> 26); + int month = (int)((v >> 22) & 0x0f); + int day = (int)((v >> 17) & 0x1f); + int hour = (int)((v >> 12) & 0x1f); + int minute = (int)((v >> 6) & 0x3f); + int second = (int)((v ) & 0x3f); + return DateAdp_.new_(year, month, day, hour, minute, second, 0); + } + @Test public void Add_bfr_trimEnd_and_clear() { + tst_Add_bfr_trimEnd_and_clear("a ", "a"); + } + void tst_Add_bfr_trimEnd_and_clear(String raw, String expd) { + Bry_bfr tmp = Bry_bfr_.New().Add_str_u8(raw); + Tfds.Eq(expd, bb.Add_bfr_trim_and_clear(tmp, false, true).To_str_and_clear()); + } + @Test public void Add_bfr_trimAll_and_clear() { + tst_Add_bfr_trimAll_and_clear(" a ", "a"); + tst_Add_bfr_trimAll_and_clear(" a b ", "a b"); + tst_Add_bfr_trimAll_and_clear("a", "a"); + tst_Add_bfr_trimAll_and_clear("", ""); + } + void tst_Add_bfr_trimAll_and_clear(String raw, String expd) { + Bry_bfr tmp = Bry_bfr_.New().Add_str_u8(raw); + Tfds.Eq(expd, bb.Add_bfr_trim_and_clear(tmp, true, true).To_str_and_clear()); + } + @Test public void Add_int_pad_bgn() { + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 0, "000"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 1, "001"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 10, "010"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 100, "100"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 1000, "1000"); + } + @Test public void Add_bry_escape() { + fxt.Test_Add_bry_escape("abc" , "abc"); // nothing to escape + fxt.Test_Add_bry_escape("a'bc" , "a''bc"); // single escape (code handles first quote differently) + fxt.Test_Add_bry_escape("a'b'c" , "a''b''c"); // double escape (code handles subsequent quotes different than first) + } + @Test public void Insert_at() { + fxt.Test_Insert_at("abcd", 0, "xyz" , "xyzabcd"); // bgn + fxt.Test_Insert_at("abcd", 4, "xyz" , "abcdxyz"); // end + fxt.Test_Insert_at("abcd", 2, "xyz" , "abxyzcd"); // mid + fxt.Test_Insert_at("abcd", 2, "xyz", 1, 2 , "abycd"); // mid + } + @Test public void Delete_rng() { + fxt.Test_Delete_rng("abcd", 0, 2 , "cd"); // bgn + fxt.Test_Delete_rng("abcd", 2, 4 , "ab"); // end + fxt.Test_Delete_rng("abcd", 1, 3 , "ad"); // mid + } + @Test public void Delete_rng_to_bgn() { + fxt.Test_Delete_rng_to_bgn("abcd", 2 , "cd"); + } + @Test public void Delete_rng_to_end() { + fxt.Test_Delete_rng_to_end("abcd", 2 , "ab"); + } + @Test public void To_bry_ary_and_clear() { + fxt.Test__to_bry_ary_and_clear("" ); // empty + fxt.Test__to_bry_ary_and_clear("a" , "a"); // lines=1 + fxt.Test__to_bry_ary_and_clear("a\nb\nc" , "a", "b", "c"); // lines=n + fxt.Test__to_bry_ary_and_clear("a\n" , "a"); // nl at end + } +} +class ByteAryBfr_fxt { + private final Bry_bfr bfr = Bry_bfr_.Reset(16); + public void Clear() { + bfr.ClearAndReset(); + } + public void Test_Add_int_pad_bgn(byte pad_byte, int str_len, int val, String expd) {Tfds.Eq(expd, bfr.Add_int_pad_bgn(pad_byte, str_len, val).To_str_and_clear());} + public void Test_Add_bry_escape(String val, String expd) { + byte[] val_bry = Bry_.new_u8(val); + Tfds.Eq(expd, bfr.Add_bry_escape(Byte_ascii.Apos, Byte_.Ary(Byte_ascii.Apos, Byte_ascii.Apos), val_bry, 0, val_bry.length).To_str_and_clear()); + } + public void Test_Insert_at(String init, int pos, String val, String expd) {Tfds.Eq(expd, bfr.Add_str_u8(init).Insert_at(pos, Bry_.new_u8(val)).To_str_and_clear());} + public void Test_Insert_at(String init, int pos, String val, int val_bgn, int val_end, String expd) {Tfds.Eq(expd, bfr.Add_str_u8(init).Insert_at(pos, Bry_.new_u8(val), val_bgn, val_end).To_str_and_clear());} + public void Test_Delete_rng(String init, int bgn, int end, String expd) {Tfds.Eq(expd, bfr.Add_str_u8(init).Delete_rng(bgn, end).To_str_and_clear());} + public void Test_Delete_rng_to_bgn(String init, int pos, String expd) {Tfds.Eq(expd, bfr.Add_str_u8(init).Delete_rng_to_bgn(pos).To_str_and_clear());} + public void Test_Delete_rng_to_end(String init, int pos, String expd) {Tfds.Eq(expd, bfr.Add_str_u8(init).Delete_rng_to_end(pos).To_str_and_clear());} + public void Test__to_bry_ary_and_clear(String bfr_str, String... expd) { + Tfds.Eq_ary(expd, String_.Ary(bfr.Add_str_u8(bfr_str).To_bry_ary_and_clear())); + } +} diff --git a/100_core/src/gplx/Bry_find_.java b/100_core/src/gplx/Bry_find_.java new file mode 100644 index 000000000..377b1a617 --- /dev/null +++ b/100_core/src/gplx/Bry_find_.java @@ -0,0 +1,365 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Bry_find_ { + public static final int Not_found = -1; + public static int Find_fwd(byte[] src, byte lkp) {return Find_fwd(src, lkp, 0, src.length);} + public static int Find_fwd(byte[] src, byte lkp, int cur) {return Find_fwd(src, lkp, cur, src.length);} + public static int Find_fwd(byte[] src, byte lkp, int cur, int end) { + for (int i = cur; i < end; i++) + if (src[i] == lkp) return i; + return Bry_find_.Not_found; + } + public static int Find_fwd_or(byte[] src, byte lkp, int cur, int end, int or) { + int rv = Find_fwd(src, lkp, cur, end); + return rv == Bry_find_.Not_found ? or : rv; + } + public static int Find_fwd_or(byte[] src, byte[] lkp, int cur, int end, int or) { + int rv = Find_fwd(src, lkp, cur, end); + return rv == Bry_find_.Not_found ? or : rv; + } + public static int Find_bwd(byte[] src, byte lkp) {return Find_bwd(src, lkp, src.length, 0);} + public static int Find_bwd(byte[] src, byte lkp, int cur) {return Find_bwd(src, lkp, cur , 0);} + public static int Find_bwd(byte[] src, byte lkp, int cur, int end) { + --cur; // always subtract 1 from cur; allows passing in src_len or cur_pos without forcing caller to subtract - 1; DATE:2014-02-11 + --end; + for (int i = cur; i > end; i--) + if (src[i] == lkp) return i; + return Bry_find_.Not_found; + } + public static int Move_fwd(byte[] src, byte lkp, int cur, int end) { + int rv = Find_fwd(src, lkp, cur, src.length); + return rv == Bry_find_.Not_found ? rv : rv + 1; + } + public static int Move_fwd(byte[] src, byte[] lkp, int cur) {return Move_fwd(src, lkp, cur, src.length);} + public static int Move_fwd(byte[] src, byte[] lkp, int cur, int end) { + int rv = Find_fwd(src, lkp, cur, src.length); + return rv == Bry_find_.Not_found ? rv : rv + lkp.length; + } + public static int Find_fwd(byte[] src, byte[] lkp) {return Find(src, lkp, 0 , src.length, true);} + public static int Find_fwd(byte[] src, byte[] lkp, int cur) {return Find(src, lkp, cur , src.length, true);} + public static int Find_fwd(byte[] src, byte[] lkp, int cur, int end) {return Find(src, lkp, cur , end, true);} + public static int Find(byte[] src, byte[] lkp, int src_bgn, int src_end, boolean fwd) { + if (src_bgn < 0 || src.length == 0) return Bry_find_.Not_found; + int dif, lkp_len = lkp.length, lkp_bgn, lkp_end, src_end_chk; + if (fwd) { + if (src_bgn > src_end) return Bry_find_.Not_found; + dif = 1; lkp_bgn = 0; lkp_end = lkp_len; src_end_chk = src_end - CompareAble_.OffsetCompare; + } + else { + if (src_bgn < src_end) return Bry_find_.Not_found; + dif = -1; lkp_bgn = lkp_len - 1; lkp_end = -1; src_end_chk = src.length - CompareAble_.OffsetCompare; // src_end_chk needed when going bwd, b/c lkp_len may be > 1 + } + while (src_bgn != src_end) { // while src is not done; + int lkp_cur = lkp_bgn; + while (lkp_cur != lkp_end) { // while lkp is not done + int pos = src_bgn + lkp_cur; + if ( pos > src_end_chk // outside bounds; occurs when lkp_len > 1 + || src[pos] != lkp[lkp_cur]) // srcByte doesn't match lkpByte + break; + else + lkp_cur += dif; + } + if (lkp_cur == lkp_end) return src_bgn; // lkp matches src; exit + src_bgn += dif; + } + return Bry_find_.Not_found; + } + public static int Find_bwd(byte[] src, byte[] lkp, int cur) {return Find_bwd(src, lkp, cur , 0);} + public static int Find_bwd(byte[] src, byte[] lkp, int cur, int end) { + if (cur < 1) return Bry_find_.Not_found; + --cur; // always subtract 1 from cur; allows passing in src_len or cur_pos without forcing caller to subtract - 1; DATE:2014-02-11 + --end; + int src_len = src.length; + int lkp_len = lkp.length; + for (int i = cur; i > end; i--) { + if (i + lkp_len > src_len) continue; // lkp too small for pos; EX: src=abcde; lkp=bcd; pos=4 + boolean match = true; + for (int j = 0; j < lkp_len; j++) { + if (lkp[j] != src[i + j]) { + match = false; + break; + } + } + if (match) return i; + } + return Bry_find_.Not_found; + } + public static int Find_bwd_last_ws(byte[] src, int cur) { + if (cur < 1) return Bry_find_.Not_found; + --cur; + int rv = Bry_find_.Not_found; + for (int i = cur; i > -1; i--) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + rv = i; + break; + default: + i = -1; + break; + } + } + return rv; + } + public static int Find_bwd_ws(byte[] src, int cur, int end) { + for (int i = cur; i > -1; --i) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + return i; + } + } + return Bry_find_.Not_found; + } + public static int Find_fwd_last_ws(byte[] src, int cur) { + int end = src.length; + if (cur >= end) return Bry_find_.Not_found; + int rv = Bry_find_.Not_found; + for (int i = cur; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + rv = i; + break; + default: + i = -1; + break; + } + } + return rv; + } + public static int Find_bwd_non_ws_or_not_found(byte[] src, int cur, int end) { // get pos of 1st char that is not ws; + if (cur >= src.length) return Bry_find_.Not_found; + for (int i = cur; i >= end; i--) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + break; + default: + return i; + } + } + return Bry_find_.Not_found; + } + public static int Find_bwd_non_ws_or_end(byte[] src, int cur, int end) { + if (cur >= src.length) return Bry_find_.Not_found; + for (int i = cur; i >= end; i--) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + break; + default: + return i; + } + } + return end; + } + public static int Find_bwd__skip_ws(byte[] src, int end, int bgn) { + int src_len = src.length; + if (end == src_len) return end; + if (end > src_len || end < 0) return Bry_find_.Not_found; + int pos = end - 1; // start from end - 1; handles situations where len is passed in + for (int i = pos; i >= bgn; --i) { + switch (src[i]) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + break; + default: + return i + 1; + } + } + return bgn; + } + public static int Find_bwd__skip(byte[] src, int end, int bgn, byte skip) { + int src_len = src.length; // if (end == src_len) return end; + if (end > src_len || end < 0) return Bry_find_.Not_found; + int pos = end - 1; // start from end - 1; handles situations where len is passed in + for (int i = pos; i >= bgn; --i) { + if (src[i] != skip) + return i + 1; + } + return bgn; + } + public static int Find_bwd__skip(byte[] src, int end, int bgn, byte[] skip) { + int src_len = src.length; + if (end > src_len || end < 0) return Bry_find_.Not_found; + int skip_len = skip.length; + int pos = end - skip_len; // start from end - 1; handles situations where len is passed in + for (int i = pos; i >= bgn; --i) { + if (!Bry_.Eq(src, i, i + skip_len, skip)) + return i + skip_len; + } + return bgn; + } + public static int Find_bwd_while(byte[] src, int cur, int end, byte while_byte) { + --cur; + while (true) { + if ( cur < end + || src[cur] != while_byte) return cur; + --cur; + } + } + public static int Find_fwd_while(byte[] src, int cur, int end, byte while_byte) { + while (true) { + if ( cur == end + || src[cur] != while_byte) return cur; + cur++; + } + } + public static int Find_fwd_until(byte[] src, int cur, int end, byte until_byte) { + while (true) { + if ( cur == end + || src[cur] == until_byte) return cur; + cur++; + } + } + public static int Find_fwd_until_space_or_tab(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return Bry_find_.Not_found; + switch (src[cur]) { + case Byte_ascii.Space: case Byte_ascii.Tab: + return cur; + default: + ++cur; + break; + } + } + } + public static int Find_fwd_until_ws(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return Bry_find_.Not_found; + switch (src[cur]) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + return cur; + default: + ++cur; + break; + } + } + } + public static int Find_fwd_while_space_or_tab(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return cur; + switch (src[cur]) { + case Byte_ascii.Space: case Byte_ascii.Tab: ++cur; break; + default: return cur; + } + } + } + public static int Trim_fwd_space_tab(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return cur; + switch (src[cur]) { + case Byte_ascii.Space: case Byte_ascii.Tab: ++cur; break; + default: return cur; + } + } + } + public static int Trim_bwd_space_tab(byte[] src, int cur, int bgn) { + while (true) { + int prv_cur = cur - 1; // check byte before cur; EX: "a b " will have len of 4, and pass cur=4; + if (prv_cur < bgn) return cur; // checking byte before prv; exit; + switch (src[prv_cur]) { + case Byte_ascii.Space: case Byte_ascii.Tab: --cur; break; + default: return cur; + } + } + } + public static int Find_fwd_while_ws(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return cur; + try { + switch (src[cur]) { + case Byte_ascii.Nl: case Byte_ascii.Cr: + case Byte_ascii.Space: case Byte_ascii.Tab: ++cur; break; + default: return cur; + } + } catch (Exception e) {throw Err_.new_exc(e, "core", "idx is invalid", "cur", cur, "src", src);} + } + } + public static int Find_fwd_while_letter(byte[] src, int cur, int end) { + while (cur < end) { + switch (src[cur]) { + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + break; + default: + return cur; + } + ++cur; + } + return cur; + } + public static int Find_fwd_while_num(byte[] src) {return Find_fwd_while_num(src, 0, src.length);} + public static int Find_fwd_while_num(byte[] src, int cur, int end) { + while (cur < end) { + if (!Byte_ascii.Is_num(src[cur])) + return cur; + ++cur; + } + return cur; + } + public static int Find_fwd_while_not_ws(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return cur; + switch (src[cur]) { + case Byte_ascii.Space: + case Byte_ascii.Nl: + case Byte_ascii.Tab: + case Byte_ascii.Cr: + ++cur; + break; + default: + return cur; + } + } + } + public static int Find_bwd_while_alphanum(byte[] src, int cur) {return Find_bwd_while_alphanum(src, cur, -1);} + public static int Find_bwd_while_alphanum(byte[] src, int cur, int end) { + --cur; + while (cur > end) { + switch (src[cur]) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + --cur; + break; + default: + return cur; + } + } + return 0; // always return a valid index + } +} diff --git a/100_core/src/gplx/Bry_find__tst.java b/100_core/src/gplx/Bry_find__tst.java new file mode 100644 index 000000000..ee58f8dd5 --- /dev/null +++ b/100_core/src/gplx/Bry_find__tst.java @@ -0,0 +1,77 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Bry_find__tst { + private Bry_find__fxt fxt = new Bry_find__fxt(); + @Test public void Find_fwd() { + fxt.Test_Find_fwd("abcba", "b", 0, 1); + fxt.Test_Find_fwd("abcba", "z", 0, -1); + fxt.Test_Find_fwd("abcba", "b", 1, 1); + fxt.Test_Find_fwd("abcba", "b", 2, 3); + fxt.Test_Find_fwd("abcba", "b", 4, -1); + fxt.Test_Find_fwd("abcba", "zb", 4, -1); + fxt.Test_Find_fwd("abcba", "a", 6, -1); + } + @Test public void Find_bwd() { + fxt.Test_Find_bwd("abcba", "b", 4, 3); + fxt.Test_Find_bwd("abcba", "z", 4, -1); + fxt.Test_Find_bwd("abcba", "b", 3, 1); + fxt.Test_Find_bwd("abcba", "b", 2, 1); + fxt.Test_Find_bwd("abcba", "b", 0, -1); + fxt.Test_Find_bwd("abcba", "zb", 4, -1); + fxt.Test_Find_fwd("abcba", "a", -1, -1); + fxt.Test_Find_bwd("abcba", "ab", 4, 0); + } + @Test public void Find_bwd_last_ws() { + fxt.Test_Find_bwd_1st_ws_tst("a b" , 2, 1); // basic + fxt.Test_Find_bwd_1st_ws_tst("a b" , 3, 1); // multiple + fxt.Test_Find_bwd_1st_ws_tst("ab" , 1, Bry_find_.Not_found); // none + } + @Test public void Trim_fwd_space_tab() { + fxt.Test_Trim_fwd_space_tab(" a b" , 1); + fxt.Test_Trim_fwd_space_tab("\ta b" , 1); + fxt.Test_Trim_fwd_space_tab(" \ta b" , 2); + fxt.Test_Trim_fwd_space_tab("a bc" , 0); + fxt.Test_Trim_fwd_space_tab("" , 0); + fxt.Test_Trim_fwd_space_tab(" \t" , 2); + } + @Test public void Trim_bwd_space_tab() { + fxt.Test_Trim_bwd_space_tab("a b " , 3); + fxt.Test_Trim_bwd_space_tab("a b\t" , 3); + fxt.Test_Trim_bwd_space_tab("a b\t " , 3); + fxt.Test_Trim_bwd_space_tab("a bc" , 4); + fxt.Test_Trim_bwd_space_tab("" , 0); + fxt.Test_Trim_bwd_space_tab(" \t" , 0); + } +} +class Bry_find__fxt { + public void Test_Find_fwd(String src, String lkp, int bgn, int expd) {Tfds.Eq(expd, Bry_find_.Find_fwd(Bry_.new_u8(src), Bry_.new_u8(lkp), bgn));} + public void Test_Find_bwd(String src, String lkp, int bgn, int expd) {Tfds.Eq(expd, Bry_find_.Find_bwd(Bry_.new_u8(src), Bry_.new_u8(lkp), bgn));} + public void Test_Find_bwd_1st_ws_tst(String src, int pos, int expd) {Tfds.Eq(expd, Bry_find_.Find_bwd_last_ws(Bry_.new_a7(src), pos));} + public void Test_Trim_bwd_space_tab(String raw_str, int expd) { + byte[] raw_bry = Bry_.new_u8(raw_str); + int actl = Bry_find_.Trim_bwd_space_tab(raw_bry, raw_bry.length, 0); + Tfds.Eq(expd, actl, raw_str); + } + public void Test_Trim_fwd_space_tab(String raw_str, int expd) { + byte[] raw_bry = Bry_.new_u8(raw_str); + int actl = Bry_find_.Trim_fwd_space_tab(raw_bry, 0, raw_bry.length); + Tfds.Eq(expd, actl, raw_str); + } +} diff --git a/100_core/src/gplx/Bry_fmt.java b/100_core/src/gplx/Bry_fmt.java new file mode 100644 index 000000000..6f1fae277 --- /dev/null +++ b/100_core/src/gplx/Bry_fmt.java @@ -0,0 +1,88 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.brys.*; import gplx.core.brys.fmts.*; +public class Bry_fmt { + private final Object thread_lock = new Object(); + private byte[] src; + private Bry_fmt_itm[] itms; private int itms_len; + private Bfr_fmt_arg[] args = Bfr_fmt_arg.Ary_empty; + private byte[][] keys = Bry_.Ary_empty; + private Object[] vals = null; + private boolean dirty; + public Bry_fmt(byte[] src, byte[][] keys, Bfr_fmt_arg[] args) { + dirty = true; + this.src = src; this.keys = keys; this.args = args; + } + public byte[] Fmt() {return src;} + public Bry_fmt Fmt_(String v) {dirty = true; src = Bry_.new_u8(v); return this;} + public Bry_fmt Args_(Bfr_fmt_arg... v) {dirty = true; args = v; return this;} + public Bry_fmt Keys_(String... v) {return Keys_(Bry_.Ary(v));} + public Bry_fmt Keys_(byte[]... v) {dirty = true; keys = v; return this;} + public Bry_fmt Vals_(Object... v) {vals = v; return this;} + public String Bld_many_to_str_auto_bfr(Object... vals_ary) { + Bry_bfr bfr = Bry_bfr_.Get(); + try {return Bld_many_to_str(bfr, vals_ary);} + finally {bfr.Mkr_rls();} + } + public String Bld_many_to_str(Bry_bfr bfr, Object... vals_ary) { + Bld_many(bfr, vals_ary); + return bfr.To_str_and_clear(); + } + public byte[] Bld_many_to_bry(Bry_bfr bfr, Object... vals_ary) { + Bld_many(bfr, vals_ary); + return bfr.To_bry_and_clear(); + } + public void Bld_many(Bry_bfr bfr, Object... vals_ary) { + if (dirty) Compile(); + int vals_len = vals_ary.length; + for (int i = 0; i < itms_len; ++i) { + Bry_fmt_itm itm = itms[i]; + switch (itm.Tid) { + case Bry_fmt_itm.Tid__txt: bfr.Add_mid(src, itm.Src_bgn, itm.Src_end); break; + case Bry_fmt_itm.Tid__arg: itm.Arg.Bfr_arg__add(bfr);break; + case Bry_fmt_itm.Tid__key: + int idx = itm.Key_idx; + if (idx > -1 && idx < vals_len) + bfr.Add_obj(vals_ary[idx]); + else + bfr.Add_mid(src, itm.Src_bgn, itm.Src_end); + break; + default: throw Err_.new_unhandled(itm.Tid); + } + } + } + public String To_str() {return Bld_many_to_str_auto_bfr(vals);} + private void Compile() { + synchronized (thread_lock) { + dirty = false; + this.itms = Bry_fmt_parser_.Parse(Byte_ascii.Tilde, Byte_ascii.Curly_bgn, Byte_ascii.Curly_end, args, keys, src); + this.itms_len = itms.length; + } + } + public static Bry_fmt New(String fmt, String... keys) {return new Bry_fmt(Bry_.new_u8(fmt), Bry_.Ary(keys), Bfr_fmt_arg.Ary_empty);} + public static Bry_fmt New(byte[] fmt, String... keys) {return new Bry_fmt(fmt , Bry_.Ary(keys), Bfr_fmt_arg.Ary_empty);} + public static String Make_str(String fmt_str, Object... vals) {return Auto(fmt_str).Vals_(vals).To_str();} + public static Bry_fmt Auto_nl_apos(String... lines) {return Auto(Bry_.New_u8_nl_apos(lines));} + public static Bry_fmt Auto_nl_skip_last(String... lines) {return Auto(Bry_.new_u8(String_.Concat_lines_nl_skip_last(lines)));} + public static Bry_fmt Auto(String fmt_str) {return Auto(Bry_.new_u8(fmt_str));} + public static Bry_fmt Auto(byte[] fmt_bry) { + byte[][] keys_bry = Bry_fmt_parser_.Parse_keys(fmt_bry); + return new Bry_fmt(fmt_bry, keys_bry, Bfr_fmt_arg.Ary_empty); + } +} diff --git a/100_core/src/gplx/Bry_split_.java b/100_core/src/gplx/Bry_split_.java new file mode 100644 index 000000000..c5e629af0 --- /dev/null +++ b/100_core/src/gplx/Bry_split_.java @@ -0,0 +1,136 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.brys.*; +public class Bry_split_ { + private static final Object thread_lock = new Object(); + public static byte[][] Split(byte[] src, byte dlm) {return Split(src, dlm, false);} + public static byte[][] Split(byte[] src, byte dlm, boolean trim) {return src == null ? Bry_.Ary_empty : Split(src, 0, src.length, dlm, trim);} + public static byte[][] Split(byte[] src, int bgn, int end, byte dlm, boolean trim) { + synchronized (thread_lock) { + Bry_split_wkr__to_ary wkr = Bry_split_wkr__to_ary.Instance; + Split(src, bgn, end, dlm, trim, wkr); + return wkr.To_ary(); + } + } + public static int Split(byte[] src, int src_bgn, int src_end, byte dlm, boolean trim, Bry_split_wkr wkr) { + if (src == null || src_end - src_bgn < 1) return 0; + int pos = src_bgn; + int itm_bgn = -1, itm_end = -1; + int count = 0; + while (true) { + boolean pos_is_last = pos == src_end; + byte b = pos_is_last ? dlm : src[pos]; + int nxt_pos = pos + 1; + boolean process = true; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: // ignore ws; assumes that flags have no ws (they are single char) and vnts have no ws (EX: zh-hans) + if (trim && b != dlm) process = false; // b != dlm handles cases where ws is dlm, but trim is enabled; EX: " a \n b" -> "a", "b" + break; + } + if (process) { + if (b == dlm) { + boolean reset = true; + if (itm_bgn == -1) { + if (pos_is_last) {} // skip dlm at bgn / end; EX: "a," + else {wkr.Split(src, itm_bgn, itm_end);} // else, process "empty" dlm; EX: ",a" + } + else { + int rv = wkr.Split(src, itm_bgn, itm_end); + switch (rv) { + case Rv__ok: ++count; break; + case Rv__extend: reset = false; break; + case Rv__cancel: return count; + default: throw Err_.new_unhandled(rv); + } + } + if (reset) itm_bgn = itm_end = -1; + } + else { + if (itm_bgn == -1) itm_bgn = pos; + itm_end = nxt_pos; + } + } + if (pos_is_last) break; + pos = nxt_pos; + } + return count; + } + public static byte[][] Split(byte[] src, byte[] dlm) {return Split(src, 0, src.length, dlm);} + public static byte[][] Split(byte[] src, int src_bgn, int src_end, byte[] dlm) { + if (src == null) return Bry_.Ary_empty; + int src_len = src.length; + if (src_len == 0) return Bry_.Ary_empty; + int cur_pos = src_bgn, dlm_len = dlm.length; + List_adp rv = List_adp_.New(); + while (true) { + int find_pos = Bry_find_.Find_fwd(src, dlm, cur_pos); + if (find_pos == Bry_find_.Not_found) { + if (cur_pos >= src_end) break; // dlm is last sequence in src; do not create empty itm + find_pos = src_end; + } + rv.Add(Bry_.Mid(src, cur_pos, find_pos)); + cur_pos = find_pos + dlm_len; + if (cur_pos >= src_end) break; + } + return (byte[][])rv.To_ary(byte[].class); + } + public static byte[][] Split_lines(byte[] src) { + if (Bry_.Len_eq_0(src)) return Bry_.Ary_empty; + int src_len = src.length, src_pos = 0, fld_bgn = 0; + List_adp rv = List_adp_.New(); + while (true) { + boolean last = src_pos == src_len; + byte b = last ? Byte_ascii.Nl : src[src_pos]; + int nxt_bgn = src_pos + 1; + switch (b) { + case Byte_ascii.Cr: + case Byte_ascii.Nl: + if ( b == Byte_ascii.Cr // check for crlf + && nxt_bgn < src_len && src[nxt_bgn] == Byte_ascii.Nl) { + ++nxt_bgn; + } + if (last && (src_pos - fld_bgn == 0)) {} // ignore trailing itms + else + rv.Add(Bry_.Mid(src, fld_bgn, src_pos)); + fld_bgn = nxt_bgn; + break; + } + if (last) break; + src_pos = nxt_bgn; + } + return (byte[][])rv.To_ary(byte[].class); + } + public static final int Rv__ok = 0, Rv__extend = 1, Rv__cancel = 2; +} +class Bry_split_wkr__to_ary implements gplx.core.brys.Bry_split_wkr { + private final List_adp list = List_adp_.New(); + public int Split(byte[] src, int itm_bgn, int itm_end) { + synchronized (list) { + byte[] bry = itm_end == itm_bgn ? Bry_.Empty : Bry_.Mid(src, itm_bgn, itm_end); + list.Add(bry); + return Bry_split_.Rv__ok; + } + } + public byte[][] To_ary() { + synchronized (list) { + return (byte[][])list.To_ary_and_clear(byte[].class); + } + } + public static final Bry_split_wkr__to_ary Instance = new Bry_split_wkr__to_ary(); Bry_split_wkr__to_ary() {} +} diff --git a/100_core/src/gplx/Bry_split__tst.java b/100_core/src/gplx/Bry_split__tst.java new file mode 100644 index 000000000..68d677da8 --- /dev/null +++ b/100_core/src/gplx/Bry_split__tst.java @@ -0,0 +1,71 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Bry_split__tst { + private final Bry_split__fxt fxt = new Bry_split__fxt(); + @Test public void Split() { + fxt.Test_split("a" , Byte_ascii.Pipe, Bool_.N, "a"); // no trim + fxt.Test_split("a|" , Byte_ascii.Pipe, Bool_.N, "a"); + fxt.Test_split("|a" , Byte_ascii.Pipe, Bool_.N, "", "a"); + fxt.Test_split("|" , Byte_ascii.Pipe, Bool_.N, ""); + fxt.Test_split("" , Byte_ascii.Pipe, Bool_.N); + fxt.Test_split("a|b|c" , Byte_ascii.Pipe, Bool_.N, "a", "b", "c"); + fxt.Test_split(" a " , Byte_ascii.Pipe, Bool_.Y, "a"); // trim + fxt.Test_split(" a |" , Byte_ascii.Pipe, Bool_.Y, "a"); + fxt.Test_split("| a " , Byte_ascii.Pipe, Bool_.Y, "", "a"); + fxt.Test_split(" | " , Byte_ascii.Pipe, Bool_.Y, ""); + fxt.Test_split(" " , Byte_ascii.Pipe, Bool_.Y); + fxt.Test_split(" a | b | c " , Byte_ascii.Pipe, Bool_.Y, "a", "b", "c"); + fxt.Test_split(" a b | c d " , Byte_ascii.Pipe, Bool_.Y, "a b", "c d"); + fxt.Test_split(" a \n b " , Byte_ascii.Nl , Bool_.N, " a ", " b "); // ws as dlm + fxt.Test_split(" a \n b " , Byte_ascii.Nl , Bool_.Y, "a", "b"); // ws as dlm; trim + fxt.Test_split("a|extend|b" , Byte_ascii.Pipe, Bool_.Y, "a", "extend|b"); // extend + fxt.Test_split("extend|a" , Byte_ascii.Pipe, Bool_.Y, "extend|a"); // extend + fxt.Test_split("a|cancel|b" , Byte_ascii.Pipe, Bool_.Y, "a"); // cancel + } + @Test public void Split__bry() { + fxt.Test_split("a|b|c|d" , 2, 6, "|", "b", "c"); + fxt.Test_split("a|b|c|d" , 2, 4, "|", "b"); + } +} +class Bry_split__fxt { + private final Bry_split_wkr__example wkr = new Bry_split_wkr__example(); + public void Test_split(String raw_str, byte dlm, boolean trim, String... expd) { + byte[] src = Bry_.new_a7(raw_str); + Bry_split_.Split(src, 0, src.length, dlm, trim, wkr); + byte[][] actl_ary = wkr.To_ary(); + Tfds.Eq_ary_str(expd, String_.Ary(actl_ary)); + } + public void Test_split(String src, int src_bgn, int src_end, String dlm, String... expd) { + Tfds.Eq_ary_str(Bry_.Ary(expd), Bry_split_.Split(Bry_.new_u8(src), src_bgn, src_end, Bry_.new_u8(dlm))); + } +} +class Bry_split_wkr__example implements gplx.core.brys.Bry_split_wkr { + private final List_adp list = List_adp_.New(); + public int Split(byte[] src, int itm_bgn, int itm_end) { + byte[] bry = itm_end == itm_bgn ? Bry_.Empty : Bry_.Mid(src, itm_bgn, itm_end); + if (Bry_.Eq(bry, Bry_.new_a7("extend"))) return Bry_split_.Rv__extend; + else if (Bry_.Eq(bry, Bry_.new_a7("cancel"))) return Bry_split_.Rv__cancel; + list.Add(bry); + return Bry_split_.Rv__ok; + } + public byte[][] To_ary() { + return (byte[][])list.To_ary_and_clear(byte[].class); + } +} diff --git a/100_core/src/gplx/Byte_.java b/100_core/src/gplx/Byte_.java new file mode 100644 index 000000000..02d07b6e1 --- /dev/null +++ b/100_core/src/gplx/Byte_.java @@ -0,0 +1,63 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Byte_ { + public static final String Cls_val_name = "byte"; + public static final Class Cls_ref_type = Byte.class; + public static final byte + Zero = 0 + , Min_value = Byte.MIN_VALUE + , Max_value_127 = 127 + , Val_128 = -128 + , Val_255 = -1 + ; + public static byte cast(Object o) {try {return (Byte)o;} catch (Exception e) {throw Err_.new_type_mismatch_w_exc(e, byte.class, o);}} + public static byte parse(String raw) {return Byte.parseByte(raw);} + public static byte parse_or(String raw, byte or) { + if (raw == null) return or; + try {return parse(raw);} + catch (Exception e) {Err_.Noop(e); return or;} + } + public static byte By_int(int v) {return v > 127 ? (byte)(v - 256) : (byte)v;} // PERF?: (byte)(v & 0xff) + public static int To_int(byte v) {return v < 0 ? (int)v + 256 : v;} + public static String To_str(byte v) {return new Byte(v).toString();} + public static byte[] To_bry(byte v) {return new byte[] {v};} + public static boolean In(byte v, byte... ary) { + for (byte itm : ary) + if (v == itm) return true; + return false; + } + public static boolean Eq_many(byte v, byte... ary) { + for (byte itm : ary) + if (v != itm) return false; + return true; + } + public static int Compare(byte lhs, byte rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } + public static byte[] Ary(byte... ary) {return ary;} + public static byte[] Ary_by_ints(int... ary) { + int ary_len = ary.length; + byte[] rv = new byte[ary_len]; + for (int i = 0; i < ary_len; i++) + rv[i] = By_int(ary[i]); + return rv; + } +} diff --git a/100_core/src/gplx/Byte__tst.java b/100_core/src/gplx/Byte__tst.java new file mode 100644 index 000000000..3e78dbb9b --- /dev/null +++ b/100_core/src/gplx/Byte__tst.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Byte__tst { + @Test public void int_() { + tst_int_( 0, 0); + tst_int_( 127, 127); + tst_int_( 128, 128); // NOTE: JAVA defines byte as -128 -> 127 + tst_int_( 255, 255); + tst_int_( 256, 0); // NOTE: 256 will cast to 1; (byte)256 works same in both JAVA/.NET + } void tst_int_(int v, int expd) {Tfds.Eq((byte)expd, Byte_.By_int(v));} // WORKAROUND/JAVA: expd is of type int b/c java promotes numbers to ints + @Test public void To_int() { + tst_XtoInt( 0, 0); + tst_XtoInt( 127, 127); + tst_XtoInt( 128, 128); + tst_XtoInt( 255, 255); + tst_XtoInt( 256, 0); + } void tst_XtoInt(int v, int expd) {Tfds.Eq(expd, Byte_.To_int((byte)v));} // WORKAROUND/JAVA: v is of type int b/c java promotes numbers to ints +} diff --git a/100_core/src/gplx/Byte_ascii.java b/100_core/src/gplx/Byte_ascii.java new file mode 100644 index 000000000..595038573 --- /dev/null +++ b/100_core/src/gplx/Byte_ascii.java @@ -0,0 +1,129 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Byte_ascii { + public static final byte + Null = 0 , Backfeed = 8, Tab = 9 + , Nl = 10, Formfeed = 12, Cr = 13 + , Escape = 27 + , Space = 32, Bang = 33, Quote = 34 + , Hash = 35, Dollar = 36, Percent = 37, Amp = 38, Apos = 39 + , Paren_bgn = 40, Paren_end = 41, Star = 42, Plus = 43, Comma = 44 + , Dash = 45, Dot = 46, Slash = 47, Num_0 = 48, Num_1 = 49 + , Num_2 = 50, Num_3 = 51, Num_4 = 52, Num_5 = 53, Num_6 = 54 + , Num_7 = 55, Num_8 = 56, Num_9 = 57, Colon = 58, Semic = 59 + , Lt = 60, Eq = 61, Gt = 62, Question = 63, At = 64 + , Ltr_A = 65, Ltr_B = 66, Ltr_C = 67, Ltr_D = 68, Ltr_E = 69 + , Ltr_F = 70, Ltr_G = 71, Ltr_H = 72, Ltr_I = 73, Ltr_J = 74 + , Ltr_K = 75, Ltr_L = 76, Ltr_M = 77, Ltr_N = 78, Ltr_O = 79 + , Ltr_P = 80, Ltr_Q = 81, Ltr_R = 82, Ltr_S = 83, Ltr_T = 84 + , Ltr_U = 85, Ltr_V = 86, Ltr_W = 87, Ltr_X = 88, Ltr_Y = 89 + , Ltr_Z = 90, Brack_bgn = 91, Backslash = 92, Brack_end = 93, Pow = 94 // Circumflex + , Underline = 95, Tick = 96, Ltr_a = 97, Ltr_b = 98, Ltr_c = 99 + , Ltr_d = 100, Ltr_e = 101, Ltr_f = 102, Ltr_g = 103, Ltr_h = 104 + , Ltr_i = 105, Ltr_j = 106, Ltr_k = 107, Ltr_l = 108, Ltr_m = 109 + , Ltr_n = 110, Ltr_o = 111, Ltr_p = 112, Ltr_q = 113, Ltr_r = 114 + , Ltr_s = 115, Ltr_t = 116, Ltr_u = 117, Ltr_v = 118, Ltr_w = 119 + , Ltr_x = 120, Ltr_y = 121, Ltr_z = 122, Curly_bgn = 123, Pipe = 124 + , Curly_end = 125, Tilde = 126 + ; + public static final byte + Angle_bgn = Lt, Angle_end = Gt + ; + public static final byte Max_7_bit = (byte)127, Ascii_min = 0, Ascii_max = 127; + public static boolean Is_sym(byte b) { + switch (b) { + case Byte_ascii.Bang: case Byte_ascii.Quote: + case Byte_ascii.Hash: case Byte_ascii.Dollar: case Byte_ascii.Percent: case Byte_ascii.Amp: case Byte_ascii.Apos: + case Byte_ascii.Paren_bgn: case Byte_ascii.Paren_end: case Byte_ascii.Star: case Byte_ascii.Plus: case Byte_ascii.Comma: + case Byte_ascii.Dash: case Byte_ascii.Dot: case Byte_ascii.Slash: + case Byte_ascii.Colon: case Byte_ascii.Semic: + case Byte_ascii.Lt: case Byte_ascii.Eq: case Byte_ascii.Gt: case Byte_ascii.Question: case Byte_ascii.At: + case Byte_ascii.Brack_bgn: case Byte_ascii.Backslash: case Byte_ascii.Brack_end: case Byte_ascii.Pow: + case Byte_ascii.Underline: case Byte_ascii.Tick: + case Byte_ascii.Curly_bgn: case Byte_ascii.Pipe: + case Byte_ascii.Curly_end: case Byte_ascii.Tilde: + return true; + default: + return false; + } + } + public static boolean Is_ltr(byte b) { + return ( b >= Byte_ascii.Ltr_a && b <= Byte_ascii.Ltr_z + || b >= Byte_ascii.Ltr_A && b <= Byte_ascii.Ltr_Z); + } + public static boolean Is_ws(byte b) { + switch (b) { + case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: case Byte_ascii.Space: return true; + default: return false; + } + } + public static boolean Is_num(byte b) { + return b > Byte_ascii.Slash && b < Byte_ascii.Colon; + } + public static byte To_a7_int(byte b) {return (byte)(b - Byte_ascii.Num_0);} + public static byte To_a7_str(int digit) { + switch (digit) { + case 0: return Byte_ascii.Num_0; case 1: return Byte_ascii.Num_1; case 2: return Byte_ascii.Num_2; case 3: return Byte_ascii.Num_3; case 4: return Byte_ascii.Num_4; + case 5: return Byte_ascii.Num_5; case 6: return Byte_ascii.Num_6; case 7: return Byte_ascii.Num_7; case 8: return Byte_ascii.Num_8; case 9: return Byte_ascii.Num_9; + default: throw Err_.new_("Byte_ascii", "unknown digit", "digit", digit); + } + } + public static String To_str(byte b) {return Char_.To_str((char)b);} + public static byte Case_upper(byte b) { + return b > 96 && b < 123 + ? (byte)(b - 32) + : b; + } + public static byte Case_lower(byte b) { + return b > 64 && b < 91 + ? (byte)(b + 32) + : b; + } + public static final byte[] Space_len2 = new byte[] {Space, Space}, Space_len4 = new byte[] {Space, Space, Space, Space}; + public static final byte[] + Tab_bry = new byte[] {Byte_ascii.Tab} + , Nl_bry = new byte[] {Byte_ascii.Nl} + , Space_bry = new byte[] {Byte_ascii.Space} + , Bang_bry = new byte[] {Byte_ascii.Bang} + , Quote_bry = new byte[] {Byte_ascii.Quote} + , Hash_bry = new byte[] {Byte_ascii.Hash} + , Dot_bry = new byte[] {Byte_ascii.Dot} + , Angle_bgn_bry = new byte[] {Byte_ascii.Angle_bgn} + , Angle_end_bry = new byte[] {Byte_ascii.Angle_end} + , Comma_bry = new byte[] {Byte_ascii.Comma} + , Colon_bry = new byte[] {Byte_ascii.Colon} + , Semic_bry = new byte[] {Byte_ascii.Semic} + , Eq_bry = new byte[] {Byte_ascii.Eq} + , Amp_bry = new byte[] {Byte_ascii.Amp} + , Lt_bry = new byte[] {Byte_ascii.Lt} + , Gt_bry = new byte[] {Byte_ascii.Gt} + , Question_bry = new byte[] {Byte_ascii.Question} + , Brack_bgn_bry = new byte[] {Byte_ascii.Brack_bgn} + , Brack_end_bry = new byte[] {Byte_ascii.Brack_end} + , Apos_bry = new byte[] {Byte_ascii.Apos} + , Pipe_bry = new byte[] {Byte_ascii.Pipe} + , Underline_bry = new byte[] {Byte_ascii.Underline} + , Slash_bry = new byte[] {Byte_ascii.Slash} + , Star_bry = new byte[] {Byte_ascii.Star} + , Dash_bry = new byte[] {Byte_ascii.Dash} + , Cr_lf_bry = new byte[] {Byte_ascii.Cr, Byte_ascii.Nl} + , Num_0_bry = new byte[] {Byte_ascii.Num_0} + , Num_1_bry = new byte[] {Byte_ascii.Num_1} + ; +} diff --git a/100_core/src/gplx/Cancelable.java b/100_core/src/gplx/Cancelable.java new file mode 100644 index 000000000..fe3415a68 --- /dev/null +++ b/100_core/src/gplx/Cancelable.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Cancelable { + boolean Canceled(); + void Cancel(); +} diff --git a/100_core/src/gplx/Cancelable_.java b/100_core/src/gplx/Cancelable_.java new file mode 100644 index 000000000..0e838042f --- /dev/null +++ b/100_core/src/gplx/Cancelable_.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Cancelable_ { + public static final Cancelable Never = new Cancelable_never(); + public static Cancelable New_proxy() {return new Cancelable_proxy();} +} +class Cancelable_never implements Cancelable { + public boolean Canceled() {return false;} + public void Cancel() {} +} +class Cancelable_proxy implements Cancelable { + private boolean canceled = false; + public boolean Canceled() {return canceled;} + public void Cancel() {canceled = true;} +} diff --git a/100_core/src/gplx/Char_.java b/100_core/src/gplx/Char_.java new file mode 100644 index 000000000..48635d4ef --- /dev/null +++ b/100_core/src/gplx/Char_.java @@ -0,0 +1,73 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Char_ { + public static final Class Cls_ref_type = Character.class; + public static final char Null = '\0', NewLine = '\n'; + public static boolean IsCaseLower(char c) {return Character.isLowerCase(c);} + public static boolean IsLetterOrDigit(char c) {return Character.isLetterOrDigit(c);} + public static boolean IsLetterEnglish(char c) { + switch (c) { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': return true; + default: return false; + } + } + public static boolean IsLetterLowerEnglish(char c) { + switch (c) { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': return true; + default: return false; + } + } + public static boolean IsNumber(char c) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return true; + default: return false; + } + } + public static boolean IsWhitespace(char c) { + switch (c) { + case ' ': case '\t': case '\n': case '\r': return true; + default: return false; + } + } + public static boolean In(char match, char... ary) { + for (char itm : ary) + if (itm == match) return true; + return false; + } + public static int To_int_or(char c, int or) { + switch (c) { + case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; + case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; + default: return or; + } + } + public static String To_str(char[] ary, int pos, int length) {return new String(ary, pos, length);} + public static String To_str(int b) {return To_str((char)b);} + public static String To_str(char c) {return String.valueOf(c);} + public static char By_int(int i) {return (char)i;} + public static char cast(Object o) {try {return (Character)o;} catch(Exception e) {throw Err_.new_type_mismatch_w_exc(e, char.class, o);}} + public static char parse(String raw) {try {return raw.charAt(0);} catch(Exception exc) {throw Err_.new_parse_exc(exc, char.class, raw);}} +} diff --git a/100_core/src/gplx/CompareAble.java b/100_core/src/gplx/CompareAble.java new file mode 100644 index 000000000..5e8bcb7ef --- /dev/null +++ b/100_core/src/gplx/CompareAble.java @@ -0,0 +1,20 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface CompareAble extends Comparable {} // URL:/doc/gplx/CompareAble_.txt +// public int compareTo(Object obj) {Type comp = (Type)obj; return prop.compareTo(comp.prop);} diff --git a/100_core/src/gplx/CompareAble_.java b/100_core/src/gplx/CompareAble_.java new file mode 100644 index 000000000..58168de29 --- /dev/null +++ b/100_core/src/gplx/CompareAble_.java @@ -0,0 +1,78 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.lists.*; +public class CompareAble_ { + public static Comparable as_(Object obj) {return obj instanceof Comparable ? (Comparable)obj : null;} + public static int Compare_obj(Object lhs, Object rhs) {return CompareComparables(as_(lhs), as_(rhs));} + public static int CompareComparables(Comparable lhs, Comparable rhs) { + if (lhs == null && rhs == null) return CompareAble_.Same; + else if (lhs == null) return CompareAble_.More; + else if (rhs == null) return CompareAble_.Less; + else return Compare(lhs, rhs); + } + + public static boolean Is_more(Comparable lhs, Comparable rhs) {return Is(More, lhs, rhs);} + public static boolean Is_moreOrSame(Comparable lhs, Comparable rhs) {return Is(MoreOrSame, lhs, rhs);} + public static boolean Is_less(Comparable lhs, Comparable rhs) {return Is(Less, lhs, rhs);} + public static boolean Is_lessOrSame(Comparable lhs, Comparable rhs) {return Is(LessOrSame, lhs, rhs);} + public static boolean Is_same(Comparable lhs, Comparable rhs) {return Is(Same, lhs, rhs);} + public static boolean Is(int expt, Comparable lhs, Comparable rhs) { + int actl = CompareComparables(lhs, rhs); + if (actl == Same && expt % 2 == Same) // actl=Same and expt=(Same||MoreOrSame||LessOrSame) + return true; + else + return (actl * expt) > 0; // actl=More||Less; expd will match if on same side of 0 (ex: expt=Less; actl=Less; -1 * -1 = 1) + } +// public static int FindSlot(ComparerAble comparer, Object[] ary, Object itm) {return FindSlot(comparer, ary, itm, false);} + public static int FindSlot(ComparerAble comparer, Object[] ary, Object itm) {if (itm == null) throw Err_.new_null(); + int aryLen = ary.length; + switch (aryLen) { + case 0: throw Err_.new_wo_type("ary cannot have 0 itms"); + case 1: return 0; + } + int lo = -1, hi = aryLen - 1; // NOTE: -1 is necessary; see test + int curPos = (hi - lo) / 2; + int delta = 1; + while (true) { + Object curSeg = ary[curPos]; + int comp = curSeg == null ? CompareAble_.More : comparer.compare(itm, curSeg); // nulls should only happen for lastAry +// if (dbg) { +// Tfds.Write(curPos, itm.toString(), comp, comp.toString(), curSeg.toString()); +// } + if (comp == CompareAble_.Same) return curPos; + else if (comp > CompareAble_.Same) {lo = curPos; delta = 1;} + else if (comp < CompareAble_.Same) {hi = curPos; delta = -1;} + int dif = hi - lo; + if (dif == 1 || dif == 0) return hi; // NOTE: can be 0 when ary.length == 1 || 2; also, sometimes 0 in some situations + else curPos += (dif / 2) * delta; + } + } + public static int Compare(Comparable lhs, Comparable rhs) {return lhs.compareTo(rhs);} + + public static final int + More = 1 + , Less = -1 + , Same = 0 + , MoreOrSame = 2 + , LessOrSame = -2 + , ReverseMult = -1 + , OffsetCompare = 1 // handle srcPos >= 1 -> srcPosChk > 0 + ; + public static int Multiplier(boolean v) {return v ? 1 : -1;} +} diff --git a/100_core/src/gplx/CompareAble_tst.java b/100_core/src/gplx/CompareAble_tst.java new file mode 100644 index 000000000..01df932d0 --- /dev/null +++ b/100_core/src/gplx/CompareAble_tst.java @@ -0,0 +1,38 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +import gplx.core.lists.*; +public class CompareAble_tst implements ComparerAble { + @Test public void Basic() { + String[] slotAry = new String[] {"b", "e", "h"}; // 0=b 1=e 2=h + tst_FindSlot(slotAry, "f", "h"); // f -> 1 2 -> 2 + tst_FindSlot(slotAry, "c", "e"); // c -> -1 1 -> 0 -> 0 1 -> 1 + tst_FindSlot(slotAry, "a", "b"); // a -> -1 1 -> 0 -> -1 0 -> 0 + } + @Test public void Null() { + String[] slotAry = new String[] {"b", "g", "l", "q", "v", null}; + tst_FindSlot(slotAry, "a", "b"); + tst_FindSlot(slotAry, "b", "b"); + tst_FindSlot(slotAry, "c", "g"); + tst_FindSlot(slotAry, "v", "v"); + tst_FindSlot(slotAry, "w", null); + } + public int compare(Object lhsObj, Object rhsObj) {return CompareAble_.Compare_obj(lhsObj, rhsObj);} + void tst_FindSlot(String[] slotAry, String s, String expd) {Tfds.Eq(expd, slotAry[CompareAble_.FindSlot(this, slotAry, s)]);} +} diff --git a/100_core/src/gplx/DateAdp.java b/100_core/src/gplx/DateAdp.java new file mode 100644 index 000000000..3c934f8c2 --- /dev/null +++ b/100_core/src/gplx/DateAdp.java @@ -0,0 +1,153 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.TimeZone; +import java.text.SimpleDateFormat; +public class DateAdp implements CompareAble, Gfo_invk { + public int compareTo(Object obj) {DateAdp comp = (DateAdp)obj; return under.compareTo(comp.under);} + @Override public String toString() {return XtoStr_gplx_long();} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_XtoStr_fmt)) return XtoStr_fmt("yyyy-MM-dd HH:mm:ss"); + else if (ctx.Match(k, Invk_AddDays)) { + int days = m.ReadInt("days"); + if (ctx.Deny()) return this; + return this.Add_day(days); + } + else return Gfo_invk_.Rv_unhandled; + } public static final String Invk_XtoStr_fmt = "XtoStr_fmt", Invk_AddDays = "Add_day"; + public int Segment(int segmentIdx) { + switch (segmentIdx) { + case DateAdp_.SegIdx_year: return this.Year(); + case DateAdp_.SegIdx_month: return this.Month(); + case DateAdp_.SegIdx_day: return this.Day(); + case DateAdp_.SegIdx_hour: return this.Hour(); + case DateAdp_.SegIdx_minute: return this.Minute(); + case DateAdp_.SegIdx_second: return this.Second(); + case DateAdp_.SegIdx_frac: return this.Frac(); + case DateAdp_.SegIdx_dayOfWeek: return this.DayOfWeek(); + case DateAdp_.SegIdx_weekOfYear: return this.WeekOfYear(); + case DateAdp_.SegIdx_dayOfYear: return this.DayOfYear(); + default: throw Err_.new_unhandled(segmentIdx); + } + } + public int[] XtoSegAry() { + int[] rv = new int[7]; + rv[DateAdp_.SegIdx_year] = this.Year(); + rv[DateAdp_.SegIdx_month] = this.Month(); + rv[DateAdp_.SegIdx_day] = this.Day(); + rv[DateAdp_.SegIdx_hour] = this.Hour(); + rv[DateAdp_.SegIdx_minute] = this.Minute(); + rv[DateAdp_.SegIdx_second] = this.Second(); + rv[DateAdp_.SegIdx_frac] = this.Frac(); + return rv; + } + public String XtoStr_gplx() {return XtoStr_fmt("yyyyMMdd_HHmmss.fff");} + public String XtoStr_gplx_long() {return XtoStr_fmt("yyyy-MM-dd HH:mm:ss.fff");} + public String XtoStr_fmt_HHmmss() {return XtoStr_fmt("HH:mm:ss");} + public String XtoStr_fmt_HHmm() {return XtoStr_fmt("HH:mm");} + public String XtoStr_fmt_yyyy_MM_dd() {return XtoStr_fmt("yyyy-MM-dd");} + public String XtoStr_fmt_yyyyMMdd_HHmmss() {return XtoStr_fmt("yyyyMMdd_HHmmss");} + public String XtoStr_fmt_yyyyMMdd_HHmmss_fff() {return XtoStr_fmt("yyyyMMdd_HHmmss.fff");} + public String XtoStr_fmt_yyyyMMdd_HHmm() {return XtoStr_fmt("yyyyMMdd_HHmm");} + public String XtoStr_fmt_yyyy_MM_dd_HH_mm() {return XtoStr_fmt("yyyy-MM-dd HH:mm");} + public String XtoStr_fmt_yyyy_MM_dd_HH_mm_ss() {return XtoStr_fmt("yyyy-MM-dd HH:mm:ss");} + public String XtoStr_fmt_iso_8561() {return XtoStr_fmt("yyyy-MM-dd HH:mm:ss");} + public String XtoStr_fmt_iso_8561_w_tz() {return XtoStr_fmt("yyyy-MM-dd'T'HH:mm:ss'Z'");} + public static int Timezone_offset_test = Int_.Min_value; + public Calendar UnderDateTime() {return under;} Calendar under; + public int Year() {return under.get(Calendar.YEAR);} + public int Month() {return under.get(Calendar.MONTH) + Month_base0adj;} + public int Day() {return under.get(Calendar.DAY_OF_MONTH);} + public int Hour() {return under.get(Calendar.HOUR_OF_DAY);} + public int Minute() {return under.get(Calendar.MINUTE);} + public int Second() {return under.get(Calendar.SECOND);} + public int DayOfWeek() {return under.get(Calendar.DAY_OF_WEEK) - 1;} // -1 : Base0; NOTE: dotnet/php is also Sunday=0 + public int DayOfYear() {return under.get(Calendar.DAY_OF_YEAR);} + public int Timezone_offset() { + return Timezone_offset_test == Int_.Min_value // Timezone_offset_test not over-ridden + ? 0 + // ? under.getTimeZone().getOffset(this.Timestamp_unix()) / 1000 // divide by 1000 to convert from ms to seconds + : Timezone_offset_test + ; + } + public DateAdp XtoUtc() { + java.util.Date date = under.getTime(); + java.util.TimeZone tz = under.getTimeZone(); + long msFromEpochGmt = date.getTime(); + int offsetFromUTC = tz.getOffset(msFromEpochGmt); + Calendar gmtCal = Calendar.getInstance(); + gmtCal.setTimeInMillis(msFromEpochGmt + -offsetFromUTC); + return new DateAdp(gmtCal); + } + public DateAdp XtoLocal() { + java.util.Date date = under.getTime(); + java.util.TimeZone tz = under.getTimeZone(); + long msFromEpochGmt = date.getTime(); + int offsetFromUTC = tz.getOffset(msFromEpochGmt); + Calendar gmtCal = Calendar.getInstance(); + gmtCal.setTimeInMillis(msFromEpochGmt + offsetFromUTC); + return new DateAdp(gmtCal); + } + public long Timestamp_unix() { + long offsetFromUTC = (under.getTimeZone().getOffset(0)); + boolean dst = TimeZone.getDefault().inDaylightTime(under.getTime()); + long dst_adj = dst ? 3600000 : 0; + return (under.getTimeInMillis() + offsetFromUTC + dst_adj) / 1000; + } + public int WeekOfYear() {return under.get(Calendar.WEEK_OF_YEAR);} + public int Frac() {return under.get(Calendar.MILLISECOND);} + public DateAdp Add_frac(int val) {return CloneAndAdd(Calendar.MILLISECOND, val);} + public DateAdp Add_second(int val) {return CloneAndAdd(Calendar.SECOND, val);} + public DateAdp Add_minute(int val) {return CloneAndAdd(Calendar.MINUTE, val);} + public DateAdp Add_hour(int val) {return CloneAndAdd(Calendar.HOUR, val);} + public DateAdp Add_day(int val) {return CloneAndAdd(Calendar.DAY_OF_MONTH, val);} + public DateAdp Add_month(int val) {return CloneAndAdd(Calendar.MONTH, val);} + public DateAdp Add_year(int val) {return CloneAndAdd(Calendar.YEAR, val);} + DateAdp CloneAndAdd(int field, int val) { + Calendar clone = (Calendar)under.clone(); + clone.add(field, val); + return new DateAdp(clone); + } + public String XtoStr_fmt(String fmt) { + fmt = fmt.replace("f", "S"); + SimpleDateFormat sdf = new SimpleDateFormat(fmt); + return sdf.format(under.getTime()); + } + public String XtoStr_tz() { + SimpleDateFormat sdf = new SimpleDateFormat("Z"); + String time_zone = sdf.format(under.getTime()); + return String_.Mid(time_zone, 0, 3) + ":" + String_.Mid(time_zone, 3, String_.Len(time_zone)); + } + public boolean Eq(DateAdp v) {DateAdp comp = v; return Object_.Eq(under.getTimeInMillis(), comp.under.getTimeInMillis());} + public int Diff_days(DateAdp prev) { + long diff = this.under.getTimeInMillis() - prev.under.getTimeInMillis(); + return (int)(diff / (1000 * 60 * 60 * 24)); + } + public Time_span Diff(DateAdp earlier) { + long diff = this.under.getTimeInMillis() - earlier.under.getTimeInMillis(); + return Time_span_.fracs_(diff); + } + protected DateAdp(Calendar under) {this.under = under;} + protected DateAdp(int year, int month, int day, int hour, int minute, int second, int frac) { + this.under = new GregorianCalendar(year, month - Month_base0adj, day, hour, minute, second); + under.set(Calendar.MILLISECOND, frac); + } + public static final int Month_base0adj = 1; + } diff --git a/100_core/src/gplx/DateAdp_.java b/100_core/src/gplx/DateAdp_.java new file mode 100644 index 000000000..59d2bf7f2 --- /dev/null +++ b/100_core/src/gplx/DateAdp_.java @@ -0,0 +1,126 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; +import gplx.core.times.*; +public class DateAdp_ implements Gfo_invk { + public static final String Cls_ref_name = "Date"; + public static final Class Cls_ref_type = DateAdp.class; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Now)) return Datetime_now.Get(); + else return Gfo_invk_.Rv_unhandled; + } public static final String Invk_Now = "Now"; + public static final DateAdp MinValue = new DateAdp( 1, 1, 1, 0, 0, 0, 0); + public static final DateAdp MaxValue = new DateAdp(9999, 12, 31, 23, 59, 59, 999); +// public static DateAdp Now() {return Tfds.Now_enabled() ? Tfds.Now() : new DateAdp(new GregorianCalendar());} + public static DateAdp new_(int year, int month, int day, int hour, int minute, int second, int frac) {return new DateAdp(year, month, day, hour, minute, second, frac);} + public static DateAdp seg_(int[] ary) { + int ary_len = ary.length; + int y = ary_len > 0 ? ary[0] : 1; + int M = ary_len > 1 ? ary[1] : 1; + int d = ary_len > 2 ? ary[2] : 1; + int h = ary_len > 3 ? ary[3] : 0; + int m = ary_len > 4 ? ary[4] : 0; + int s = ary_len > 5 ? ary[5] : 0; + int f = ary_len > 6 ? ary[6] : 0; + return new DateAdp(y, M, d, h, m, s, f); + } + public static DateAdp cast(Object arg) {try {return (DateAdp)arg;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, DateAdp.class, arg);}} + public static DateAdp parse_iso8561_or(String raw, DateAdp or) { + try {return parse_iso8561(raw);} + catch (Exception e) {Err_.Noop(e); return or;} + } + public static DateAdp parse_iso8561(String raw) { // NOTE: for now, same as parse_gplx + int[] ary = date_parser.Parse_iso8651_like(raw); + if (ary[1] < 1 || ary[1] > 12) return DateAdp_.MinValue; // guard against invalid month + if (ary[2] < 1 || ary[2] > 31) return DateAdp_.MinValue; + return new DateAdp(ary[0], ary[1], ary[2], ary[3], ary[4], ary[5], ary[6]); + } + public static DateAdp parse_gplx(String raw) { + int[] ary = date_parser.Parse_iso8651_like(raw); + if (ary[1] < 1 || ary[1] > 12) return DateAdp_.MinValue; // guard against invalid month + if (ary[2] < 1 || ary[2] > 31) return DateAdp_.MinValue; + return new DateAdp(ary[0], ary[1], ary[2], ary[3], ary[4], ary[5], ary[6]); + } static DateAdp_parser date_parser = DateAdp_parser.new_(); + public static DateAdp dateTime_(GregorianCalendar v) {return new DateAdp(v);} + public static DateAdp dateTime_obj_(Object v) {return new DateAdp((GregorianCalendar)v);} + public static final DateAdp_ Gfs = new DateAdp_(); + + public static int DaysInMonth(DateAdp date) { + int rv = DaysInMonth_ary[date.Month() - Int_.Base1]; + if (rv == 28 && IsLeapYear(date.Year())) rv = 29; + return rv; + } static int [] DaysInMonth_ary = {31,28,31,30,31,30,31,31,30,31,30,31}; + public static boolean IsLeapYear(int year) { + if (year % 4 != 0) return false; + else if (year % 400 == 0) return true; + else if (year % 100 == 0) return false; + else return true; + } + public static DateAdp unixtime_utc_seconds_(long v) {return unixtime_utc_ms_(v * 1000);} + public static DateAdp parse_fmt_or(String raw, String fmt, DateAdp or) { + try {return parse_fmt(raw, fmt);} + catch (Exception e) {Err_.Noop(e); return or;} + } + public static DateAdp db_(Object v) { + Timestamp ts = (Timestamp)v; + Calendar gc = Calendar.getInstance(); + gc.setTimeInMillis(ts.getTime()); + return new DateAdp(gc); + } + public static DateAdp parse_(String raw) { + SimpleDateFormat sdf = new SimpleDateFormat(); + Date d = null; + try {d = sdf.parse(raw);} + catch (ParseException e) {throw Err_.new_("parse", "failed to parse to DateAdp", "raw", raw);} + GregorianCalendar cal = (GregorianCalendar)Calendar.getInstance(); + cal.setTime(d); + return dateTime_(cal); + } + public static DateAdp parse_fmt(String raw, String fmt) { + fmt = fmt.replace('t', 'a'); // AM/PM + fmt = fmt.replace('f', 'S'); // milliseconds + SimpleDateFormat sdf = new SimpleDateFormat(fmt, Locale.US); + Date d = null; + try {d = sdf.parse(raw);} + catch (ParseException e) {throw Err_.new_("parse", "failed to parse to DateAdp", "raw", raw, "fmt", fmt);} + GregorianCalendar cal = (GregorianCalendar)Calendar.getInstance(); + cal.setTime(d); + return dateTime_(cal); + } + public static DateAdp unixtime_utc_ms_(long v) {return unixtime_lcl_ms_(v).XtoUtc();} + public static DateAdp unixtime_lcl_ms_(long v) { + GregorianCalendar c = new GregorianCalendar(); + c.setTimeInMillis(v); + return new DateAdp(c); + } + public static final int SegIdx_year = 0, SegIdx_month = 1, SegIdx_day = 2, SegIdx_hour = 3, SegIdx_minute = 4, SegIdx_second = 5, SegIdx_frac = 6, SegIdx_dayOfWeek = 7, SegIdx_weekOfYear = 8, SegIdx_dayOfYear = 9, SegIdx__max = 10; + public static String Xto_str_fmt_or(DateAdp v, String fmt, String or) { + return v == null ? or : v.XtoStr_fmt(fmt); + } + public static final String + Fmt_iso8561_date_time = "yyyy-MM-dd HH:mm:ss" + , Fmt__yyyyMMdd = "yyyyMMdd"; +} diff --git a/100_core/src/gplx/DateAdp__tst.java b/100_core/src/gplx/DateAdp__tst.java new file mode 100644 index 000000000..9a9a6e9ac --- /dev/null +++ b/100_core/src/gplx/DateAdp__tst.java @@ -0,0 +1,88 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; import gplx.core.tests.*; +public class DateAdp__tst { + private final DateAdp__fxt fxt = new DateAdp__fxt(); + @Test public void Parse_gplx() { + fxt.Test__parse_gplx("99991231_235959.999" , "99991231_235959.999"); + fxt.Test__parse_gplx("20090430_213200.123" , "20090430_213200.123"); + fxt.Test__parse_gplx("20090430_213200" , "20090430_213200.000"); + fxt.Test__parse_gplx("20090430" , "20090430_000000.000"); + } + @Test public void Parse_separators() { + fxt.Test__parse_gplx("2009-04-30 21:32:00.123" , "20090430_213200.123"); + fxt.Test__parse_gplx("2009-04-30 21:32:00" , "20090430_213200.000"); + fxt.Test__parse_gplx("2009-04-30" , "20090430_000000.000"); + } + @Test public void Parse_utc() { + fxt.Test__parse_gplx("2015-12-26T10:03:53Z" , "20151226_100353.000"); + } + @Test public void DayOfWeek() { + fxt.Test__day_of_week("2012-01-18", 3); //3=Wed + } + @Test public void WeekOfYear() { + fxt.Test__week_of_year("2006-02-01", 5); // 1-1:Sun;2-1:Wed + fxt.Test__week_of_year("2007-02-01", 5); // 1-1:Mon;2-1:Thu + fxt.Test__week_of_year("2008-02-01", 5); // 1-1:Tue;2-1:Fri + fxt.Test__week_of_year("2009-02-01", 6); // 1-1:Thu;2-1:Sun + fxt.Test__week_of_year("2010-02-01", 6); // 1-1:Fri;2-1:Mon + fxt.Test__week_of_year("2011-02-01", 6); // 1-1:Sat;2-1:Tue + } + @Test public void DayOfYear() { + fxt.Test__day_of_year("2012-01-01", 1); + fxt.Test__day_of_year("2012-02-29", 60); + fxt.Test__day_of_year("2012-12-31", 366); + } + @Test public void Timestamp_unix() { + fxt.Test__timestamp_unix("1970-01-01 00:00:00", 0); + fxt.Test__timestamp_unix("2012-01-01 00:00:00", 1325376000); + } + @Test public void DaysInMonth() { + fxt.Test__days_in_month("2012-01-01", 31); + fxt.Test__days_in_month("2012-02-01", 29); + fxt.Test__days_in_month("2012-04-01", 30); + fxt.Test__days_in_month("2011-02-01", 28); + } + @Test public void XtoUtc() { + fxt.Test__to_utc("2012-01-01 00:00", "2012-01-01 05:00"); //4=Wed + } +} +class DateAdp__fxt { + public void Test__parse_gplx(String raw, String expd) { + Gftest.Eq__str(expd, DateAdp_.parse_gplx(raw).XtoStr_gplx()); + } + public void Test__day_of_week(String raw, int expd) { + Gftest.Eq__int(expd, DateAdp_.parse_gplx(raw).DayOfWeek()); + } + public void Test__week_of_year(String raw, int expd) { + Gftest.Eq__int(expd, DateAdp_.parse_gplx(raw).WeekOfYear()); + } + public void Test__day_of_year(String raw, int expd) { + Gftest.Eq__int(expd, DateAdp_.parse_gplx(raw).DayOfYear()); + } + public void Test__days_in_month(String raw, int expd) { + Gftest.Eq__int(expd, DateAdp_.DaysInMonth(DateAdp_.parse_gplx(raw))); + } + public void Test__timestamp_unix(String raw, long expd) { + Gftest.Eq__long(expd, DateAdp_.parse_gplx(raw).Timestamp_unix()); + } + public void Test__to_utc(String raw, String expd) { + Tfds.Eq(expd, DateAdp_.parse_gplx(raw).XtoUtc().XtoStr_fmt_yyyy_MM_dd_HH_mm()); + } +} diff --git a/100_core/src/gplx/Datetime_now.java b/100_core/src/gplx/Datetime_now.java new file mode 100644 index 000000000..5f21a1d25 --- /dev/null +++ b/100_core/src/gplx/Datetime_now.java @@ -0,0 +1,69 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.util.GregorianCalendar; +public class Datetime_now { + private static final DateAdp dflt = DateAdp_.parse_gplx("2001-01-01 00:00:00.000"); + private static DateAdp manual; + private static boolean autoincrement = true; + public static void Manual_y_() { + manual = dflt; + } + public static void Manual_n_() { + manual = null; + autoincrement = true; + } + public static void Manual_and_freeze_(DateAdp v) { + manual = v; + autoincrement = false; + } + public static void Manual_(DateAdp v) { + manual = v; + } + public static void Autoincrement_n_() { + autoincrement = false; + } + public static DateAdp Dflt_add_min_(int v) { + return dflt.Add_minute(v); + } + + public static DateAdp Get() { + if (manual == null) return new DateAdp(new GregorianCalendar()); + DateAdp rv = manual; + if (autoincrement) manual = rv.Add_minute(1); // simulate passage of manual by increasing manual by 1 minute with each call + return rv; + } + public static DateAdp Get_force() { // ignore manual and force get of real time + return new DateAdp(new GregorianCalendar()); + } +// private static final DateAdp manual_time_dflt = DateAdp_.parse_gplx("2001-01-01 00:00:00.000"); +// private static DateAdp manual_time; +// static boolean Now_enabled() {return now_enabled;} private static boolean now_enabled; +// static void Now_enabled_y_() {now_enabled = Bool_.Y; manual_time = manual_time_dflt;} +// static void Now_enabled_n_() {now_enabled = Bool_.N; now_freeze = false;} + // public static void Now_set(DateAdp date) {now_enabled = true; manual_time = date;} + // public static void Now_freeze_y_() {now_freeze = true;} +// private static boolean now_freeze; + // public static DateAdp Now_time0_add_min(int minutes) {return manual_time_dflt.Add_minute(minutes);} +// @gplx.Internal protected static DateAdp Now() { +// DateAdp rv = manual_time; +// if (!now_freeze) manual_time = rv.Add_minute(1); +// return rv; +// } + +} \ No newline at end of file diff --git a/100_core/src/gplx/Decimal_adp.java b/100_core/src/gplx/Decimal_adp.java new file mode 100644 index 000000000..02698eff4 --- /dev/null +++ b/100_core/src/gplx/Decimal_adp.java @@ -0,0 +1,92 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.text.DecimalFormat; +public class Decimal_adp implements CompareAble { + public int compareTo(Object obj) {Decimal_adp comp = (Decimal_adp)obj; return under.compareTo(comp.under);} + protected Decimal_adp(BigDecimal v) {this.under = v;} private final BigDecimal under; + protected Decimal_adp(int v) {this.under = new BigDecimal(v);} + public Object Under() {return under;} + public BigDecimal Under_as_native() {return under;} + public int Precision() {return under.precision();} + public int Frac_1000() {return (int)(under.movePointRight(3).floatValue() % 1000);} + public boolean Eq(Decimal_adp v) {return v.under.doubleValue() == under.doubleValue();} + public boolean Eq(int v) {return under.doubleValue() == v;} + public String To_str() { + BigDecimal tmp = under; + int tmp_scale = tmp.scale(); + if (tmp_scale <= -14) return tmp.toString(); // NOTE: if large number, call .toString which will return exponential notaion (1E##) instead of literal (1000....); 14 matches MW code; DATE:2015-04-10 + if (tmp_scale > 14) + tmp = tmp.setScale(14, RoundingMode.DOWN); // NOTE: if small number, round down to remove excessive zeroes; 14 matches PHP/C# values more closely; RoundingMode.Down for same reason; see E, Pi tests + return tmp .stripTrailingZeros() // NOTE: stripTrailingZeros for exp tests; EX: 120.0 -> 120; 0.01200000000000 -> .012 + .toPlainString(); // NOTE: toPlainString b/c stripTrailingZeros now converts 120 to 1.2E+2 (and any other value that is a multiple of 10) + } + public String To_str(String fmt) { + return new DecimalFormat(fmt).format(under); + } + @Override public String toString() {return under.toString();} + public int To_int() {return (int)under.doubleValue();} + public long To_long() {return (long)under.doubleValue();} + public long To_long_mult_1000() {return under.movePointRight(3).longValue();} + public double To_double() {return under.doubleValue();} + public Decimal_adp Add(Decimal_adp v) {return new Decimal_adp(under.add(v.under, Decimal_adp_.Gplx_rounding_context));} + public Decimal_adp Subtract(Decimal_adp v) {return new Decimal_adp(under.subtract(v.under, Decimal_adp_.Gplx_rounding_context));} + public Decimal_adp Multiply(Decimal_adp v) {return new Decimal_adp(under.multiply(v.under));} + public Decimal_adp Multiply(double v) {return new Decimal_adp(under.multiply(new BigDecimal(v, Decimal_adp_.Gplx_rounding_context)));} + public Decimal_adp Multiply(long v) {return new Decimal_adp(under.multiply(new BigDecimal(v)));} + public Decimal_adp Divide(Decimal_adp v) {return new Decimal_adp(under.divide(v.under, Decimal_adp_.Gplx_rounding_context));} + public Decimal_adp Mod(Decimal_adp v) {return new Decimal_adp(under.remainder(v.under, Decimal_adp_.Gplx_rounding_context));} + public Decimal_adp Abs() {return new Decimal_adp(under.abs(Decimal_adp_.Gplx_rounding_context));} + public Decimal_adp Pow(int v) {return new Decimal_adp(under.pow(v, Decimal_adp_.Gplx_rounding_context));} + public Decimal_adp Sqrt() {return new Decimal_adp(new BigDecimal(Math_.Sqrt(under.doubleValue())));} + public Decimal_adp Truncate() {return new Decimal_adp(under.intValue());} + public Decimal_adp Round_old(int v) {return new Decimal_adp(under.setScale(v, RoundingMode.HALF_UP));} + public Decimal_adp Round(int v) { + BigDecimal new_val = null; + if (v > 0) { + new_val = under.setScale(v, RoundingMode.HALF_UP); + } + else { + int actl_places = under.precision() - under.scale(); + int reqd_places = -v; + if (reqd_places < actl_places) + new_val = under.round(new java.math.MathContext(actl_places - reqd_places, RoundingMode.HALF_UP)); + else if (reqd_places == actl_places) { + int base_10 = (int)Math_.Pow(10, reqd_places - 1); + if (under.intValue() / base_10 < 5) + new_val = BigDecimal.ZERO; + else + new_val = new BigDecimal(Math_.Pow(10, reqd_places)); + } + else + new_val = BigDecimal.ZERO; + } + return new Decimal_adp(new_val); + } + public boolean Comp_gte(Decimal_adp v) {return under.doubleValue() >= v.under.doubleValue();} + public boolean Comp_gte(int v) {return under.doubleValue() >= v;} + public boolean Comp_lte(Decimal_adp v) {return under.doubleValue() <= v.under.doubleValue();} + public boolean Comp_lte(int v) {return under.doubleValue() <= v;} + public boolean Comp_gt(Decimal_adp v) {return under.doubleValue() > v.under.doubleValue();} + public boolean Comp_gt(int v) {return under.doubleValue() > v;} + public boolean Comp_lt(Decimal_adp v) {return under.doubleValue() < v.under.doubleValue();} + public boolean Comp_lt(int v) {return under.doubleValue() < v;} + } diff --git a/100_core/src/gplx/Decimal_adp_.java b/100_core/src/gplx/Decimal_adp_.java new file mode 100644 index 000000000..f6444df52 --- /dev/null +++ b/100_core/src/gplx/Decimal_adp_.java @@ -0,0 +1,70 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.Locale; +public class Decimal_adp_ { + public static final Class Cls_ref_type = Decimal_adp.class; + public static Decimal_adp as_(Object obj) {return obj instanceof Decimal_adp ? (Decimal_adp)obj : null;} + public static final Decimal_adp Zero = new Decimal_adp(0); + public static final Decimal_adp One = new Decimal_adp(1); + public static final Decimal_adp Neg1 = new Decimal_adp(-1); + public static final Decimal_adp Const_e = Decimal_adp_.double_(Math_.E); + public static final Decimal_adp Const_pi = Decimal_adp_.double_(Math_.Pi); + public static Decimal_adp base1000_(long v) {return divide_(v, 1000);} + public static Decimal_adp parts_1000_(long num, int frc) {return divide_((num * (1000)) + frc, 1000);} + public static Decimal_adp parts_(long num, int frc) { + // int log10 = frc == 0 ? 0 : (Math_.Log10(frc) + 1); + // int pow10 = (int)Math_.Pow(10, log10); + int pow10 = XtoPow10(frc); + return divide_((num * (pow10)) + frc, pow10); + } + public static Decimal_adp cast(Object obj) {return (Decimal_adp)obj;} + static int XtoPow10(int v) { + if (v > -1 && v < 10) return 10; + else if (v > 9 && v < 100) return 100; + else if (v > 99 && v < 1000) return 1000; + else if (v > 999 && v < 10000) return 10000; + else if (v > 9999 && v < 100000) return 100000; + else if (v > 99999 && v < 1000000) return 1000000; + else if (v > 999999 && v < 10000000) return 10000000; + else if (v > 9999999 && v < 100000000) return 100000000; + else if (v > 99999999 && v < 1000000000) return 1000000000; + else throw Err_.new_wo_type("value must be between 0 and 1 billion", "v", v); + } + public static String CalcPctStr(long dividend, long divisor, String fmt) { + if (divisor == 0) return "%ERR"; + return Decimal_adp_.float_(Float_.Div(dividend, divisor) * 100).To_str(fmt) + "%"; + } + public static Decimal_adp divide_safe_(long lhs, long rhs) {return rhs == 0 ? Zero : divide_(lhs, rhs);} + public static Decimal_adp divide_(long lhs, long rhs) { return new Decimal_adp(new BigDecimal(lhs).divide(new BigDecimal(rhs), Gplx_rounding_context)); } public static Decimal_adp int_(int v) {return new Decimal_adp(new BigDecimal(v));} public static Decimal_adp long_(long v) {return new Decimal_adp(new BigDecimal(v));} + public static Decimal_adp float_(float v) {return new Decimal_adp(new BigDecimal(v));} public static Decimal_adp double_(double v) {return new Decimal_adp(new BigDecimal(v));} + public static Decimal_adp double_thru_str_(double v) {return new Decimal_adp(BigDecimal.valueOf(v));} + public static Decimal_adp db_(Object v) {return new Decimal_adp((BigDecimal)v);} public static Decimal_adp parse(String raw) { + try { + DecimalFormat nf = (DecimalFormat)NumberFormat.getInstance(Locale.US); // always parse as US format; EX:".9" should not be ",9" in german; DATE:2016-01-31 + nf.setParseBigDecimal(true); + BigDecimal bd = (BigDecimal)nf.parse(raw); + return new Decimal_adp(bd); + } catch (ParseException e) { + throw Err_.new_("Decimal_adp_", "parse to decimal failed", "raw", raw); + } + } public static Decimal_adp pow_10_(int v) {return new Decimal_adp(new BigDecimal(1).scaleByPowerOfTen(v));} + public static final MathContext RoundDownContext = new MathContext(0, RoundingMode.DOWN); public static final MathContext Gplx_rounding_context = new MathContext(14, RoundingMode.HALF_UP); // changed from 28 to 14; DATE:2015-07-31 } diff --git a/100_core/src/gplx/Decimal_adp__tst.java b/100_core/src/gplx/Decimal_adp__tst.java new file mode 100644 index 000000000..04f26383f --- /dev/null +++ b/100_core/src/gplx/Decimal_adp__tst.java @@ -0,0 +1,88 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Decimal_adp__tst { + private final Decimal_adp__fxt fxt = new Decimal_adp__fxt(); + @Test public void divide_() { + fxt.Test_divide(1, 1000, "0.001"); + fxt.Test_divide(1, 3, "0.33333333333333"); + fxt.Test_divide(1, 7, "0.14285714285714"); + } + @Test public void base1000_() { + fxt.Test_base_1000(1000, "1"); + fxt.Test_base_1000(1234, "1.234"); + fxt.Test_base_1000(123, "0.123"); + } + @Test public void parts_() { + fxt.Test_parts(1, 0, "1"); + fxt.Test_parts(1, 2, "1.2"); + fxt.Test_parts(1, 23, "1.23"); + fxt.Test_parts(123, 4567, "123.4567"); + } + @Test public void parse() { + fxt.Test_parse("1", "1"); + fxt.Test_parse("1.2", "1.2"); + fxt.Test_parse("0.1", "0.1"); + } + @Test public void Truncate_decimal() { + fxt.Test_truncate_decimal("1", "1"); + fxt.Test_truncate_decimal("1.1", "1"); + fxt.Test_truncate_decimal("1.9", "1"); + } + @Test public void Fraction1000() { + fxt.Test_frac_1000(1, 1000, 1); // 0.001 + fxt.Test_frac_1000(1, 3, 333); // 0.33333333 + fxt.Test_frac_1000(1234, 1000, 234); // 1.234 + fxt.Test_frac_1000(12345, 10000, 234); // 1.2345 + } + @Test public void Lt() { + fxt.Test_comp_lt(1,123, 2, true); + fxt.Test_comp_lt(1,99999999, 2, true); + } + @Test public void To_str_fmt() { + fxt.Test_to_str_fmt(1, 2, "0.0", "0.5"); + fxt.Test_to_str_fmt(1, 3, "0.0", "0.3"); + fxt.Test_to_str_fmt(10000, 7, "0,000.000", "1,428.571"); + fxt.Test_to_str_fmt(1, 2, "00.00", "00.50"); + } + @Test public void Round() { + fxt.Test_round("123.456", 3, "123.456"); + fxt.Test_round("123.456", 2, "123.46"); + fxt.Test_round("123.456", 1, "123.5"); + fxt.Test_round("123.456", 0, "123"); + fxt.Test_round("123.456", -1, "120"); + fxt.Test_round("123.456", -2, "100"); + fxt.Test_round("123.456", -3, "0"); + + fxt.Test_round("6", -1, "10"); + fxt.Test_round("5", -1, "10"); + fxt.Test_round("6", -2, "0"); + } +} +class Decimal_adp__fxt { + public void Test_divide(int lhs, int rhs, String expd) {Tfds.Eq(expd, Decimal_adp_.divide_(lhs, rhs).To_str());} + public void Test_base_1000(int val, String expd) {Tfds.Eq(expd, Decimal_adp_.base1000_(val).To_str());} + public void Test_parts(int num, int fracs, String expd) {Tfds.Eq(expd, Decimal_adp_.parts_(num, fracs).To_str());} + public void Test_parse(String raw, String expd) {Tfds.Eq(expd, Decimal_adp_.parse(raw).To_str());} + public void Test_truncate_decimal(String raw, String expd) {Tfds.Eq(Decimal_adp_.parse(expd).To_str(), Decimal_adp_.parse(raw).Truncate().To_str());} + public void Test_frac_1000(int lhs, int rhs, int expd) {Tfds.Eq(expd, Decimal_adp_.divide_(lhs, rhs).Frac_1000());} + public void Test_comp_lt(int lhsNum, int lhsFrc, int rhs, boolean expd) {Tfds.Eq(expd, Decimal_adp_.parts_(lhsNum, lhsFrc).Comp_lt(rhs));} + public void Test_to_str_fmt(int l, int r, String fmt, String expd) {Tfds.Eq(expd, Decimal_adp_.divide_(l, r).To_str(fmt));} + public void Test_round(String raw, int places, String expd) {Tfds.Eq_str(expd, Decimal_adp_.parse(raw).Round(places).To_str(), "round");} +} diff --git a/100_core/src/gplx/Double_.java b/100_core/src/gplx/Double_.java new file mode 100644 index 000000000..421e00e75 --- /dev/null +++ b/100_core/src/gplx/Double_.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Double_ { + public static final String Cls_val_name = "double"; + public static final Class Cls_ref_type = Double.class; + public static final double + MinValue = Double.MIN_VALUE + , NaN = Double.NaN + , Inf_pos = Double.POSITIVE_INFINITY + ; + public static final byte[] + NaN_bry = Bry_.new_a7("NaN") + , Inf_pos_bry = Bry_.new_a7("INF") + ; + public static boolean IsNaN(double v) {return Double.isNaN(v);} + public static double cast(Object o) {try {return (Double)o;} catch(Exception e) {throw Err_.new_type_mismatch_w_exc(e, double.class, o);}} + public static double parse(String raw) {try {return Double.parseDouble(raw);} catch(Exception e) {throw Err_.new_parse_exc(e, double.class, raw);}} + public static double parse_or(String raw, double v) {try {return Double.parseDouble(raw);} catch(Exception e) {Err_.Noop(e); return v;}} + public static double coerce_(Object v) { + try {String s = String_.as_(v); return s == null ? Double_.cast(v) : Double_.parse(s);} + catch (Exception e) {throw Err_.new_cast(e, double.class, v);} + } + public static String To_str(double v) { + int v_int = (int)v; + return v - v_int == 0 ? Int_.To_str(v_int) : Double.toString(v); + } + public static String To_str_loose(double v) { + int v_as_int = (int)v; + return v == v_as_int + ? Int_.To_str(v_as_int) // convert to int, and call print String to eliminate any trailing decimal places + : Float_.To_str((float)v); // calling ((float)v).toString is better at removing trailing 0s than String.format("%g", v). note that .net .toString() handles it better; EX:2449.600000000000d; DATE:2014-07-29 + } + public static int Compare(double lhs, double rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } +} diff --git a/100_core/src/gplx/Double__tst.java b/100_core/src/gplx/Double__tst.java new file mode 100644 index 000000000..9a2cad0bd --- /dev/null +++ b/100_core/src/gplx/Double__tst.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Double__tst { + private Double__fxt fxt = new Double__fxt(); + @Test public void Xto_str_loose() { + fxt.Test_Xto_str_loose(2449.6000000d , "2449.6"); + fxt.Test_Xto_str_loose(623.700d , "623.7"); + } +} +class Double__fxt { + public void Test_Xto_str_loose(double v, String expd) {Tfds.Eq(expd, Double_.To_str_loose(v));} +} diff --git a/100_core/src/gplx/Enm_.java b/100_core/src/gplx/Enm_.java new file mode 100644 index 000000000..05dd6be99 --- /dev/null +++ b/100_core/src/gplx/Enm_.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Enm_ { + public static int To_int(Object enm) {return Ordinal_lang(enm);} + private static int Ordinal_lang(Object v) {return ((Enum)v).ordinal();} +} diff --git a/100_core/src/gplx/Err.java b/100_core/src/gplx/Err.java new file mode 100644 index 000000000..7f9d3b77d --- /dev/null +++ b/100_core/src/gplx/Err.java @@ -0,0 +1,87 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.errs.*; +public class Err extends RuntimeException { + private final boolean is_gplx; + private final String trace; + private Err_msg[] msgs_ary = new Err_msg[8]; private int msgs_len = 8, msgs_idx = 0; + public Err(boolean is_gplx, String trace, String type, String msg, Object... args) { + this.is_gplx = is_gplx; + this.trace = is_gplx ? Err_.Trace_lang(this) : trace; // NOTE: Err_ factory methods pass in null stack trace for gplx excs; call Stack_trace here, note that trace will not show constructor + Msgs_add(type, msg, args); + } + public boolean Logged() {return logged;} public Err Logged_y_() {logged = true; return this;} private boolean logged; + public int Trace_ignore() {return trace_ignore;} public Err Trace_ignore_add_1_() {++trace_ignore; return this;} private int trace_ignore = 0; + public Err Args_add(Object... args) {msgs_ary[msgs_idx - 1].Args_add(args); return this;} // i - 1 to get current + @gplx.Internal protected boolean Type_match(String type) { + for (int i = 0; i < msgs_len; ++i) { + if (String_.Eq(type, msgs_ary[i].Type())) return true; + } + return false; + } + @gplx.Internal protected void Msgs_add(String type, String msg, Object[] args) { + if (msgs_idx == msgs_len) { + int new_len = msgs_len * 2; + Err_msg[] new_ary = new Err_msg[new_len]; + Array_.Copy_to(msgs_ary, new_ary, 0); + this.msgs_ary = new_ary; + this.msgs_len = new_len; + } + msgs_ary[msgs_idx] = new Err_msg(type, msg, args); + ++msgs_idx; + } + public String To_str__full() {return To_str(Bool_.N, Bool_.Y);} + public String To_str__log() {return To_str(Bool_.Y, Bool_.Y);} + public String To_str__msg_only(){ + return msgs_idx == 0 ? "<>" : msgs_ary[0].To_str_wo_type(); // take 1st message only + } + public String To_str__top_wo_args() { + return msgs_idx == 0 ? "<>" : msgs_ary[0].To_str_wo_args(); + } + private String To_str(boolean called_by_log, boolean include_trace) { + String nl_str = called_by_log ? "\t" : "\n"; + String rv = ""; //nl_str + "----------------------------------------------------------------------" + nl_str; + for (int i = 0; i < msgs_idx; ++i) { + rv += "[err " + Int_.To_str(i) + "] " + String_.Replace(msgs_ary[i].To_str(), "\n", nl_str) + nl_str; + } + if (include_trace) + rv += "[trace]:" + Trace_to_str(is_gplx, called_by_log, trace_ignore, trace == null ? Err_.Trace_lang(this) : trace); + return rv; + } + @Override public String getMessage() {return To_str__msg_only();} + public static String Trace_to_str(boolean is_gplx, boolean called_by_log, int ignore_lines, String trace) { + if (trace == null) return ""; // WORKAROUND:.NET: StackTrace is only available when error is thrown; can't do "Console.Write(new Exception().StackTrace); + String[] lines = String_.Split_lang(trace, '\n'); int lines_len = lines.length; + int line_bgn = 0; + if (is_gplx) { // remove Err_.new_wo_type lines from trace for gplx exceptions + for (int i = 0; i < lines_len; ++i) { + String line = lines[i]; + if (String_.Has_at_bgn(line, "gplx.Err_.new")) continue; // ignore trace frames with "gplx.Err_.new"; EX: throw Err_.new_unimplemented + line_bgn = i + ignore_lines; + break; + } + } + // concat lines + String rv = ""; + String line_bgn_dlm = called_by_log ? "\t " : "\n "; // "\n " indents + for (int i = line_bgn; i < lines_len; ++i) + rv += line_bgn_dlm + lines[i]; + return rv; + } +} diff --git a/100_core/src/gplx/Err_.java b/100_core/src/gplx/Err_.java new file mode 100644 index 000000000..9f5482ae2 --- /dev/null +++ b/100_core/src/gplx/Err_.java @@ -0,0 +1,76 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Err_ { + private static String Type__gplx = "gplx"; @gplx.Internal protected static String Trace_null = null; + public static void Noop(Exception e) {} + public static Err as_(Object obj) {return obj instanceof Err ? (Err)obj : null;} + public static Err New(String msg, Object... args) {return new Err(Bool_.Y, Trace_null, "", String_.Format(msg, args));} + public static Err new_(String type, String msg, Object... args) {return new Err(Bool_.Y, Trace_null, type, msg, args);} + public static Err new_wo_type(String msg, Object... args) {return new Err(Bool_.Y, Trace_null, Type__gplx, msg, args);} + public static Err new_exc(Exception e, String type, String msg, Object... args) { + Err rv = cast_or_make(e); + rv.Msgs_add(type, msg, args); + return rv; + } + public static Err new_unhandled(Object val) {return new Err(Bool_.Y, Trace_null, Type__gplx, "val is not in switch/if", "val", val);} + public static Err new_unhandled_default(Object val) {return new Err(Bool_.Y, Trace_null, Type__gplx, "val is not in switch", "val", val);} + public static Err new_unsupported() {return new Err(Bool_.Y, Trace_null, Type__gplx, "method not supported");} + public static Err new_unimplemented() {return new Err(Bool_.Y, Trace_null, Type__gplx, "method not implemented");} + public static Err new_unimplemented_w_msg(String msg, Object... args) {return new Err(Bool_.Y, Trace_null, Type__gplx, msg, args);} + public static Err new_deprecated(String s) {return new Err(Bool_.Y, Trace_null, Type__gplx, "deprecated", "method", s);} + public static Err new_parse_type(Class c, String raw) {return new_parse(Type_adp_.FullNameOf_type(c), raw);} + public static Err new_parse_exc(Exception e, Class c, String raw) {return new_parse(Type_adp_.FullNameOf_type(c), raw).Args_add("e", Err_.Message_lang(e));} + public static Err new_parse(String type, String raw) {return new Err(Bool_.Y, Trace_null, Type__gplx, "parse failed", "type", type, "raw", raw);} + public static Err new_null() {return new Err(Bool_.Y, Trace_null, Type__gplx, "null obj");} + public static Err new_null(String arg) {return new Err(Bool_.Y, Trace_null, Type__gplx, "null obj", "arg", arg);} + public static Err new_missing_idx(int idx, int len) {return new Err(Bool_.Y, Trace_null, Type__gplx, "index is out of bounds", "idx", idx, "len", len);} + public static Err new_missing_key(String key) {return new Err(Bool_.Y, Trace_null, Type__gplx, "key not found", "key", key);} + public static Err new_invalid_op(String msg) {return new Err(Bool_.Y, Trace_null, Type__gplx, msg);} + public static Err new_invalid_arg(String msg, Object... args) {return new Err(Bool_.Y, Trace_null, Type__gplx, msg, args);} + public static Err new_op_canceled() {return new Err(Bool_.Y, Trace_null, Type__op_canceled, "canceled by usr");} + public static Err new_type_mismatch_w_exc(Exception ignore, Class t, Object o) {return new_type_mismatch(t, o);} + public static Err new_type_mismatch(Class t, Object o) {return new Err(Bool_.Y, Trace_null, Type__gplx, "type mismatch", "expdType", Type_adp_.FullNameOf_type(t), "actlType", Type_adp_.NameOf_obj(o), "actlObj", Object_.Xto_str_strict_or_null_mark(o));} + public static Err new_cast(Exception ignore, Class t, Object o) { + String o_str = ""; + try {o_str = Object_.Xto_str_strict_or_null_mark(o);} + catch (Exception e) {o_str = ""; Err_.Noop(e);} + return new Err(Bool_.Y, Trace_null, Type__gplx, "cast failed", "type", Type_adp_.NameOf_type(t), "obj", o_str); + } + + public static String Message_lang(Throwable e) {return e.getMessage();} + public static String To_str(Exception e) {return e.toString();} // e.getMessage() is sometimes null? + public static String Trace_lang(Throwable e) {return Trace_lang_exec(e.getStackTrace());} + private static String Trace_lang_exec(StackTraceElement[] ary) { + String rv = ""; + int len = ary.length; + for (int i = 0; i < len; i++) { + if (i != 0) rv += "\n"; + rv += ary[i].toString(); + } + return rv; + } + public static boolean Type_match(Exception e, String type) { + Err exc = Err_.as_(e); + return exc == null ? false : exc.Type_match(type); + } + public static String Message_gplx_full(Exception e) {return cast_or_make(e).To_str__full();} + public static String Message_gplx_log(Exception e) {return cast_or_make(e).To_str__log();} + public static Err cast_or_make(Throwable e) {return Type_adp_.Eq_typeSafe(e, Err.class) ? (Err)e : new Err(Bool_.N, Err_.Trace_lang(e), Type_adp_.NameOf_obj(e), Err_.Message_lang(e));} + public static final String Type__op_canceled = "gplx.op_canceled"; +} diff --git a/100_core/src/gplx/Err_tst.java b/100_core/src/gplx/Err_tst.java new file mode 100644 index 000000000..af3f55028 --- /dev/null +++ b/100_core/src/gplx/Err_tst.java @@ -0,0 +1,47 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Err_tst { + private final Err_fxt fxt = new Err_fxt(); + @Test public void Trace_to_str__gplx() { + fxt.Test_Trace_to_str(Bool_.Y, Bool_.N, 0, String_.Concat_lines_nl_skip_last + ( "gplx.Err_.new_wo_type(Err_.java:1)" // ignore this line + , "gplx.String_.Len(String_.java:2)" + ), String_.Concat_lines_nl_skip_last + ( "" + , " gplx.String_.Len(String_.java:2)" + )); + } + @Test public void Trace_to_str__gplx_ignore() { + fxt.Test_Trace_to_str(Bool_.Y, Bool_.N, 1, String_.Concat_lines_nl_skip_last + ( "gplx.Err_.new_wo_type(Err_.java:1)" // ignore this line + , "gplx.String_.Fail(String_.java:2)" // ignore this line also + , "gplx.String_.Len(String_.java:3)" + ), String_.Concat_lines_nl_skip_last + ( "" + , " gplx.String_.Len(String_.java:3)" + )); + } +} +class Err_fxt { + public void Test_Trace_to_str(boolean is_gplx, boolean called_by_log, int ignore_lines, String trace, String expd) { + String actl = Err.Trace_to_str(is_gplx, called_by_log, ignore_lines, trace); + Tfds.Eq_str_lines(expd, actl); + } +} diff --git a/100_core/src/gplx/Float_.java b/100_core/src/gplx/Float_.java new file mode 100644 index 000000000..b8e06df9e --- /dev/null +++ b/100_core/src/gplx/Float_.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Float_ { + public static final String Cls_val_name = "float"; + public static final Class Cls_ref_type = Float.class; + public static final float NaN = Float.NaN;; + public static boolean IsNaN(float v) {return Float.isNaN(v);} + public static float cast(Object obj) {try {return (Float)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, float.class, obj);}} + public static float parse(String raw) {try {return Float.parseFloat(raw);} catch(Exception exc) {throw Err_.new_parse_exc(exc, float.class, raw);}} + public static int Compare(float lhs, float rhs) { + if ( lhs == rhs) return CompareAble_.Same; + else if ( lhs < rhs) return CompareAble_.Less; + else /*lhs > rhs*/ return CompareAble_.More; + } + public static String To_str(float v) { + int v_int = (int)v; + return v - v_int == 0 ? Int_.To_str(v_int) : Float.toString(v); + } + public static float Div(int val, int divisor) {return (float)val / (float)divisor;} + public static float Div(long val, long divisor) {return (float)val / (float)divisor;} + public static int RoundUp(float val) { + int rv = (int)val; + return (rv == val) ? rv : rv + 1; + } +} diff --git a/100_core/src/gplx/GfoMsg.java b/100_core/src/gplx/GfoMsg.java new file mode 100644 index 000000000..49a6b9365 --- /dev/null +++ b/100_core/src/gplx/GfoMsg.java @@ -0,0 +1,72 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.interfaces.*; +public interface GfoMsg { + String Key(); + GfoMsg CloneNew(); + String To_str(); + GfoMsg Clear(); + GfoMsg Parse_(boolean v); + + int Args_count(); + Keyval Args_getAt(int i); + GfoMsg Args_ovr(String k, Object v); + void Args_reset(); + GfoMsg Add(String k, Object v); + int Subs_count(); + GfoMsg Subs_getAt(int i); + GfoMsg Subs_add(GfoMsg m); + GfoMsg Subs_(GfoMsg... ary); + + boolean ReadBool(String k); + boolean ReadBoolOr(String k, boolean or); + boolean ReadBoolOrFalse(String k); + boolean ReadBoolOrTrue(String k); + int ReadInt(String k); + int ReadIntOr(String k, int or); + long ReadLong(String k); + long ReadLongOr(String k, long or); + float ReadFloat(String k); + float ReadFloatOr(String k, float or); + double ReadDouble(String k); + double ReadDoubleOr(String k, double or); + DateAdp ReadDate(String k); + DateAdp ReadDateOr(String k, DateAdp or); + Decimal_adp ReadDecimal(String k); + Decimal_adp ReadDecimalOr(String k, Decimal_adp or); + String ReadStr(String k); + String ReadStrOr(String k, String or); + Io_url ReadIoUrl(String k); + Io_url ReadIoUrlOr(String k, Io_url url); + boolean ReadYn(String k); + boolean ReadYn_toggle(String k, boolean cur); + boolean ReadYnOrY(String k); + byte ReadByte(String k); + byte[] ReadBry(String k); + byte[] ReadBryOr(String k, byte[] or); + Object ReadObj(String k); + Object ReadObj(String k, ParseAble parseAble); + Object ReadObjOr(String k, ParseAble parseAble, Object or); + String[]ReadStrAry(String k, String spr); + String[]ReadStrAryIgnore(String k, String spr, String ignore); + byte[][]ReadBryAry(String k, byte spr); + Object ReadValAt(int i); + Object CastObj(String k); + Object CastObjOr(String k, Object or); +} diff --git a/100_core/src/gplx/GfoMsgUtl.java b/100_core/src/gplx/GfoMsgUtl.java new file mode 100644 index 000000000..4bfea3995 --- /dev/null +++ b/100_core/src/gplx/GfoMsgUtl.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class GfoMsgUtl { + public static int SetInt(GfsCtx ctx, GfoMsg m, int cur) {return ctx.Deny() ? cur : m.ReadIntOr("v", cur);} + public static boolean SetBool(GfsCtx ctx, GfoMsg m, boolean cur) {return ctx.Deny() ? cur : m.ReadBoolOr("v", cur);} + public static String SetStr(GfsCtx ctx, GfoMsg m, String cur) {return ctx.Deny() ? cur : m.ReadStrOr("v", cur);} + public static Io_url SetIoUrl(GfsCtx ctx, GfoMsg m, Io_url cur) {return ctx.Deny() ? cur : m.ReadIoUrlOr("v", cur);} + public static Decimal_adp SetDecimal(GfsCtx ctx, GfoMsg m, Decimal_adp cur) {return ctx.Deny() ? cur : m.ReadDecimalOr("v", cur);} +} diff --git a/100_core/src/gplx/GfoMsg_.java b/100_core/src/gplx/GfoMsg_.java new file mode 100644 index 000000000..8076ec244 --- /dev/null +++ b/100_core/src/gplx/GfoMsg_.java @@ -0,0 +1,270 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; import gplx.core.strings.*; import gplx.core.brys.*; import gplx.core.interfaces.*; +public class GfoMsg_ { + public static GfoMsg as_(Object obj) {return obj instanceof GfoMsg ? (GfoMsg)obj : null;} + public static final GfoMsg Null = new GfoMsg_base().ctor_("<>", false); + public static GfoMsg new_parse_(String key) {return new GfoMsg_base().ctor_(key, true);} + public static GfoMsg new_cast_(String key) {return new GfoMsg_base().ctor_(key, false);} + public static GfoMsg srl_(GfoMsg owner, String key) { + GfoMsg rv = new_parse_(key); + owner.Subs_add(rv); + return rv; + } + public static GfoMsg root_(String... ary) {return root_leafArgs_(ary);} + public static GfoMsg root_leafArgs_(String[] ary, Keyval... kvAry) { + int len = Array_.Len(ary); if (len == 0) throw Err_.new_invalid_arg("== 0", "@len", len); + GfoMsg root = new GfoMsg_base().ctor_(ary[0], false); + GfoMsg owner = root; + for (int i = 1; i < len; i++) { + String key = ary[i]; + GfoMsg cur = new GfoMsg_base().ctor_(key, false); + owner.Subs_add(cur); + owner = cur; + } + for (int i = 0; i < kvAry.length; i++) { + Keyval kv = kvAry[i]; + owner.Add(kv.Key(), kv.Val()); + } + return root; + } + public static GfoMsg chain_(GfoMsg owner, String key) { + GfoMsg sub = owner; + List_adp list = List_adp_.New(); + list.Add(sub.Key()); + while (sub != null) { + if (sub.Subs_count() == 0) break; + sub = (GfoMsg)sub.Subs_getAt(0); + list.Add(sub.Key()); + } + list.Add(key); + + GfoMsg root = GfoMsg_.new_parse_((String)list.Get_at(0)); + GfoMsg cur = root; + for (int i = 1; i < list.Count(); i++) { + String k = (String)list.Get_at(i); + GfoMsg mm = GfoMsg_.new_parse_(k); + cur.Subs_add(mm); + cur = mm; + } + return root; + } + public static GfoMsg wtr_() {return new GfoMsg_wtr().ctor_("", false);} + public static GfoMsg rdr_(String cmd) {return new GfoMsg_rdr().ctor_(cmd, false);} + public static GfoMsg basic_(String cmd, Object... vals) { + GfoMsg rv = new_cast_(cmd); + int len = vals.length; + for (int i = 0; i < len; i++) + rv.Add("", vals[i]); + return rv; + } +} +class GfoMsg_wtr extends GfoMsg_base { + @Override protected Object ReadOr(String k, Object defaultOr) { + if (args == null) args = List_adp_.New(); + args.Add(Keyval_.new_(k, null)); + return defaultOr; + } +} +class GfoMsg_rdr extends GfoMsg_base { + @Override protected Object ReadOr(String k, Object defaultOr) { + if (args == null) args = List_adp_.New(); + args.Add(Keyval_.new_(k, defaultOr)); + return defaultOr; + } +} +class GfoMsg_base implements GfoMsg { + public String Key() {return key;} private String key; + public int Subs_count() {return subs == null ? 0 : subs.Count();} + public GfoMsg Subs_getAt(int i) {return subs == null ? null : (GfoMsg)subs.Get_at(i);} + public GfoMsg Subs_add(GfoMsg m) {if (subs == null) subs = List_adp_.New(); subs.Add(m); return this;} + public GfoMsg Subs_(GfoMsg... ary) {for (GfoMsg m : ary) Subs_add(m); return this;} + public int Args_count() {return args == null ? 0 : args.Count();} + public void Args_reset() { + counter = 0; + Args_reset(this); + } + public GfoMsg Clear() { + this.Args_reset(); + if (subs != null) subs.Clear(); + if (args != null) args.Clear(); + return this; + } + static void Args_reset(GfoMsg owner) { + int len = owner.Subs_count(); + for (int i = 0; i < len; i++) { + GfoMsg sub = owner.Subs_getAt(i); + sub.Args_reset(); + } + } + public Keyval Args_getAt(int i) {return args == null ? null : (Keyval)args.Get_at(i);} + public GfoMsg Args_ovr(String k, Object v) { + if (args == null) args = List_adp_.New(); + for (int i = 0; i < args.Count(); i++) { + Keyval kv = (Keyval)args.Get_at(i); + if (String_.Eq(k, kv.Key())) { + kv.Val_(v); + return this; + } + } + args.Add(Keyval_.new_(k, v)); + return this; + } + public GfoMsg Parse_(boolean v) {parse = v; return this;} + public GfoMsg Add(String k, Object v) { + if (args == null) args = List_adp_.New(); + args.Add(Keyval_.new_(k, v)); + return this; + } + public boolean ReadBool(String k) {Object rv = ReadOr(k,false); if (rv == Nil) ThrowNotFound(k); return parse ? Yn.parse_or((String)rv, false) : Bool_.cast(rv);} + public int ReadInt(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Int_.parse((String)rv) : Int_.cast(rv);} + public byte ReadByte(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Byte_.parse((String)rv) : Byte_.cast(rv);} + public long ReadLong(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Long_.parse((String)rv) : Long_.cast(rv);} + public float ReadFloat(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Float_.parse((String)rv) : Float_.cast(rv);} + public double ReadDouble(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Double_.parse((String)rv) : Double_.cast(rv);} + public Decimal_adp ReadDecimal(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Decimal_adp_.parse((String)rv) : Decimal_adp_.cast(rv);} + public String ReadStr(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return (String)rv;} + public DateAdp ReadDate(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return parse ? DateAdp_.parse_gplx((String)rv) : DateAdp_.cast(rv);} + public Io_url ReadIoUrl(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return parse ? Io_url_.new_any_((String)rv) : Io_url_.cast(rv);} + public Object CastObj(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return rv;} + public boolean ReadBoolOr(String k, boolean or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Yn.parse_or((String)rv, or) : Bool_.cast(rv);} + public int ReadIntOr(String k, int or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Int_.parse((String)rv) : Int_.cast(rv);} + public long ReadLongOr(String k, long or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Long_.parse((String)rv) : Long_.cast(rv);} + public float ReadFloatOr(String k, float or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Float_.parse((String)rv) : Float_.cast(rv);} + public double ReadDoubleOr(String k,double or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Double_.parse((String)rv) : Double_.cast(rv);} + public Decimal_adp ReadDecimalOr(String k,Decimal_adp or) {Object rv = ReadOr(k, or); if (rv == Nil) return or ; return parse ? Decimal_adp_.parse((String)rv) : Decimal_adp_.cast(rv);} + public String ReadStrOr(String k, String or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return (String)rv;} + public DateAdp ReadDateOr(String k, DateAdp or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? DateAdp_.parse_gplx((String)rv) : DateAdp_.cast(rv);} + public Io_url ReadIoUrlOr(String k, Io_url or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Io_url_.new_any_((String)rv) : Io_url_.cast(rv);} + public boolean ReadBoolOrFalse(String k) {Object rv = ReadOr(k,false); if (rv == Nil) return false ; return parse ? Yn.parse_or((String)rv, false) : Bool_.cast(rv);} + public boolean ReadBoolOrTrue(String k) {Object rv = ReadOr(k, true); if (rv == Nil) return true ; return parse ? Yn.parse_or((String)rv, true) : Bool_.cast(rv);} + public boolean ReadYnOrY(String k) {Object rv = ReadOr(k, true); if (rv == Nil) return true ; return parse ? Yn.parse_or((String)rv, true) : Bool_.cast(rv);} + public boolean ReadYn(String k) {Object rv = ReadOr(k,false); if (rv == Nil) ThrowNotFound(k); return parse ? Yn.parse_or((String)rv, false) : Yn.coerce_(rv);} + public boolean ReadYn_toggle(String k, boolean cur) { + Object rv = ReadOr(k, "!"); + if (rv == Nil) ThrowNotFound(k); + if (!parse) throw Err_.new_wo_type("only parse supported"); + String rv_str = (String)rv; + return (String_.Eq(rv_str, "!")) ? !cur : Yn.parse(rv_str); + } + public byte[] ReadBry(String k) {Object rv = ReadOr(k,false); if (rv == Nil) ThrowNotFound(k); return parse ? Bry_.new_u8((String)rv) : (byte[])rv;} + public byte[] ReadBryOr(String k, byte[] or) {Object rv = ReadOr(k, or); if (rv == Nil) return or; return parse ? Bry_.new_u8((String)rv) : (byte[])rv;} + public Object CastObjOr(String k, Object or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return rv;} + public Object ReadObj(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return rv;} + public Object ReadObj(String k, ParseAble parseAble) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return parse ? parseAble.ParseAsObj((String)rv) : rv;} + public Object ReadObjOr(String k, ParseAble parseAble, Object or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? parseAble.ParseAsObj((String)rv) : rv;} + public String[] ReadStrAry(String k, String spr) {return String_.Split(ReadStr(k), spr);} + public byte[][] ReadBryAry(String k, byte spr) {return Bry_split_.Split(ReadBry(k), spr);} + public String[] ReadStrAryIgnore(String k, String spr, String ignore) {return String_.Split(String_.Replace(ReadStr(k), ignore, ""), spr);} + public Object ReadValAt(int i) {return Args_getAt(i).Val();} + @gplx.Virtual protected Object ReadOr(String k, Object defaultOr) { + if (args == null) return Nil; // WORKAROUND.gfui: args null for DataBndr_whenEvt_execCmd + if (!String_.Eq(k, "")) { + for (int i = 0; i < args.Count(); i++) { + Keyval kv = (Keyval)args.Get_at(i); + if (String_.Eq(k, kv.Key())) return kv.Val(); + } + } + if (counter >= args.Count()) return Nil; + for (int i = 0; i < args.Count(); i++) { + Keyval kv = (Keyval)args.Get_at(i); + if (String_.Eq(kv.Key(), "") && i >= counter) { + counter++; + return kv.Val(); + } + } + return Nil; + } int counter = 0; + void ThrowNotFound(String k) {throw Err_.new_wo_type("arg not found in msg", "k", k, "counter", counter, "args", args);} + String ArgsXtoStr() { + if (this.Args_count() == 0) return "<>"; + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < this.Args_count(); i++) { + Keyval rv = (Keyval)this.Args_getAt(i); + sb.Add_fmt("{0};", rv.Key()); + } + return sb.To_str(); + } + public GfoMsg CloneNew() { + GfoMsg_base rv = new GfoMsg_base().ctor_(key, parse); + if (args != null) { + rv.args = List_adp_.New(); + for (int i = 0; i < args.Count(); i++) + rv.args.Add(args.Get_at(i)); + } + if (subs != null) { + rv.subs = List_adp_.New(); + for (int i = 0; i < args.Count(); i++) { + GfoMsg sub = (GfoMsg)args.Get_at(i); + rv.subs.Add(sub.CloneNew()); // NOTE: recursion + } + } + return rv; + } + + protected List_adp args; + List_adp subs; + public String To_str() { + String_bldr sb = String_bldr_.new_(); + To_str(sb, new XtoStrWkr_gplx(), this); + return sb.To_str_and_clear(); + } + void To_str(String_bldr sb, XtoStrWkr wkr, GfoMsg m) { + sb.Add(m.Key()); + if (m.Subs_count() == 0) { + sb.Add(":"); + boolean first = true; + for (int i = 0; i < m.Args_count(); i++) { + Keyval kv = m.Args_getAt(i); + if (kv.Val() == null) continue; + if (!first) sb.Add(" "); + sb.Add(kv.Key()); + sb.Add("='"); + sb.Add(wkr.To_str(kv.Val())); + sb.Add("'"); + first = false; + } + sb.Add(";"); + } + else { + sb.Add("."); + To_str(sb, wkr, m.Subs_getAt(0)); + } + } + + public GfoMsg_base ctor_(String key, boolean parse) {this.key = key; this.parse = parse; return this;} private boolean parse; + @gplx.Internal protected GfoMsg_base(){} + static final String_obj_val Nil = String_obj_val.new_("<>"); +} +interface XtoStrWkr { + String To_str(Object o); +} +class XtoStrWkr_gplx implements XtoStrWkr { + public String To_str(Object o) { + if (o == null) return "<>"; + Class type = Type_adp_.ClassOf_obj(o); + String rv = null; + if (type == String.class) rv = String_.cast(o); + else if (Int_.TypeMatch(type)) return Int_.To_str(Int_.cast(o)); + else if (Type_adp_.Eq(type, Bool_.Cls_ref_type)) return Yn.To_str(Bool_.cast(o)); + else if (type == DateAdp.class) return DateAdp_.cast(o).XtoStr_gplx(); + else rv = Object_.Xto_str_strict_or_empty(o); + return String_.Replace(rv, "'", "''"); + } +} diff --git a/100_core/src/gplx/GfoMsg_tst.java b/100_core/src/gplx/GfoMsg_tst.java new file mode 100644 index 000000000..964c9f0c8 --- /dev/null +++ b/100_core/src/gplx/GfoMsg_tst.java @@ -0,0 +1,51 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; import gplx.langs.gfs.*; +public class GfoMsg_tst { + @Before public void setup() { + GfsCore.Instance.AddObj(new Mok(), "Mok"); + } + @Test public void Write1() { + GfoMsg m = GfoMsg_.root_leafArgs_(String_.Ary("a", "b"), Keyval_.new_("int0", 1)); + tst_Msg(m, "a.b:int0='1';"); + } + @Test public void Write() { + Mok mok = new Mok(); + tst_Msg(Gfo_invk_to_str.WriteMsg(mok, Mok.Invk_Cmd0, true, 1, "a"), "Mok.Cmd0:bool0='y' int0='1' str0='a';"); + mok.Int0 = 2; + mok.Bool0 = true; + mok.Str0 = "b"; + tst_Msg(Gfo_invk_to_str.ReadMsg(mok, Mok.Invk_Cmd0), "Mok.Cmd0:bool0='y' int0='2' str0='b';"); + } + void tst_Msg(GfoMsg m, String expd) {Tfds.Eq(expd, m.To_str());} + class Mok implements Gfo_invk { + public boolean Bool0; + public int Int0; + public String Str0; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Cmd0)) { + Bool0 = m.ReadBoolOr("bool0", Bool0); + Int0 = m.ReadIntOr("int0", Int0); + Str0 = m.ReadStrOr("str0", Str0); + if (ctx.Deny()) return this; + } + return this; + } public static final String Invk_Cmd0 = "Cmd0"; + } +} diff --git a/100_core/src/gplx/GfoTemplate.java b/100_core/src/gplx/GfoTemplate.java new file mode 100644 index 000000000..fb301e880 --- /dev/null +++ b/100_core/src/gplx/GfoTemplate.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface GfoTemplate { + Object NewCopy(GfoTemplate template); +} diff --git a/100_core/src/gplx/GfoTemplateFactory.java b/100_core/src/gplx/GfoTemplateFactory.java new file mode 100644 index 000000000..3e94f5027 --- /dev/null +++ b/100_core/src/gplx/GfoTemplateFactory.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class GfoTemplateFactory implements Gfo_invk { + public void Reg(String key, GfoTemplate template) {hash.Add(key, template);} + public Object Make(String key) { + GfoTemplate template = (GfoTemplate)hash.Get_by(key); + return template.NewCopy(template); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + ctx.Match(k, k); + Object o = hash.Get_by(k); + return o == null ? Gfo_invk_.Rv_unhandled : o; + } + public static final GfoTemplateFactory Instance = new GfoTemplateFactory(); GfoTemplateFactory() {} + Hash_adp hash = Hash_adp_.New(); +} diff --git a/100_core/src/gplx/Gfo_evt_itm.java b/100_core/src/gplx/Gfo_evt_itm.java new file mode 100644 index 000000000..f1152fb11 --- /dev/null +++ b/100_core/src/gplx/Gfo_evt_itm.java @@ -0,0 +1,19 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_evt_itm extends Gfo_invk, Gfo_evt_mgr_owner {} diff --git a/100_core/src/gplx/Gfo_evt_mgr.java b/100_core/src/gplx/Gfo_evt_mgr.java new file mode 100644 index 000000000..05c6f767c --- /dev/null +++ b/100_core/src/gplx/Gfo_evt_mgr.java @@ -0,0 +1,126 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.lists.*; +public class Gfo_evt_mgr { + private final Gfo_evt_mgr_owner sender; private Ordered_hash subsRegy, pubsRegy; + public Gfo_evt_mgr(Gfo_evt_mgr_owner sender) {this.sender = sender;} + @gplx.Internal protected void AddSub(Gfo_evt_mgr_owner pub, String pubEvt, Gfo_evt_itm sub, String subPrc) { + GfoEvLnk lnk = new GfoEvLnk(pub, pubEvt, sub, subPrc); + if (subsRegy == null) subsRegy = Ordered_hash_.New(); + AddInList(subsRegy, pubEvt, lnk); + sub.Evt_mgr().AddPub(pubEvt, lnk); + } + @gplx.Internal protected void Lnk(Gfo_evt_mgr_owner pub) { + if (pub.Evt_mgr().lnks == null) pub.Evt_mgr().lnks = List_adp_.New(); + pub.Evt_mgr().lnks.Add(this); + } List_adp lnks; + void AddInList(Ordered_hash regy, String key, GfoEvLnk lnk) { + GfoEvLnkList list = (GfoEvLnkList)regy.Get_by(key); + if (list == null) { + list = new GfoEvLnkList(key); + regy.Add(key, list); + } + list.Add(lnk); + } + @gplx.Internal protected void AddPub(String pubEvt, GfoEvLnk lnk) { + if (pubsRegy == null) pubsRegy = Ordered_hash_.New(); + AddInList(pubsRegy, pubEvt, lnk); + } + @gplx.Internal protected void Pub(GfsCtx ctx, String evt, GfoMsg m) { + ctx.MsgSrc_(sender); + GfoEvLnkList subs = subsRegy == null ? null : (GfoEvLnkList)subsRegy.Get_by(evt); + if (subs != null) { + for (int i = 0; i < subs.Count(); i++) { + GfoEvLnk lnk = (GfoEvLnk)subs.Get_at(i); + lnk.Sub().Invk(ctx, 0, lnk.SubPrc(), m); // NOTE: itm.Key() needed for Subscribe_diff() + } + } + if (lnks != null) { + for (int i = 0; i < lnks.Count(); i++) { + Gfo_evt_mgr lnk = (Gfo_evt_mgr)lnks.Get_at(i); + lnk.Pub(ctx, evt, m); + } + } + } + @gplx.Internal protected void RlsSub(Gfo_evt_mgr_owner eobj) { + RlsRegyObj(pubsRegy, eobj, true); + RlsRegyObj(subsRegy, eobj, false); + } + @gplx.Internal protected void RlsPub(Gfo_evt_mgr_owner eobj) { + RlsRegyObj(pubsRegy, eobj, true); + RlsRegyObj(subsRegy, eobj, false); + } + @gplx.Internal protected void RlsRegyObj(Ordered_hash regy, Gfo_evt_mgr_owner eobj, boolean pub) { + if (regy == null) return; + List_adp delList = List_adp_.New(); + for (int i = 0; i < regy.Count(); i++) { + GfoEvLnkList pubsList = (GfoEvLnkList)regy.Get_at(i); + delList.Clear(); + for (int j = 0; j < pubsList.Count(); j++) { + GfoEvLnk lnk = (GfoEvLnk)pubsList.Get_at(j); + if (lnk.End(!pub) == eobj) delList.Add(lnk); + } + for (int j = 0; j < delList.Count(); j++) { + GfoEvLnk del = (GfoEvLnk)delList.Get_at(j); + del.End(pub).Evt_mgr().RlsLnk(!pub, pubsList.Key(), del.End(!pub)); + pubsList.Del(del); + } + } + } + @gplx.Internal protected void RlsLnk(boolean pubEnd, String key, Gfo_evt_mgr_owner endObj) { + Ordered_hash regy = pubEnd ? pubsRegy : subsRegy; + GfoEvLnkList list = (GfoEvLnkList)regy.Get_by(key); + List_adp delList = List_adp_.New(); + for (int i = 0; i < list.Count(); i++) { + GfoEvLnk lnk = (GfoEvLnk)list.Get_at(i); + if (lnk.End(pubEnd) == endObj) delList.Add(lnk); + } + for (int i = 0; i < delList.Count(); i++) { + GfoEvLnk lnk = (GfoEvLnk)delList.Get_at(i); + list.Del(lnk); + } + delList.Clear(); + } +} +class GfoEvLnkList { + public String Key() {return key;} private String key; + public int Count() {return list.Count();} + public void Add(GfoEvLnk lnk) {list.Add(lnk);} + public void Del(GfoEvLnk lnk) {list.Del(lnk);} + public GfoEvLnk Get_at(int i) {return (GfoEvLnk)list.Get_at(i);} + public GfoEvLnkList(String key) {this.key = key;} + List_adp list = List_adp_.New(); +} +class GfoEvLnk { + public Gfo_evt_mgr_owner Pub() {return pub;} Gfo_evt_mgr_owner pub; + public String PubEvt() {return pubEvt;} private String pubEvt; + public Gfo_evt_itm Sub() {return sub;} Gfo_evt_itm sub; + public String SubPrc() {return subPrc;} private String subPrc; + public Gfo_evt_mgr_owner End(boolean pubEnd) {return pubEnd ? pub : sub;} + public GfoEvLnk(Gfo_evt_mgr_owner pub, String pubEvt, Gfo_evt_itm sub, String subPrc) {this.pub = pub; this.pubEvt = pubEvt; this.sub = sub; this.subPrc = subPrc;} +} +class GfoEvItm { + public String Key() {return key;} private String key; + public Gfo_invk InvkAble() {return invkAble;} Gfo_invk invkAble; + public static GfoEvItm new_(Gfo_invk invkAble, String key) { + GfoEvItm rv = new GfoEvItm(); + rv.invkAble = invkAble; rv.key = key; + return rv; + } +} diff --git a/100_core/src/gplx/Gfo_evt_mgr_.java b/100_core/src/gplx/Gfo_evt_mgr_.java new file mode 100644 index 000000000..5a3e60c03 --- /dev/null +++ b/100_core/src/gplx/Gfo_evt_mgr_.java @@ -0,0 +1,45 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Gfo_evt_mgr_ { + public static void Sub(Gfo_evt_mgr_owner pub, String pubEvt, Gfo_evt_itm sub, String subEvt) {pub.Evt_mgr().AddSub(pub, pubEvt, sub, subEvt);} + public static void Sub_same(Gfo_evt_mgr_owner pub, String evt, Gfo_evt_itm sub) {pub.Evt_mgr().AddSub(pub, evt, sub, evt);} + public static void Sub_same_many(Gfo_evt_mgr_owner pub, Gfo_evt_itm sub, String... evts) { + int len = evts.length; + for (int i = 0; i < len; i++) { + String evt = evts[i]; + pub.Evt_mgr().AddSub(pub, evt, sub, evt); + } + } + public static void Pub(Gfo_evt_mgr_owner pub, String pubEvt) {pub.Evt_mgr().Pub(GfsCtx.new_(), pubEvt, GfoMsg_.new_cast_(pubEvt));} + public static void Pub_obj(Gfo_evt_mgr_owner pub, String pubEvt, String key, Object v) {pub.Evt_mgr().Pub(GfsCtx.new_(), pubEvt, msg_(pubEvt, Keyval_.new_(key, v)));} + public static void Pub_val(Gfo_evt_mgr_owner pub, String pubEvt, Object v) {pub.Evt_mgr().Pub(GfsCtx.new_(), pubEvt, msg_(pubEvt, Keyval_.new_("v", v)));} + public static void Pub_vals(Gfo_evt_mgr_owner pub, String pubEvt, Keyval... ary) {pub.Evt_mgr().Pub(GfsCtx.new_(), pubEvt, msg_(pubEvt, ary));} + public static void Pub_msg(Gfo_evt_mgr_owner pub, GfsCtx ctx, String pubEvt, GfoMsg m) {pub.Evt_mgr().Pub(ctx, pubEvt, m);} + public static void Lnk(Gfo_evt_mgr_owner pub, Gfo_evt_mgr_owner sub) {sub.Evt_mgr().Lnk(pub);} + public static void Rls_pub(Gfo_evt_mgr_owner pub) {pub.Evt_mgr().RlsPub(pub);} + public static void Rls_sub(Gfo_evt_mgr_owner sub) {sub.Evt_mgr().RlsSub(sub);} + static GfoMsg msg_(String evt, Keyval... kvAry) { + GfoMsg m = GfoMsg_.new_cast_(evt); + for (int i = 0; i < kvAry.length; i++) { + Keyval kv = kvAry[i]; + m.Add(kv.Key(), kv.Val()); + } + return m; + } +} diff --git a/100_core/src/gplx/Gfo_evt_mgr_owner.java b/100_core/src/gplx/Gfo_evt_mgr_owner.java new file mode 100644 index 000000000..8f915f3c7 --- /dev/null +++ b/100_core/src/gplx/Gfo_evt_mgr_owner.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_evt_mgr_owner { + Gfo_evt_mgr Evt_mgr(); +} diff --git a/100_core/src/gplx/Gfo_evt_mgr_tst.java b/100_core/src/gplx/Gfo_evt_mgr_tst.java new file mode 100644 index 000000000..9652b50f9 --- /dev/null +++ b/100_core/src/gplx/Gfo_evt_mgr_tst.java @@ -0,0 +1,69 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Gfo_evt_mgr_tst { + @Before public void setup() { + pub = make_(); sub = make_(); + } MockEvObj pub, sub; + @Test public void Basic() { + Gfo_evt_mgr_.Sub_same(pub, "ev1", sub); + Gfo_evt_mgr_.Pub_val(pub, "ev1", "val1"); + sub.tst_Handled("val1"); + } + @Test public void None() {// make sure no subscribers does not cause exception + Gfo_evt_mgr_.Sub_same(pub, "ev1", sub); + Gfo_evt_mgr_.Pub_val(pub, "ev2", "val1"); //ev2 does not exist + sub.tst_Handled(); + } + @Test public void Lnk() { + MockEvObj mid = make_(); + mid.Evt_mgr().Lnk(pub); + Gfo_evt_mgr_.Sub_same(mid, "ev1", sub); + Gfo_evt_mgr_.Pub_val(pub, "ev1", "val1"); + sub.tst_Handled("val1"); + } + @Test public void RlsSub() { + this.Basic(); + + Gfo_evt_mgr_.Rls_sub(sub); + Gfo_evt_mgr_.Pub_val(pub, "ev1", "val1"); + sub.tst_Handled(); + } + @Test public void RlsPub() { + this.Basic(); + + Gfo_evt_mgr_.Rls_sub(pub); + Gfo_evt_mgr_.Pub_val(pub, "ev1", "val1"); + sub.tst_Handled(); + } + MockEvObj make_() {return new MockEvObj();} +} +class MockEvObj implements Gfo_evt_itm { + public Gfo_evt_mgr Evt_mgr() {return eventMgr;} Gfo_evt_mgr eventMgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + handled.Add(m.ReadStr("v")); + return this; + } + List_adp handled = List_adp_.New(); + public void tst_Handled(String... expd) { + Tfds.Eq_ary_str(expd, handled.To_str_ary()); + handled.Clear(); + } + public MockEvObj(){eventMgr = new Gfo_evt_mgr(this);} +} diff --git a/100_core/src/gplx/Gfo_invk.java b/100_core/src/gplx/Gfo_invk.java new file mode 100644 index 000000000..0888abb3a --- /dev/null +++ b/100_core/src/gplx/Gfo_invk.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_invk { + Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m); +} +/* + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk__set)) {} + else return Gfo_invk_.Rv_unhandled; + return this; + } private static final String Invk__set = "set"; +*/ diff --git a/100_core/src/gplx/Gfo_invk_.java b/100_core/src/gplx/Gfo_invk_.java new file mode 100644 index 000000000..2205d83bf --- /dev/null +++ b/100_core/src/gplx/Gfo_invk_.java @@ -0,0 +1,43 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; +public class Gfo_invk_ { + public static final String Mutator_suffix = "_"; + public static final Gfo_invk Noop = new Gfo_invk__noop(); + public static final String_obj_val + Rv_unhandled = String_obj_val.new_("Unhandled") + , Rv_handled = String_obj_val.new_("Handled") + , Rv_host = String_obj_val.new_("Host") + , Rv_cancel = String_obj_val.new_("Cancel") + , Rv_error = String_obj_val.new_("Error"); + + public static Gfo_invk as_(Object obj) {return obj instanceof Gfo_invk ? (Gfo_invk)obj : null;} + + public static Object Invk_no_key(Gfo_invk invk) {return Invk_by_msg(invk, "", GfoMsg_.Null);} + public static Object Invk_by_key(Gfo_invk invk, String k) {return Invk_by_msg(invk, k , GfoMsg_.Null);} + public static Object Invk_by_val(Gfo_invk invk, String k, Object v) {return Invk_by_msg(invk, k , GfoMsg_.new_cast_(k).Add("v", v));} + public static Object Invk_by_msg(Gfo_invk invk, String k, GfoMsg m) { + Object rv = invk.Invk(GfsCtx.Instance, 0, k, m); + if (rv == Gfo_invk_.Rv_unhandled) throw Err_.new_wo_type("invkable did not handle message", "key", k); + return rv; + } +} +class Gfo_invk__noop implements Gfo_invk { + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} +} diff --git a/100_core/src/gplx/Gfo_invk_cmd.java b/100_core/src/gplx/Gfo_invk_cmd.java new file mode 100644 index 000000000..6e11793c7 --- /dev/null +++ b/100_core/src/gplx/Gfo_invk_cmd.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Gfo_invk_cmd { + private final Gfo_invk itm; private final String cmd; private final GfoMsg msg; + public Gfo_invk_cmd(Gfo_invk itm, String cmd, GfoMsg msg) { + this.itm = itm; this.cmd = cmd; this.msg = msg; + } + public Object Exec() {return itm.Invk(GfsCtx.Instance, 0, cmd, msg);} + public Object Exec_by_ctx(GfsCtx ctx, GfoMsg msg) {return itm.Invk(ctx, 0, cmd, msg);} + + public static final Gfo_invk_cmd Noop = new Gfo_invk_cmd(Gfo_invk_.Noop, "", GfoMsg_.Null); + public static Gfo_invk_cmd New_by_key(Gfo_invk itm, String cmd) {return New_by_val(itm, cmd, null);} + public static Gfo_invk_cmd New_by_val(Gfo_invk itm, String cmd, Object val) { + return new Gfo_invk_cmd(itm, cmd, (val == null) ? GfoMsg_.Null : GfoMsg_.new_parse_(cmd).Add("v", val)); + } +} diff --git a/100_core/src/gplx/Gfo_invk_cmd_mgr.java b/100_core/src/gplx/Gfo_invk_cmd_mgr.java new file mode 100644 index 000000000..d2aff53d1 --- /dev/null +++ b/100_core/src/gplx/Gfo_invk_cmd_mgr.java @@ -0,0 +1,68 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; +public class Gfo_invk_cmd_mgr { + public Gfo_invk_cmd_mgr Add_cmd_many(Gfo_invk invk, String... keys) { + for (String key : keys) + list.Add(GfoInvkCmdItm.new_(key, invk)); + return this; + } + public Gfo_invk_cmd_mgr Add_cmd(String key, Gfo_invk invk) { + list.Add(GfoInvkCmdItm.new_(key, invk)); + return this; + } + public Gfo_invk_cmd_mgr Add_mgr(String key, Gfo_invk invk) { + list.Add(GfoInvkCmdItm.new_(key, invk).Type_isMgr_(true)); + return this; + } + public Gfo_invk_cmd_mgr Add_xtn(Gfo_invk xtn) { + list.Add(GfoInvkCmdItm.new_("xtn", xtn).Type_isXtn_(true)); + return this; + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m, Object host) { + for (int i = 0; i < list.Count(); i++) { + GfoInvkCmdItm itm = (GfoInvkCmdItm)list.Get_at(i); + if (itm.Type_isXtn()) { + Object invkVal = itm.Invk().Invk(ctx, ikey, k, m); + if (invkVal != Gfo_invk_.Rv_unhandled) return invkVal; + } + if (!ctx.Match(k, itm.Key())) continue; + if (itm.Type_isMgr()) return itm.Invk(); + Object rv = null; + m.Add("host", host); + rv = itm.Invk().Invk(ctx, ikey, k, m); + return rv == Gfo_invk_.Rv_host ? host : rv; // if returning "this" return host + } + return Unhandled; + } + public static final String_obj_val Unhandled = String_obj_val.new_("Gfo_invk_cmd_mgr Unhandled"); + List_adp list = List_adp_.New(); + public static Gfo_invk_cmd_mgr new_() {return new Gfo_invk_cmd_mgr();} Gfo_invk_cmd_mgr() {} +} +class GfoInvkCmdItm { + public String Key() {return key;} private String key; + public Gfo_invk Invk() {return invk;} Gfo_invk invk; + public boolean Type_isMgr() {return type_isMgr;} public GfoInvkCmdItm Type_isMgr_(boolean v) {type_isMgr = v; return this;} private boolean type_isMgr; + public boolean Type_isXtn() {return type_isXtn;} public GfoInvkCmdItm Type_isXtn_(boolean v) {type_isXtn = v; return this;} private boolean type_isXtn; + public static GfoInvkCmdItm new_(String key, Gfo_invk invk) { + GfoInvkCmdItm rv = new GfoInvkCmdItm(); + rv.key = key; rv.invk = invk; + return rv; + } GfoInvkCmdItm() {} +} diff --git a/100_core/src/gplx/Gfo_invk_cmd_mgr_owner.java b/100_core/src/gplx/Gfo_invk_cmd_mgr_owner.java new file mode 100644 index 000000000..37016871c --- /dev/null +++ b/100_core/src/gplx/Gfo_invk_cmd_mgr_owner.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_invk_cmd_mgr_owner { + Gfo_invk_cmd_mgr InvkMgr(); +} diff --git a/100_core/src/gplx/Gfo_invk_root_wkr.java b/100_core/src/gplx/Gfo_invk_root_wkr.java new file mode 100644 index 000000000..65186e688 --- /dev/null +++ b/100_core/src/gplx/Gfo_invk_root_wkr.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_invk_root_wkr { + Object Run_str_for(Gfo_invk invk, GfoMsg msg); +} diff --git a/100_core/src/gplx/Gfo_invk_to_str.java b/100_core/src/gplx/Gfo_invk_to_str.java new file mode 100644 index 000000000..03674c4a6 --- /dev/null +++ b/100_core/src/gplx/Gfo_invk_to_str.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.langs.gfs.*; +public class Gfo_invk_to_str { + public static GfoMsg ReadMsg(Gfo_invk invk, String k) { + GfsCtx ctx = GfsCtx.wtr_(); + GfoMsg m = GfoMsg_.rdr_(k); + invk.Invk(ctx, 0, k, m); + String invkKey = GfsCore.Instance.FetchKey(invk); + GfoMsg root = GfoMsg_.new_cast_(invkKey); + root.Subs_add(m); + return root; + } + public static GfoMsg WriteMsg(Gfo_invk invk, String k, Object... ary) {return WriteMsg(GfsCore.Instance.FetchKey(invk), invk, k, ary);} + public static GfoMsg WriteMsg(String invkKey, Gfo_invk invk, String k, Object... ary) { + GfsCtx ctx = GfsCtx.wtr_(); + GfoMsg m = GfoMsg_.wtr_(); + invk.Invk(ctx, 0, k, m); + GfoMsg rv = GfoMsg_.new_cast_(k); + for (int i = 0; i < m.Args_count(); i++) { + Keyval kv = m.Args_getAt(i); + rv.Add(kv.Key(), ary[i]); + } + GfoMsg root = GfoMsg_.new_cast_(invkKey); + root.Subs_add(rv); + return root; + } +} diff --git a/100_core/src/gplx/Gfo_log.java b/100_core/src/gplx/Gfo_log.java new file mode 100644 index 000000000..e53f3d771 --- /dev/null +++ b/100_core/src/gplx/Gfo_log.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_log { + List_adp Itms(); + Gfo_log Itms_(List_adp v); + void Warn(String msg, Object... args); + void Note(String msg, Object... args); + void Info(String msg, Object... args); + void Prog(String msg, Object... args); + void Flush(); +} diff --git a/100_core/src/gplx/Gfo_log_.java b/100_core/src/gplx/Gfo_log_.java new file mode 100644 index 000000000..a2b21441e --- /dev/null +++ b/100_core/src/gplx/Gfo_log_.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.logs.*; +public class Gfo_log_ { + public static Gfo_log Instance = new Gfo_log__mem(); + public static Gfo_log Instance__set(Gfo_log v) { + v.Itms_(Instance.Itms()); + Instance = v; + return v; + } + public static final String File__fmt = "yyyyMMdd_HHmmss", File__ext = ".log"; + public static Io_url New_url(Io_url dir) { + return dir.GenSubFil(Datetime_now.Get().XtoUtc().XtoStr_fmt(Gfo_log_.File__fmt) + Gfo_log_.File__ext); + } + public static Gfo_log New_file(Io_url dir) { + Io_url url = dir.GenSubFil(Datetime_now.Get().XtoStr_fmt(File__fmt) + File__ext); + Gfo_log__file.Delete_old_files(dir, Gfo_log_.Instance); + return new Gfo_log__file(url, new Gfo_log_itm_wtr__csv()); + } +} diff --git a/100_core/src/gplx/Gfo_log_bfr.java b/100_core/src/gplx/Gfo_log_bfr.java new file mode 100644 index 000000000..37f8540df --- /dev/null +++ b/100_core/src/gplx/Gfo_log_bfr.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Gfo_log_bfr { + private Bry_bfr bfr = Bry_bfr_.Reset(255); + public Gfo_log_bfr Add(String s) { + bfr.Add_str_a7(Datetime_now.Get().XtoUtc().XtoStr_fmt_yyyyMMdd_HHmmss_fff()); + bfr.Add_byte_space(); + bfr.Add_str_u8(s); + bfr.Add_byte_nl(); + return this; + } + public String Xto_str() {return bfr.To_str_and_clear();} +} diff --git a/100_core/src/gplx/Gfo_usr_dlg.java b/100_core/src/gplx/Gfo_usr_dlg.java new file mode 100644 index 000000000..59d0ddf3c --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_usr_dlg extends Cancelable { + void Canceled_y_(); void Canceled_n_(); + Gfo_usr_dlg__log Log_wkr(); void Log_wkr_(Gfo_usr_dlg__log v); + Gfo_usr_dlg__gui Gui_wkr(); void Gui_wkr_(Gfo_usr_dlg__gui v); + String Log_many(String grp_key, String msg_key, String fmt, Object... args); + String Warn_many(String grp_key, String msg_key, String fmt, Object... args); + Err Fail_many(String grp_key, String msg_key, String fmt, Object... args); + String Prog_many(String grp_key, String msg_key, String fmt, Object... args); + String Prog_none(String grp_key, String msg_key, String fmt); + String Note_many(String grp_key, String msg_key, String fmt, Object... args); + String Note_none(String grp_key, String msg_key, String fmt); + String Note_gui_none(String grp_key, String msg_key, String fmt); + String Prog_one(String grp_key, String msg_key, String fmt, Object arg); + String Prog_direct(String msg); + String Log_direct(String msg); + String Plog_many(String grp_key, String msg_key, String fmt, Object... args); +} diff --git a/100_core/src/gplx/Gfo_usr_dlg_.java b/100_core/src/gplx/Gfo_usr_dlg_.java new file mode 100644 index 000000000..7dc76ccba --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg_.java @@ -0,0 +1,74 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Gfo_usr_dlg_ { + private static Gfo_usr_dlg_base test__list, test__show; + public static Gfo_usr_dlg Instance = Gfo_usr_dlg_noop.Instance; // NOTE: global instance which can be reassigned + public static final Gfo_usr_dlg Noop = Gfo_usr_dlg_noop.Instance; + public static Gfo_usr_dlg__gui Test__list__init() { + if (test__list == null) + test__list = new Gfo_usr_dlg_base(Gfo_usr_dlg__log_.Noop, Gfo_usr_dlg__gui_.Test); + Gfo_usr_dlg__gui_.Test.Clear(); + Instance = test__list; + return Gfo_usr_dlg__gui_.Test; + } + public static String Test__list__term__get_1st() { + Instance = Noop; + String[] rv = ((Gfo_usr_dlg__gui_test)test__list.Gui_wkr()).Warns().To_str_ary_and_clear(); + return rv.length == 0 ? "" : rv[0]; + } + public static void Test__show__init() { + if (test__show == null) + test__show = new Gfo_usr_dlg_base(Gfo_usr_dlg__log_.Noop, Gfo_usr_dlg__gui_.Console); + Instance = test__show; + } + public static void Test__show__term() { + Instance = Noop; + } + public static Gfo_usr_dlg Test() { + if (test__list == null) + test__list = new Gfo_usr_dlg_base(Gfo_usr_dlg__log_.Noop, Gfo_usr_dlg__gui_.Test); + return test__list; + } + public static Gfo_usr_dlg Test_console() { + if (test_console == null) + test_console = new Gfo_usr_dlg_base(Gfo_usr_dlg__log_.Noop, Gfo_usr_dlg__gui_.Console); + return test_console; + } private static Gfo_usr_dlg_base test_console; +} +class Gfo_usr_dlg_noop implements Gfo_usr_dlg { + public boolean Canceled() {return false;} public void Canceled_y_() {} public void Canceled_n_() {} + public void Cancel() {} + public void Clear() {} + public Gfo_usr_dlg__log Log_wkr() {return Gfo_usr_dlg__log_.Noop;} public void Log_wkr_(Gfo_usr_dlg__log v) {} + public Gfo_usr_dlg__gui Gui_wkr() {return Gfo_usr_dlg__gui_.Noop;} public void Gui_wkr_(Gfo_usr_dlg__gui v) {} + public String Log_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public String Warn_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public Err Fail_many(String grp_key, String msg_key, String fmt, Object... args) {return Err_.new_wo_type(fmt);} + public String Prog_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public String Prog_none(String grp_key, String msg_key, String fmt) {return "";} + public String Note_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public String Note_none(String grp_key, String msg_key, String fmt) {return "";} + public String Note_gui_none(String grp_key, String msg_key, String fmt) {return "";} + public String Prog_one(String grp_key, String msg_key, String fmt, Object arg) {return "";} + public String Prog_direct(String msg) {return "";} + public String Log_direct(String msg) {return "";} + public String Plog_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} + public static final Gfo_usr_dlg_noop Instance = new Gfo_usr_dlg_noop(); Gfo_usr_dlg_noop() {} +} diff --git a/100_core/src/gplx/Gfo_usr_dlg__gui.java b/100_core/src/gplx/Gfo_usr_dlg__gui.java new file mode 100644 index 000000000..6ee42740a --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg__gui.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.lists.rings.*; +public interface Gfo_usr_dlg__gui { + void Clear(); + Ring__string Prog_msgs(); + void Write_prog(String text); + void Write_note(String text); + void Write_warn(String text); + void Write_stop(String text); +} diff --git a/100_core/src/gplx/Gfo_usr_dlg__gui_.java b/100_core/src/gplx/Gfo_usr_dlg__gui_.java new file mode 100644 index 000000000..02f4c5b8d --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg__gui_.java @@ -0,0 +1,52 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.consoles.*; import gplx.core.lists.rings.*; +public class Gfo_usr_dlg__gui_ { + public static final Gfo_usr_dlg__gui Noop = new Gfo_usr_dlg__gui_noop(); + public static final Gfo_usr_dlg__gui Console = new Gfo_usr_dlg__gui_console(); + public static final Gfo_usr_dlg__gui Test = new Gfo_usr_dlg__gui_test(); + public static final Gfo_usr_dlg__gui Mem = new Gfo_usr_dlg__gui_mem_string(); + public static String Mem_file() {return ((Gfo_usr_dlg__gui_mem_string)Mem).file;} +} +class Gfo_usr_dlg__gui_noop implements Gfo_usr_dlg__gui { + public void Clear() {} + public Ring__string Prog_msgs() {return ring;} Ring__string ring = new Ring__string().Max_(0); + public void Write_prog(String text) {} + public void Write_note(String text) {} + public void Write_warn(String text) {} + public void Write_stop(String text) {} +} +class Gfo_usr_dlg__gui_console implements Gfo_usr_dlg__gui { + private final Console_adp__sys console = Console_adp__sys.Instance; + public void Clear() {} + public Ring__string Prog_msgs() {return ring;} private final Ring__string ring = new Ring__string().Max_(0); + public void Write_prog(String text) {console.Write_tmp(text);} + public void Write_note(String text) {console.Write_str_w_nl(text);} + public void Write_warn(String text) {console.Write_str_w_nl(text);} + public void Write_stop(String text) {console.Write_str_w_nl(text);} +} +class Gfo_usr_dlg__gui_mem_string implements Gfo_usr_dlg__gui { + public String file = ""; + public void Clear() {file = "";} + public Ring__string Prog_msgs() {return ring;} private final Ring__string ring = new Ring__string().Max_(0); + public void Write_prog(String text) {file += text + "\n";} + public void Write_note(String text) {file += text + "\n";} + public void Write_warn(String text) {file += text + "\n";} + public void Write_stop(String text) {file += text + "\n";} +} diff --git a/100_core/src/gplx/Gfo_usr_dlg__gui_test.java b/100_core/src/gplx/Gfo_usr_dlg__gui_test.java new file mode 100644 index 000000000..a5ecc78f3 --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg__gui_test.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.lists.rings.*; +public class Gfo_usr_dlg__gui_test implements Gfo_usr_dlg__gui { + public List_adp Warns() {return warns;} private final List_adp warns = List_adp_.New(); + public List_adp Msgs() {return msgs;} private final List_adp msgs = List_adp_.New(); + public Ring__string Prog_msgs() {return ring;} private final Ring__string ring = new Ring__string().Max_(0); + public void Clear() {msgs.Clear(); warns.Clear();} + public void Write_prog(String text) {msgs.Add(text);} + public void Write_note(String text) {msgs.Add(text);} + public void Write_warn(String text) {warns.Add(text);} + public void Write_stop(String text) {msgs.Add(text);} +} diff --git a/100_core/src/gplx/Gfo_usr_dlg__log.java b/100_core/src/gplx/Gfo_usr_dlg__log.java new file mode 100644 index 000000000..141b41163 --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg__log.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Gfo_usr_dlg__log extends Gfo_invk { + boolean Enabled(); void Enabled_(boolean v); + boolean Queue_enabled(); void Queue_enabled_(boolean v); + Io_url Log_dir(); void Log_dir_(Io_url v); + Io_url Session_dir(); + Io_url Session_fil(); + void Log_msg_to_url_fmt(Io_url url, String fmt, Object... args); + void Log_to_session(String txt); + void Log_to_session_fmt(String fmt, Object... args); + void Log_to_session_direct(String txt); + void Log_to_err(String txt); + void Log_term(); +} diff --git a/100_core/src/gplx/Gfo_usr_dlg__log_.java b/100_core/src/gplx/Gfo_usr_dlg__log_.java new file mode 100644 index 000000000..d3da9c519 --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg__log_.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Gfo_usr_dlg__log_ { + public static final Gfo_usr_dlg__log Noop = new Gfo_usr_dlg__log_noop(); +} +class Gfo_usr_dlg__log_noop implements Gfo_usr_dlg__log { + public Io_url Session_fil() {return Io_url_.Empty;} + public Io_url Session_dir() {return Io_url_.Empty;} + public Io_url Log_dir() {return Io_url_.Empty;} public void Log_dir_(Io_url v) {} + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public boolean Queue_enabled() {return queue_enabled;} public void Queue_enabled_(boolean v) {queue_enabled = v;} private boolean queue_enabled; + public void Log_msg_to_url_fmt(Io_url url, String fmt, Object... args) {} + public void Log_to_session_fmt(String fmt, Object... args) {} + public void Log_to_session(String txt) {} + public void Log_to_session_direct(String txt) {} + public void Log_to_err(String txt) {} + public void Log_term() {} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} +} diff --git a/100_core/src/gplx/Gfo_usr_dlg__log_base.java b/100_core/src/gplx/Gfo_usr_dlg__log_base.java new file mode 100644 index 000000000..d11982327 --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg__log_base.java @@ -0,0 +1,123 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.core.consoles.*; import gplx.core.brys.fmtrs.*; +public class Gfo_usr_dlg__log_base implements Gfo_usr_dlg__log { + private int archive_dirs_max = 8; + private Io_url log_dir, err_fil; + private Ordered_hash queued_list = Ordered_hash_.New(); + private Bry_fmtr fmtr = Bry_fmtr.New__tmp(); private Bry_bfr tmp_bfr = Bry_bfr_.Reset(255); + public boolean Queue_enabled() {return queue_enabled;} public void Queue_enabled_(boolean v) {queue_enabled = v; if (!v) this.Flush();} private boolean queue_enabled; + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled = true; + public Io_url Session_dir() {return session_dir;} private Io_url session_dir; + public Io_url Session_fil() {return session_fil;} private Io_url session_fil; + private void Flush() { + int queued_len = queued_list.Count(); + for (int i = 0; i < queued_len; i++) { + Usr_log_fil fil = (Usr_log_fil)queued_list.Get_at(i); + if (fil.Url() == null) { + fil.Url_(session_dir.GenSubFil("session.txt")); + } + fil.Flush(); + } + } + public Io_url Log_dir() {return log_dir;} + public void Log_dir_(Io_url log_dir) { + this.log_dir = log_dir; + session_dir = log_dir.GenSubDir(Dir_name_current); + session_fil = session_dir.GenSubFil("session.txt"); + err_fil = session_dir.GenSubFil("err.txt"); + } + public void Log_term() { + if (!enabled) return; + Io_url[] archive_dirs = Io_mgr.Instance.QueryDir_args(log_dir).DirInclude_().DirOnly_().ExecAsUrlAry(); + int archive_dirs_len = archive_dirs.length; + int session_cutoff = archive_dirs_len - archive_dirs_max; + for (int i = 0; i < session_cutoff; i++) { + Io_url archive_dir = archive_dirs[i]; + Io_mgr.Instance.DeleteDirDeep(archive_dir); + this.Log_to_session("archive dir del: " + session_dir.Raw()); + } + this.Log_to_session("app term"); + MoveCurrentToArchive(session_dir); + } + private void MoveCurrentToArchive(Io_url dir) {Io_mgr.Instance.MoveDirDeep(dir, dir.OwnerDir().GenSubDir(Datetime_now.Get().XtoStr_fmt_yyyyMMdd_HHmmss_fff()));} + public void Log_info(boolean warn, String s) {if (warn) Log_to_err(s); else Log_to_session(s);} + public void Log_msg_to_url_fmt(Io_url url, String fmt, Object... args) { + if (!enabled) return; + String msg = Bld_msg(String_.new_u8(fmtr.Fmt_(fmt).Bld_bry_many(tmp_bfr, args))); + Log_msg(url, msg); + Log_msg(session_fil, msg); + } + public void Log_to_session_fmt(String fmt, Object... args) {Log_to_session(String_.new_u8(fmtr.Fmt_(fmt).Bld_bry_many(tmp_bfr, args)));} + public void Log_to_session(String s) { + if (!enabled) return; + String line = Bld_msg(s); + Log_msg(session_fil, line); + } + public void Log_to_session_direct(String s) { + if (!enabled) return; + Log_msg(session_fil, s); + } + public void Log_to_err(String s) { + if (!enabled) return; + try { + String line = Bld_msg(s); + Log_msg(session_fil, line); + Log_msg(err_fil, line); + } + catch (Exception e) {Err_.Noop(e);} // java.lang.StringBuilder can throw exceptions in some situations when called on a different thread; ignore errors + } private String_bldr sb = String_bldr_.new_thread(); // NOTE: use java.lang.StringBuffer to try to avoid random exceptions when called on a different thread + private String Bld_msg(String s) {return sb.Add(Datetime_now.Get_force().XtoUtc().XtoStr_fmt_yyyyMMdd_HHmmss_fff()).Add(" ").Add(s).Add_char_nl().To_str_and_clear();} + private void Log_msg(Io_url url, String txt) { + if (queue_enabled) { + String url_raw = url == null ? "mem" : url.Raw(); + Usr_log_fil fil = (Usr_log_fil)queued_list.Get_by(url_raw); + if (fil == null) { + fil = new Usr_log_fil(url); + queued_list.Add(url_raw, fil); + } + fil.Add(txt); + } + else + Io_mgr.Instance.AppendFilStr(url, txt); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled_)) enabled = m.ReadYn("v"); + else if (ctx.Match(k, Invk_archive_dirs_max_)) archive_dirs_max = m.ReadInt("v"); + else if (ctx.Match(k, Invk_log_dir_)) log_dir = m.ReadIoUrl("v"); + else return Gfo_invk_.Rv_unhandled; + return this; + } public static final String Invk_enabled_ = "enabled_", Invk_archive_dirs_max_ = "archive_dirs_max_", Invk_log_dir_ = "log_dir_"; + static final String Dir_name_log = "log", Dir_name_current = "current"; + public static final Gfo_usr_dlg__log_base Instance = new Gfo_usr_dlg__log_base(); +} +class Usr_log_fil { + public Usr_log_fil(Io_url url) {this.url = url;} + public Io_url Url() {return url;} public Usr_log_fil Url_(Io_url v) {url = v; return this;} Io_url url; + public void Add(String text) {sb.Add(text);} String_bldr sb = String_bldr_.new_(); + public void Flush() { + if (sb.Count() == 0) return; + try { + Io_mgr.Instance.AppendFilStr(url, sb.To_str_and_clear()); + } + catch (Exception e) { + Console_adp__sys.Instance.Write_str_w_nl(Err_.Message_gplx_full(e)); + } + } +} diff --git a/100_core/src/gplx/Gfo_usr_dlg_base.java b/100_core/src/gplx/Gfo_usr_dlg_base.java new file mode 100644 index 000000000..84767b802 --- /dev/null +++ b/100_core/src/gplx/Gfo_usr_dlg_base.java @@ -0,0 +1,66 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.brys.fmtrs.*; +public class Gfo_usr_dlg_base implements Gfo_usr_dlg { + private final Bry_bfr tmp_bfr = Bry_bfr_.Reset(255); + private final Bry_fmtr tmp_fmtr = Bry_fmtr.New__tmp().Fail_when_invalid_escapes_(false); // do not fail b/c msgs may contain excerpt of random text; EX:[[User:A|~A~]] DATE:2014-11-28 + public Gfo_usr_dlg_base(Gfo_usr_dlg__log log_wkr, Gfo_usr_dlg__gui gui_wkr) {this.log_wkr = log_wkr; this.gui_wkr = gui_wkr;} + public Gfo_usr_dlg__log Log_wkr() {return log_wkr;} public void Log_wkr_(Gfo_usr_dlg__log v) {log_wkr = v;} private Gfo_usr_dlg__log log_wkr; + public Gfo_usr_dlg__gui Gui_wkr() {return gui_wkr;} public void Gui_wkr_(Gfo_usr_dlg__gui v) {gui_wkr = v;} private Gfo_usr_dlg__gui gui_wkr; + public boolean Canceled() {return canceled;} public void Canceled_y_() {canceled = true;} public void Canceled_n_() {canceled = false;} private boolean canceled; + public void Cancel() {canceled = true;} + public String Log_many(String grp_key, String msg_key, String fmt, Object... args) {String rv = Bld_msg_many(grp_key, msg_key, fmt, args ); log_wkr.Log_to_session(rv); return rv;} + public String Warn_many(String grp_key, String msg_key, String fmt, Object... args) {String rv = Bld_msg_many(grp_key, msg_key, fmt, args ); log_wkr.Log_to_err(rv); gui_wkr.Write_warn(rv); return rv;} + public String Prog_many(String grp_key, String msg_key, String fmt, Object... args) {String rv = Bld_msg_many(grp_key, msg_key, fmt, args ); gui_wkr.Write_prog(rv); return rv;} + public String Prog_one(String grp_key, String msg_key, String fmt, Object arg) {String rv = Bld_msg_one (grp_key, msg_key, fmt, arg ); gui_wkr.Write_prog(rv); return rv;} + public String Prog_none(String grp_key, String msg_key, String fmt) {String rv = Bld_msg_none(grp_key, msg_key, fmt ); gui_wkr.Write_prog(rv); return rv;} + public String Prog_direct(String msg) { gui_wkr.Write_prog(msg); return msg;} + public String Log_direct(String msg) { log_wkr.Log_to_session(msg); return msg;} + public String Note_many(String grp_key, String msg_key, String fmt, Object... args) {String rv = Bld_msg_many(grp_key, msg_key, fmt, args ); log_wkr.Log_to_session(rv); gui_wkr.Write_note(rv); return rv;} + public String Note_none(String grp_key, String msg_key, String fmt) {String rv = Bld_msg_none(grp_key, msg_key, fmt ); log_wkr.Log_to_session(rv); gui_wkr.Write_note(rv); return rv;} + public String Note_gui_none(String grp_key, String msg_key, String fmt) {String rv = Bld_msg_none(grp_key, msg_key, fmt ); gui_wkr.Write_note(rv); return rv;} + public String Plog_many(String grp_key, String msg_key, String fmt, Object... args) { + String rv = Log_many(grp_key, msg_key, fmt, args); + return Prog_direct(rv); + } + public Err Fail_many(String grp_key, String msg_key, String fmt, Object... args) { + Err rv = Err_.new_wo_type(Bld_msg_many(grp_key, msg_key, fmt, args)); + log_wkr.Log_to_err(rv.To_str__full()); + return rv; + } + private String Bld_msg_many(String grp_key, String msg_key, String fmt, Object[] args) { + synchronized (tmp_fmtr) { + try { + tmp_fmtr.Fmt_(fmt).Bld_bfr_many(tmp_bfr, args); + return tmp_bfr.To_str_and_clear(); + } + catch (Exception e) { // NOTE: can fail if fmt has ~{}; callers should proactively remove, but for now, just return fmt if fails; EX:Page_sync and en.w:Web_crawler; DATE:2016-11-17 + Err_.Noop(e); + return fmt; + } + } + } + private String Bld_msg_one(String grp_key, String msg_key, String fmt, Object val) { + synchronized (tmp_fmtr) { + tmp_fmtr.Fmt_(fmt).Bld_bfr_one(tmp_bfr, val); + return tmp_bfr.To_str_and_clear(); + } + } + private String Bld_msg_none(String grp_key, String msg_key, String fmt) {return fmt;} +} diff --git a/100_core/src/gplx/GfsCtx.java b/100_core/src/gplx/GfsCtx.java new file mode 100644 index 000000000..cf33577a3 --- /dev/null +++ b/100_core/src/gplx/GfsCtx.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class GfsCtx { + public Ordered_hash Vars() {return vars;} Ordered_hash vars = Ordered_hash_.New(); + public boolean Fail_if_unhandled() {return fail_if_unhandled;} public GfsCtx Fail_if_unhandled_(boolean v) {fail_if_unhandled = v; return this;} private boolean fail_if_unhandled; + public Gfo_usr_dlg Usr_dlg() {return usr_dlg;} public GfsCtx Usr_dlg_(Gfo_usr_dlg v) {usr_dlg = v; return this;} Gfo_usr_dlg usr_dlg; + public boolean Help_browseMode() {return help_browseMode;} public GfsCtx Help_browseMode_(boolean v) {help_browseMode = v; return this;} private boolean help_browseMode; + public List_adp Help_browseList() {return help_browseList;} List_adp help_browseList = List_adp_.New(); + public Object MsgSrc() {return msgSrc;} public GfsCtx MsgSrc_(Object v) {msgSrc = v; return this;} Object msgSrc; + public boolean Match(String k, String match) { + if (help_browseMode) { + help_browseList.Add(match); + return false; + } + else + return String_.Eq(k, match); + } + public boolean MatchPriv(String k, String match) {return help_browseMode ? false : String_.Eq(k, match);} + public boolean MatchIn(String k, String... match) { + if (help_browseMode) { + for (String i : match) + help_browseList.Add(i); + return false; + } + return String_.In(k, match); + } + public boolean Write_note(String fmt, Object... ary) {UsrDlg_.Instance.Note(fmt, ary); return false;} + public boolean Write_warn(String fmt, Object... ary) {UsrDlg_.Instance.Note("! " + fmt, ary); return false;} + public boolean Write_stop(UsrMsg umsg) {UsrDlg_.Instance.Note("* " + umsg.To_str()); return false;} + public boolean Write_stop(String fmt, Object... ary) {UsrDlg_.Instance.Note("* " + fmt, ary); return false;} + public boolean Deny() {return deny;} private boolean deny; + public static final GfsCtx Instance = new GfsCtx(); + public static GfsCtx new_() {return new GfsCtx();} GfsCtx() {} + public static GfsCtx rdr_() { + GfsCtx rv = new GfsCtx(); + rv.deny = true; + rv.mode = "read"; + return rv; + } + public static GfsCtx wtr_() { + GfsCtx rv = new GfsCtx(); + rv.deny = true; + rv.mode = Mode_write; + return rv; + } + public String Mode() {return mode;} public GfsCtx Mode_(String v) {mode = v; return this;} private String mode = "regular"; + public static final String Mode_write = "write"; + public static final int Ikey_null = -1; +} diff --git a/100_core/src/gplx/Guid_adp.java b/100_core/src/gplx/Guid_adp.java new file mode 100644 index 000000000..f157c31ac --- /dev/null +++ b/100_core/src/gplx/Guid_adp.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Guid_adp { + public Guid_adp(java.util.UUID guid) {this.guid = guid;} java.util.UUID guid; + public String To_str() {return guid.toString();} +} \ No newline at end of file diff --git a/100_core/src/gplx/Guid_adp_.java b/100_core/src/gplx/Guid_adp_.java new file mode 100644 index 000000000..9c6b42b8b --- /dev/null +++ b/100_core/src/gplx/Guid_adp_.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Guid_adp_ { + public static final String Cls_ref_name = "Guid"; + public static final Guid_adp Empty = Parse("00000000-0000-0000-0000-000000000000"); + public static String New_str() {return New().To_str();} + public static Guid_adp New() {return new Guid_adp(java.util.UUID.randomUUID());} + public static Guid_adp Parse(String s) {return new Guid_adp(java.util.UUID.fromString(s));} +} \ No newline at end of file diff --git a/100_core/src/gplx/Guid_adp__tst.java b/100_core/src/gplx/Guid_adp__tst.java new file mode 100644 index 000000000..b2f785e21 --- /dev/null +++ b/100_core/src/gplx/Guid_adp__tst.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Guid_adp__tst { + @Test public void parse() { + tst_parse_("467ffb41-cdfe-402f-b22b-be855425784b"); + } + void tst_parse_(String s) { + Guid_adp uuid = Guid_adp_.Parse(s); + Tfds.Eq(uuid.To_str(), s); + } +} diff --git a/100_core/src/gplx/Hash_adp.java b/100_core/src/gplx/Hash_adp.java new file mode 100644 index 000000000..cb2548723 --- /dev/null +++ b/100_core/src/gplx/Hash_adp.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Hash_adp extends gplx.core.lists.EnumerAble { + int Count(); + boolean Has(Object key); + Object Get_by(Object key); + Object Get_by_or_fail(Object key); + void Add(Object key, Object val); + void Add_as_key_and_val(Object val); + boolean Add_if_dupe_use_1st(Object key, Object val); + void Add_if_dupe_use_nth(Object key, Object val); + void Del(Object key); + void Clear(); +} diff --git a/100_core/src/gplx/Hash_adp_.java b/100_core/src/gplx/Hash_adp_.java new file mode 100644 index 000000000..350624029 --- /dev/null +++ b/100_core/src/gplx/Hash_adp_.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; +public class Hash_adp_ { + public static Hash_adp New() {return new Hash_adp_obj();} + public static final Hash_adp Noop = new Hash_adp_noop(); +} +class Hash_adp_obj extends gplx.core.lists.Hash_adp_base implements Hash_adp {}//_20110428 +class Hash_adp_noop implements Hash_adp { + public int Count() {return 0;} + public boolean Has(Object key) {return false;} + public Object Get_by(Object key) {return null;} + public Object Get_by_or_fail(Object key) {throw Err_.new_missing_key(Object_.Xto_str_strict_or_null_mark(key));} + public void Add(Object key, Object val) {} + public void Add_as_key_and_val(Object val) {} + public void Add_if_dupe_use_nth(Object key, Object val) {} + public boolean Add_if_dupe_use_1st(Object key, Object val) {return false;} + public void Del(Object key) {} + public void Clear() {} + public java.util.Iterator iterator() {return gplx.core.lists.Iterator_null.Instance;} +} diff --git a/100_core/src/gplx/Hash_adp_bry.java b/100_core/src/gplx/Hash_adp_bry.java new file mode 100644 index 000000000..f3fa04933 --- /dev/null +++ b/100_core/src/gplx/Hash_adp_bry.java @@ -0,0 +1,207 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; +import gplx.core.intls.*; +public class Hash_adp_bry extends gplx.core.lists.Hash_adp_base implements Hash_adp { + private final Hash_adp_bry_itm_base proto, key_ref; + Hash_adp_bry(Hash_adp_bry_itm_base proto) { + this.proto = proto; + this.key_ref = proto.New(); + } + @Override protected Object Fetch_base(Object key) {synchronized (key_ref) {return super.Fetch_base(key_ref.Init((byte[])key));}} // TS: DATE:2016-07-06 + @Override protected void Del_base(Object key) {synchronized (key_ref) {super.Del_base(key_ref.Init((byte[])key));}}// TS: DATE:2016-07-06 + @Override protected boolean Has_base(Object key) {synchronized (key_ref) {return super.Has_base(key_ref.Init((byte[])key));}}// TS: DATE:2016-07-06 + public int Get_as_int(byte[] key) {return Get_as_int(key, 0, key.length);} + public int Get_as_int(byte[] key, int bgn, int end) { + int rv = Get_as_int_or(key, bgn, end, Int_.Min_value); if (rv == Int_.Min_value) throw Err_.new_("core", "unknown key", "key", key); + return rv; + } + public int Get_as_int_or(byte[] key, int or) {return Get_as_int_or(key, 0, key.length, or);} + public int Get_as_int_or(byte[] key, int bgn, int end, int or) { + Object o = Get_by_mid(key, bgn, end); + return (o == null) ? or : ((Int_obj_val)o).Val(); + } + public byte Get_as_byte_or(byte[] key, byte or) {return Get_as_byte_or(key, 0, key.length, or);} + public byte Get_as_byte_or(byte[] key, int bgn, int end, byte or) { + Object o = Get_by_mid(key, bgn, end); + return o == null ? or : ((Byte_obj_val)o).Val(); + } + public Object Get_by_bry(byte[] src) {synchronized (key_ref) {return super.Fetch_base(key_ref.Init(src));}} // TS: DATE:2016-07-06 + public Object Get_by_mid(byte[] src, int bgn, int end) {synchronized (key_ref) {return super.Fetch_base(key_ref.Init(src, bgn, end));}}// TS: DATE:2016-07-06 + public Hash_adp_bry Add_byte_int(byte key, int val) {this.Add_base(new byte[]{key}, new Int_obj_val(val)); return this;} + public Hash_adp_bry Add_bry_byte(byte[] key, byte val) {this.Add_base(key, Byte_obj_val.new_(val)); return this;} + public Hash_adp_bry Add_bry_int(byte[] key, int val) {this.Add_base(key, new Int_obj_val(val)); return this;} + public Hash_adp_bry Add_bry_bry(byte[] key) {this.Add_base(key, key); return this;} + public Hash_adp_bry Add_str_byte(String key, byte val) {this.Add_base(Bry_.new_u8(key), Byte_obj_val.new_(val)); return this;} + public Hash_adp_bry Add_str_int(String key, int val) {this.Add_base(Bry_.new_u8(key), new Int_obj_val(val)); return this;} + public Hash_adp_bry Add_str_obj(String key, Object val) {this.Add_base(Bry_.new_u8(key), val); return this;} + public Hash_adp_bry Add_bry_obj(byte[] key, Object val) {this.Add_base(key, val); return this;} + public Hash_adp_bry Add_many_str(String... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) { + String itm = ary[i]; + byte[] bry = Bry_.new_u8(itm); + Add_bry_bry(bry); + } + return this; + } + public Hash_adp_bry Add_many_bry(byte[]... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) + Add_bry_bry(ary[i]); + return this; + } + @Override protected void Add_base(Object key, Object val) { + byte[] key_bry = (byte[])key; + Hash_adp_bry_itm_base key_itm = proto.New(); + key_itm.Init(key_bry, 0, key_bry.length); + super.Add_base(key_itm, val); + } + public static Hash_adp_bry cs() {return new Hash_adp_bry(Hash_adp_bry_itm_cs.Instance);} + public static Hash_adp_bry ci_a7() {return new Hash_adp_bry(Hash_adp_bry_itm_ci_a7.Instance);} + public static Hash_adp_bry ci_u8(Gfo_case_mgr case_mgr) {return new Hash_adp_bry(Hash_adp_bry_itm_ci_u8.get_or_new(case_mgr));} + public static Hash_adp_bry c__u8(boolean case_match, Gfo_case_mgr case_mgr) {return case_match ? cs() : ci_u8(case_mgr);} +} +abstract class Hash_adp_bry_itm_base { + public abstract Hash_adp_bry_itm_base New(); + public Hash_adp_bry_itm_base Init(byte[] src) {return this.Init(src, 0, src.length);} + public abstract Hash_adp_bry_itm_base Init(byte[] src, int src_bgn, int src_end); +} +class Hash_adp_bry_itm_cs extends Hash_adp_bry_itm_base { + private byte[] src; int src_bgn, src_end; + @Override public Hash_adp_bry_itm_base New() {return new Hash_adp_bry_itm_cs();} + @Override public Hash_adp_bry_itm_base Init(byte[] src, int src_bgn, int src_end) {this.src = src; this.src_bgn = src_bgn; this.src_end = src_end; return this;} + @Override public int hashCode() { + int rv = 0; + for (int i = src_bgn; i < src_end; i++) { + int b_int = src[i] & 0xFF; // JAVA: patch + rv = (31 * rv) + b_int; + } + return rv; + } + @Override public boolean equals(Object obj) { + if (obj == null) return false; + Hash_adp_bry_itm_cs comp = (Hash_adp_bry_itm_cs)obj; + byte[] comp_src = comp.src; int comp_bgn = comp.src_bgn, comp_end = comp.src_end; + int comp_len = comp_end - comp_bgn, src_len = src_end - src_bgn; + if (comp_len != src_len) return false; + for (int i = 0; i < comp_len; i++) { + int src_pos = src_bgn + i; + if (src_pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + if (src[src_pos] != comp_src[i + comp_bgn]) return false; + } + return true; + } + public static final Hash_adp_bry_itm_cs Instance = new Hash_adp_bry_itm_cs(); Hash_adp_bry_itm_cs() {} +} +class Hash_adp_bry_itm_ci_a7 extends Hash_adp_bry_itm_base { + private byte[] src; int src_bgn, src_end; + @Override public Hash_adp_bry_itm_base New() {return new Hash_adp_bry_itm_ci_a7();} + @Override public Hash_adp_bry_itm_base Init(byte[] src, int src_bgn, int src_end) {this.src = src; this.src_bgn = src_bgn; this.src_end = src_end; return this;} + @Override public int hashCode() { + int rv = 0; + for (int i = src_bgn; i < src_end; i++) { + int b_int = src[i] & 0xFF; // JAVA: patch + if (b_int > 64 && b_int < 91) // 64=before A; 91=after Z; NOTE: lowering upper-case on PERF assumption that there will be more lower-case letters than upper-case + b_int += 32; + rv = (31 * rv) + b_int; + } + return rv; + } + @Override public boolean equals(Object obj) { + if (obj == null) return false; + Hash_adp_bry_itm_ci_a7 comp = (Hash_adp_bry_itm_ci_a7)obj; + byte[] comp_src = comp.src; int comp_bgn = comp.src_bgn, comp_end = comp.src_end; + int comp_len = comp_end - comp_bgn, src_len = src_end - src_bgn; + if (comp_len != src_len) return false; + for (int i = 0; i < comp_len; i++) { + int src_pos = src_bgn + i; + if (src_pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + byte src_byte = src[src_pos]; + if (src_byte > 64 && src_byte < 91) src_byte += 32; + byte comp_byte = comp_src[i + comp_bgn]; + if (comp_byte > 64 && comp_byte < 91) comp_byte += 32; + if (src_byte != comp_byte) return false; + } + return true; + } + public static final Hash_adp_bry_itm_ci_a7 Instance = new Hash_adp_bry_itm_ci_a7(); Hash_adp_bry_itm_ci_a7() {} +} +class Hash_adp_bry_itm_ci_u8 extends Hash_adp_bry_itm_base { + private final Gfo_case_mgr case_mgr; + Hash_adp_bry_itm_ci_u8(Gfo_case_mgr case_mgr) {this.case_mgr = case_mgr;} + private byte[] src; int src_bgn, src_end; + @Override public Hash_adp_bry_itm_base New() {return new Hash_adp_bry_itm_ci_u8(case_mgr);} + @Override public Hash_adp_bry_itm_base Init(byte[] src, int src_bgn, int src_end) {this.src = src; this.src_bgn = src_bgn; this.src_end = src_end; return this;} + @Override public int hashCode() { + int rv = 0; + for (int i = src_bgn; i < src_end; i++) { + byte b = src[i]; + int b_int = b & 0xFF; // JAVA: patch + Gfo_case_itm itm = case_mgr.Get_or_null(b, src, i, src_end); + if (itm == null) { // unknown itm; byte is a number, symbol, or unknown; just use the existing byte + } + else { // known itm; use its hash_code + b_int = itm.Hashcode_lo(); + int b_len = Utf8_.Len_of_char_by_1st_byte(b); // NOTE: must calc b_len for langs with asymmetric upper / lower; PAGE:tr.w:Zvishavane DATE:2015-09-07 + i += b_len - 1; + } + rv = (31 * rv) + b_int; + } + return rv; + } + @Override public boolean equals(Object obj) { + if (obj == null) return false; + Hash_adp_bry_itm_ci_u8 trg_itm = (Hash_adp_bry_itm_ci_u8)obj; + byte[] trg = trg_itm.src; int trg_bgn = trg_itm.src_bgn, trg_end = trg_itm.src_end; + int src_c_bgn = src_bgn; + int trg_c_bgn = trg_bgn; + while ( src_c_bgn < src_end + && trg_c_bgn < trg_end) { // exit once one goes out of bounds + byte src_c = src[src_c_bgn]; + byte trg_c = trg[trg_c_bgn]; + int src_c_len = Utf8_.Len_of_char_by_1st_byte(src_c); + int trg_c_len = Utf8_.Len_of_char_by_1st_byte(trg_c); + int src_c_end = src_c_bgn + src_c_len; + int trg_c_end = trg_c_bgn + trg_c_len; + Gfo_case_itm src_c_itm = case_mgr.Get_or_null(src_c, src, src_c_bgn, src_c_end); + Gfo_case_itm trg_c_itm = case_mgr.Get_or_null(trg_c, trg, trg_c_bgn, trg_c_end); + if (src_c_itm != null && trg_c_itm == null) return false; // src == ltr; trg != ltr; EX: a, 1 + else if (src_c_itm == null && trg_c_itm != null) return false; // src != ltr; trg == ltr; EX: 1, a + else if (src_c_itm == null && trg_c_itm == null) { // src != ltr; trg != ltr; EX: 1, 2; _, â’¶ + if (!Bry_.Match(src, src_c_bgn, src_c_end, trg, trg_c_bgn, trg_c_end)) return false;// syms do not match; return false; + } + else { + if (src_c_itm.Utf8_id_lo() != trg_c_itm.Utf8_id_lo()) return false; // lower-case utf8-ids don't match; return false; NOTE: using utf8-ids instead of hash-code to handle asymmetric brys; DATE:2015-09-07 + } + src_c_bgn = src_c_end; + trg_c_bgn = trg_c_end; + } + return src_c_bgn == src_end && trg_c_bgn == trg_end; // only return true if both src and trg read to end of their brys, otherwise "a","ab" will match + } + public static Hash_adp_bry_itm_ci_u8 get_or_new(Gfo_case_mgr case_mgr) { + switch (case_mgr.Tid()) { + case Gfo_case_mgr_.Tid_a7: if (Itm_a7 == null) Itm_a7 = new Hash_adp_bry_itm_ci_u8(case_mgr); return Itm_a7; + case Gfo_case_mgr_.Tid_u8: if (Itm_u8 == null) Itm_u8 = new Hash_adp_bry_itm_ci_u8(case_mgr); return Itm_u8; + case Gfo_case_mgr_.Tid_custom: return new Hash_adp_bry_itm_ci_u8(case_mgr); + default: throw Err_.new_unhandled(case_mgr.Tid()); + } + } + private static Hash_adp_bry_itm_ci_u8 Itm_a7, Itm_u8; +} diff --git a/100_core/src/gplx/Hash_adp_bry_tst.java b/100_core/src/gplx/Hash_adp_bry_tst.java new file mode 100644 index 000000000..71396bdda --- /dev/null +++ b/100_core/src/gplx/Hash_adp_bry_tst.java @@ -0,0 +1,68 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Hash_adp_bry_tst { + @Before public void setup() {fxt.Clear();} private Hash_adp_bry_fxt fxt = new Hash_adp_bry_fxt(); + @Test public void Add_bry() { + fxt .New_cs() + .Add("a0").Add("b0").Add("c0") + .Get_bry_tst("a0").Get_bry_tst("b0").Get_bry_tst("c0").Get_bry_tst("A0", null) + ; + } + @Test public void Get_mid() { + fxt .New_cs() + .Add("a0").Add("b0").Add("c0") + .Get_mid_tst("xyza0xyz", 3, 5, "a0") + .Get_mid_tst("xyza0xyz", 3, 4, null) + ; + } + @Test public void Case_insensitive() { + fxt .New_ci() + .Add("a0").Add("B0").Add("c0") + .Get_bry_tst("a0", "a0") + .Get_bry_tst("A0", "a0") + .Get_bry_tst("b0", "B0") + .Get_bry_tst("B0", "B0") + .Get_mid_tst("xyza0xyz", 3, 5, "a0") + .Get_mid_tst("xyzA0xyz", 3, 5, "a0") + .Count_tst(3) + ; + } +} +class Hash_adp_bry_fxt { + Hash_adp_bry hash; + public void Clear() {} + public Hash_adp_bry_fxt New_cs() {hash = Hash_adp_bry.cs(); return this;} + public Hash_adp_bry_fxt New_ci() {hash = Hash_adp_bry.ci_a7(); return this;} + public Hash_adp_bry_fxt Add(String key) {byte[] key_bry = Bry_.new_u8(key); hash.Add(key_bry, key_bry); return this;} + public Hash_adp_bry_fxt Count_tst(int expd) {Tfds.Eq(expd, hash.Count()); return this;} + public Hash_adp_bry_fxt Get_bry_tst(String key) {return Get_bry_tst(key, key);} + public Hash_adp_bry_fxt Get_bry_tst(String key, String expd) { + byte[] key_bry = Bry_.new_u8(key); + byte[] actl_bry = (byte[])hash.Get_by_bry(key_bry); + Tfds.Eq(expd, String_.new_u8(actl_bry)); + return this; + } + public Hash_adp_bry_fxt Get_mid_tst(String key, int bgn, int end, String expd) { + byte[] key_bry = Bry_.new_u8(key); + byte[] actl_bry = (byte[])hash.Get_by_mid(key_bry, bgn, end); + Tfds.Eq(expd, String_.new_u8(actl_bry)); + return this; + } +} diff --git a/100_core/src/gplx/Int_.java b/100_core/src/gplx/Int_.java new file mode 100644 index 000000000..5a97dc6c2 --- /dev/null +++ b/100_core/src/gplx/Int_.java @@ -0,0 +1,289 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.langs.gfs.*; +public class Int_ implements Gfo_invk { + public static final String Cls_val_name = "int"; + public static final Class Cls_ref_type = Integer.class; + public static final int Base1 = 1; + public static final int Const_dlm_len = 1; + public static final int Const_position_after_char = 1; + public static final int Null = Int_.Min_value; + public static int coerce_(Object v) { + try {String s = String_.as_(v); return s == null ? Int_.cast(v) : Int_.parse(s);} + catch (Exception e) {throw Err_.new_cast(e, int.class, v);} + } + public static int[] Ary_empty = new int[0]; + public static int[] Ary(int... v) {return v;} + public static int[] Ary_copy(int[] ary) {return Ary_copy(ary, ary.length);} + public static int[] Ary_copy(int[] ary, int new_len) { + int old_len = ary.length; + int[] rv = new int[new_len]; + for (int i = 0; i < old_len; i++) + rv[i] = ary[i]; + return rv; + } + public static void Ary_copy_to(int[] src, int src_len, int[] trg) { + for (int i = 0; i < src_len; ++i) + trg[i] = src[i]; + } + public static String Ary_concat(String spr, int... ary) { + Bry_bfr bfr = Bry_bfr_.New(); + int len = ary.length; + for (int i = 0; i < len; ++i) { + if (i != 0) bfr.Add_str_u8(spr); + int itm = ary[i]; + bfr.Add_int_variable(itm); + } + return bfr.To_str_and_clear(); + } + public static int[] AryRng(int bgn, int end) { + int len = end - bgn + 1; + int[] rv = new int[len]; + for (int i = 0; i < len; i++) + rv[i] = bgn + i; + return rv; + } + public static boolean Bounds_chk(int bgn, int end, int len) {return bgn > -1 && end < len;} + public static int parse_or(String raw, int or) { + if (raw == null) return or; + int rawLen = String_.Len(raw); if (rawLen == 0) return or; + int rv = 0, tmp = 0, factor = 1; + for (int i = rawLen; i > 0; i--) { + char c = String_.CharAt(raw, i - 1); + switch (c) { + case '0': tmp = 0; break; case '1': tmp = 1; break; case '2': tmp = 2; break; case '3': tmp = 3; break; case '4': tmp = 4; break; + case '5': tmp = 5; break; case '6': tmp = 6; break; case '7': tmp = 7; break; case '8': tmp = 8; break; case '9': tmp = 9; break; + case '-': rv *= -1; continue; // NOTE: note continue + default: return or; + } + rv += (tmp * factor); + factor *= 10; + } + return rv; + } + public static int EnsureLessThan(int v, int max) {return v >= max ? max : v;} + public static boolean In(int v, int comp0, int comp1) {return v == comp0 || v == comp1;} + public static boolean In(int v, int... ary) { + for (int itm : ary) + if (v == itm) return true; + return false; + } + public static int BoundEnd(int v, int end) {return v >= end ? end - 1 : v;} + public static int Min(int lhs, int rhs) {return lhs < rhs ? lhs : rhs;} + public static int Min_many(int... ary) { + int len = ary.length; if (len == 0) throw Err_.new_wo_type("Min_many requires at least 1 value"); + boolean init = true; + int min = Int_.Min_value; + for (int i = 0; i < len; ++i) { + int val = ary[i]; + if (init) { + min = val; + init = false; + } + else { + if (val < min) + min = val; + } + } + return min; + } + public static int Max(int lhs, int rhs) {return lhs > rhs ? lhs : rhs;} + public static int ModIfNeg1(int v, int or) {return v == -1 ? or : v;} + public static boolean RangeCheck(int v, int max) {return v >= 0 && v < max;} + public static void RangeCheckOrFail_list(int v, int max, String s) {if (v < 0 || v >= max) throw Err_.new_wo_type("bounds check failed", "msg", s, "v", v, "min", 0, "max", max - 1);} + public static void RangeCheckOrFail(int v, int min, int max, String s) {if (v < min || v >= max) throw Err_.new_wo_type("bounds check failed", "msg", s, "v", v, "min", min, "max", max);} + public static boolean Between(int v, int lhs, int rhs) { + int lhsCompare = v == lhs ? 0 : (v < lhs ? -1 : 1); + int rhsCompare = v == rhs ? 0 : (v < rhs ? -1 : 1); + return (lhsCompare * rhsCompare) != 1; // 1 when v is (a) greater than both or (b) less than both + } + public static int Div(int v, float divisor) {return (int)((float)v / divisor);} + public static int DivAndRoundUp(int v, int divisor) { + int whole = v / divisor; + int partial = v % divisor == 0 ? 0 : 1; + return whole + partial; + } + public static int Mult(int v, float multiplier) { + float product = ((float)v * multiplier); // WORKAROUND (DotNet): (int)((float)v * multiplier) returns 0 for 100 and .01f + return (int)product; + } + public static int Compare(int lhs, int rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } + public static int DigitCount(int v) { + int log10 = Log10(v); + return v > -1 ? log10 + 1 : log10 * -1 + 2; + } + public static int Log10(int v) { + if (v == 0) return 0; + int sign = 1; + if (v < 0) { + if (v == Int_.Min_value) return -9; // NOTE: Int_.Min_value * -1 = Int_.Min_value + v *= -1; + sign = -1; + } + int rv = Log10AryLen - 2; // rv will only happen when v == Int_.Max_value + int bgn = 0; + if (v > 1000) { // optimization to reduce number of ops to < 5 + bgn = 3; + if (v > 1000000) bgn = 6; + } + for (int i = bgn; i < Log10AryLen; i++) { + if (v < Log10Ary[i]) {rv = i - 1; break;} + } + return rv * sign; + } public static int[] Log10Ary = new int[] {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, Int_.Max_value}; public static int Log10AryLen = 11; + public Int_ FailIfNeg1(String key, int val) { + if (val < 0) throw Err_.new_wo_type("key must be >= 0", "key", key, "val", val); + return this; + } + public static String To_str_pad_bgn_space(int v, int reqdPlaces) {return To_str_pad_bgn_zero(v, reqdPlaces, Byte_ascii.Space, true);} // EX: 1, 3 returns " 1" + public static String To_str_pad_bgn_zero(int v, int reqdPlaces) {return To_str_pad_bgn_zero(v, reqdPlaces, Byte_ascii.Num_0, true);} // EX: 1, 3 returns "001" + static String To_str_pad_bgn_zero(int val, int places, byte pad_chr, boolean bgn) { + int len = DigitCount(val); + int pad_len = places - len; if (pad_len < 0) return Int_.To_str(val); + Bry_bfr bfr = Bry_bfr_.New(); + boolean neg = val < 0; + if (bgn) { // special logic to handle negative numbers; EX: -1 -> "-001", not "00-1" + if (neg) { + bfr.Add_byte(Byte_ascii.Dash); + val *= -1; + --len; + } + } + else + bfr.Add_int_fixed(val, len); + bfr.Add_byte_repeat(pad_chr, pad_len); + if (bgn) bfr.Add_int_fixed(val, len); // NOTE: neg handled above + return bfr.To_str(); + } + public static int read_(Object o) {String s = String_.as_(o); return s != null ? Int_.parse(s) : Int_.cast(o);} + public static int parse(String raw) {try {return Integer.parseInt(raw);} catch(Exception e) {throw Err_.new_parse_exc(e, int.class, raw);}} + public static int cast(Object obj) {try {return (Integer)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, int.class, obj);}} + public static int cast_or(Object obj, int or) {try {return (Integer)obj;} catch(Exception e) {Err_.Noop(e); return or;}} + public static int Xby_double_(double v) {return (int)v;} + public static String To_str(int v) {return new Integer(v).toString();} + public static String To_str_fmt(int v, String fmt) {return new java.text.DecimalFormat(fmt).format(v);} + public static boolean TypeMatch(Class type) {return type == int.class || type == Integer.class;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_XtoStr_PadBgn)) { + int v = m.ReadInt(GfsCore_.Arg_primitive), pad = m.ReadInt("pad"); + return ctx.Deny() ? (Object)this : To_str_pad_bgn_zero(v, pad); + } + else if (ctx.Match(k, "Add")) { + int v = m.ReadInt(GfsCore_.Arg_primitive), operand = m.ReadInt("operand"); + return ctx.Deny() ? (Object)this : v + operand; + } + else return Gfo_invk_.Rv_unhandled; + } public static final String Invk_XtoStr_PadBgn = "XtoStr_PadBgn"; + public static final Int_ Gfs = new Int_(); + public static int To_int_hex(byte[] src) {return To_int_hex(src, 0, src.length);} + public static int To_int_hex(byte[] src, int bgn, int end) { + int rv = 0; int factor = 1; + for (int i = end - 1; i >= bgn; i--) { + int val = To_int_hex(src[i]); + rv += (val * factor); + factor *= 16; + } + return rv; + } + public static int Subtract_long(long lhs, long rhs) {return (int)(lhs - rhs);} + public static int To_int_hex(byte b) { + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + return b - Byte_ascii.Num_0; + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: case Byte_ascii.Ltr_F: + return b - Byte_ascii.Ltr_A + 10; + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: case Byte_ascii.Ltr_f: + return b - Byte_ascii.Ltr_a + 10; + default: + return -1; + } + } + public static String To_str_hex(int v) {return To_str_hex(Bool_.Y, Bool_.Y, v);} + public static String To_str_hex(boolean zero_pad, boolean upper, int v) { + String rv = Integer.toHexString(v); + int rv_len = String_.Len(rv); + if (zero_pad && rv_len < 8) rv = String_.Repeat("0", 8 - rv_len) + rv; + return upper ? String_.Upper(rv) : rv; + } + public static String To_str(int[] ary) {return To_str(ary, " ");} + public static String To_str(int[] ary, String dlm) { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < ary.length; i++) + sb.Add_spr_unless_first(Int_.To_str(ary[i]), dlm, i); + return sb.To_str(); + } + public static int[] Ary_parse(String raw_str, int reqd_len, int[] or) { + byte[] raw_bry = Bry_.new_a7(raw_str); + int raw_bry_len = raw_bry.length; + int[] rv = new int[reqd_len]; + int cur_val = 0, cur_mult = 1, cur_idx = reqd_len - 1; boolean signed = false; + for (int i = raw_bry_len - 1; i > -2; i--) { + byte b = i == -1 ? Byte_ascii.Comma : raw_bry[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + if (signed) return or; + cur_val += (b - Byte_ascii.Num_0) * cur_mult; + cur_mult *= 10; + break; + case Byte_ascii.Space: case Byte_ascii.Nl: case Byte_ascii.Cr: case Byte_ascii.Tab: + break; + case Byte_ascii.Comma: + if (cur_idx < 0) return or; + rv[cur_idx--] = cur_val; + cur_val = 0; cur_mult = 1; + signed = false; + break; + case Byte_ascii.Dash: + if (signed) return or; + cur_val *= -1; + signed = true; + break; + case Byte_ascii.Plus: // noop; all values positive by default + if (signed) return or; + signed = true; + break; + default: + return or; + } + } + return cur_idx == -1 ? rv : or; // cur_idx == -1 checks for unfilled; EX: Ary_parse("1,2", 3, null) is unfilled + } + public static int[] Ary_parse(String raw_str, String spr) { + String[] ary = String_.Split(raw_str, spr); + int len = ary.length; + int[] rv = new int[len]; + for (int i = 0; i < len; i++) + rv[i] = Int_.parse(ary[i]); + return rv; + } + public static byte[] To_bry(int v) {return Bry_.new_a7(To_str(v));} + public static final int + Min_value = Integer.MIN_VALUE + , Max_value = Integer.MAX_VALUE + , Max_value__31 = 2147483647 + , Neg1 = -1 + , Neg1_count = -1 + ; +} diff --git a/100_core/src/gplx/Int__tst.java b/100_core/src/gplx/Int__tst.java new file mode 100644 index 000000000..b75d6c852 --- /dev/null +++ b/100_core/src/gplx/Int__tst.java @@ -0,0 +1,115 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Int__tst { + @Test public void XtoStr_PadBgn() { +// tst_XtoStr_PadLeft_Zeroes(1 , 3, "001"); // pad +// tst_XtoStr_PadLeft_Zeroes(123 , 3, "123"); // no pad +// tst_XtoStr_PadLeft_Zeroes(1234 , 3, "1234"); // val exceeds pad; confirm noop + tst_XtoStr_PadLeft_Zeroes(-1 , 3, "-01"); // negative + tst_XtoStr_PadLeft_Zeroes(-12 , 3, "-12"); // negative + tst_XtoStr_PadLeft_Zeroes(-123 , 3, "-123"); // negative + tst_XtoStr_PadLeft_Zeroes(-1234 , 3, "-1234"); // negative + } void tst_XtoStr_PadLeft_Zeroes(int val, int zeros, String expd) {Tfds.Eq(expd, Int_.To_str_pad_bgn_zero(val, zeros));} + @Test public void parseOr_() { + tst_ParseOr("", -1); // empty + tst_ParseOr("123", 123); // single + tst_ParseOr("1a", -1); // fail + } void tst_ParseOr(String raw, int expd) {Tfds.Eq(expd, Int_.parse_or(raw, -1));} + @Test public void Between() { + tst_Between(1, 0, 2, true); // simple true + tst_Between(3, 0, 2, false); // simple false + tst_Between(0, 0, 2, true); // bgn true + tst_Between(2, 0, 2, true); // end true + } void tst_Between(int val, int lhs, int rhs, boolean expd) {Tfds.Eq(expd, Int_.Between(val, lhs, rhs));} + @Test public void Xto_fmt() { + tst_XtoStr_fmt(1, "1"); + tst_XtoStr_fmt(1000, "1,000"); + } void tst_XtoStr_fmt(int v, String expd) {Tfds.Eq(expd, Int_.To_str_fmt(v, "#,###"));} + @Test public void AryRng() { + tst_AryRng(1, 3, Int_.Ary(1, 2, 3)); + } void tst_AryRng(int bgn, int end, int[] expd) {Tfds.Eq_ary(expd, Int_.AryRng(bgn, end));} + @Test public void Log10_pos() { + tst_Log10(0, 0); + tst_Log10(1, 0); + tst_Log10(9, 0); + tst_Log10(10, 1); + tst_Log10(100, 2); + tst_Log10(1000000, 6); + tst_Log10(1000000000, 9); + tst_Log10(Int_.Max_value, 9); + } + @Test public void Log10_neg() { + tst_Log10(-1, 0); + tst_Log10(-10, -1); + tst_Log10(-100, -2); + tst_Log10(-1000000, -6); + tst_Log10(-1000000000, -9); + tst_Log10(Int_.Min_value, -9); + tst_Log10(Int_.Min_value + 1, -9); + } + void tst_Log10(int val, int expd) {Tfds.Eq(expd, Int_.Log10(val));} + @Test public void DigitCount() { + tst_DigitCount(0, 1); + tst_DigitCount(9, 1); + tst_DigitCount(100, 3); + tst_DigitCount(-1, 2); + tst_DigitCount(-100, 4); + } void tst_DigitCount(int val, int expd) {Tfds.Eq(expd, Int_.DigitCount(val), Int_.To_str(val));} + @Test public void Log10() { + tst_Log10( 0, 0); + tst_Log10( 1, 0); + tst_Log10( 2, 0); + tst_Log10( 10, 1); + tst_Log10( 12, 1); + tst_Log10( 100, 2); + tst_Log10( 123, 2); + tst_Log10( 1000, 3); + tst_Log10( 1234, 3); + tst_Log10( 10000, 4); + tst_Log10( 12345, 4); + tst_Log10( 100000, 5); + tst_Log10( 123456, 5); + tst_Log10( 1000000, 6); + tst_Log10( 1234567, 6); + tst_Log10( 10000000, 7); + tst_Log10( 12345678, 7); + tst_Log10( 100000000, 8); + tst_Log10( 123456789, 8); + tst_Log10( 1000000000, 9); + tst_Log10( 1234567890, 9); + tst_Log10(Int_.Max_value, 9); + } + @Test public void Xto_int_hex_tst() { + Xto_int_hex("007C", 124); + } void Xto_int_hex(String raw, int expd) {Tfds.Eq(expd, Int_.To_int_hex(Bry_.new_a7(raw)));} + @Test public void Ary_parse() { + Ary_parse__tst("1,2,3" , 3, Int_.Ary_empty, 1, 2, 3); + Ary_parse__tst("123,321,213" , 3, Int_.Ary_empty, 123, 321, 213); + Ary_parse__tst(" 1, 2,3" , 3, Int_.Ary_empty, 1, 2, 3); + Ary_parse__tst("-1,+2,-3" , 3, Int_.Ary_empty, -1, 2, -3); + Ary_parse__tst(Int_.To_str(Int_.Min_value) , 1, Int_.Ary_empty, Int_.Min_value); + Ary_parse__tst(Int_.To_str(Int_.Max_value) , 1, Int_.Ary_empty, Int_.Max_value); + Ary_parse__tst("1,2" , 1, Int_.Ary_empty); + Ary_parse__tst("1" , 2, Int_.Ary_empty); + Ary_parse__tst("a" , 1, Int_.Ary_empty); + Ary_parse__tst("1-2," , 1, Int_.Ary_empty); + } + void Ary_parse__tst(String raw, int reqd_len, int[] or, int... expd) {Tfds.Eq_ary(expd, Int_.Ary_parse(raw, reqd_len, or));} +} diff --git a/100_core/src/gplx/Internal.java b/100_core/src/gplx/Internal.java new file mode 100644 index 000000000..15e516551 --- /dev/null +++ b/100_core/src/gplx/Internal.java @@ -0,0 +1,19 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public @interface Internal {} diff --git a/100_core/src/gplx/Io_mgr.java b/100_core/src/gplx/Io_mgr.java new file mode 100644 index 000000000..07838cb29 --- /dev/null +++ b/100_core/src/gplx/Io_mgr.java @@ -0,0 +1,178 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; import gplx.core.ios.*; /*IoItmFil, IoItmDir..*/ import gplx.core.ios.streams.*; import gplx.core.ios.loaders.*; +public class Io_mgr implements Gfo_evt_mgr_owner { // exists primarily to gather all cmds under gplx namespace; otherwise need to use gplx.core.ios whenever copying/deleting file + public Io_mgr() {evt_mgr = new Gfo_evt_mgr(this);} + public Gfo_evt_mgr Evt_mgr() {return evt_mgr;} private final Gfo_evt_mgr evt_mgr; + public boolean Exists(Io_url url) {return url.Type_dir() ? ExistsDir(url) : ExistsFil(url);} + public boolean ExistsFil(Io_url url) {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).ExistsFil_api(url);} + public void ExistsFilOrFail(Io_url url) {if (!ExistsFil(url)) throw Err_.new_wo_type("could not find file", "url", url);} + public void SaveFilStr(String url, String text) {SaveFilStr_args(Io_url_.new_fil_(url), text).Exec();} + public void SaveFilStr(Io_url url, String text) {SaveFilStr_args(url, text).Exec();} + public IoEngine_xrg_saveFilStr SaveFilStr_args(Io_url url, String text) {return IoEngine_xrg_saveFilStr.new_(url, text);} + public void AppendFilStr(String url, String text) {AppendFilStr(Io_url_.new_fil_(url), text);} + public void AppendFilStr(Io_url url, String text) {SaveFilStr_args(url, text).Append_(true).Exec();} + public void DeleteFil(Io_url url) {DeleteFil_args(url).Exec();} + public IoEngine_xrg_deleteFil DeleteFil_args(Io_url url) {return IoEngine_xrg_deleteFil.new_(url);} + public void MoveFil(Io_url src, Io_url trg) {IoEngine_xrg_xferFil.move_(src, trg).Exec();} + public IoEngine_xrg_xferFil MoveFil_args(Io_url src, Io_url trg, boolean overwrite) {return IoEngine_xrg_xferFil.move_(src, trg).Overwrite_(overwrite);} + public void CopyFil(Io_url src, Io_url trg, boolean overwrite) {IoEngine_xrg_xferFil.copy_(src, trg).Overwrite_(overwrite).Exec();} + public IoEngine_xrg_xferFil CopyFil_args(Io_url src, Io_url trg, boolean overwrite) {return IoEngine_xrg_xferFil.copy_(src, trg).Overwrite_(overwrite);} + public IoRecycleBin RecycleBin() {return recycleBin;} private IoRecycleBin recycleBin = IoRecycleBin.Instance; + public Io_loader Loader() {return loader;} public void Loader_(Io_loader v) {this.loader = v;} private Io_loader loader; + + public IoStream OpenStreamWrite(Io_url url) {return OpenStreamWrite_args(url).Exec();} + public IoEngine_xrg_openWrite OpenStreamWrite_args(Io_url url) {return IoEngine_xrg_openWrite.new_(url);} + public IoItmFil QueryFil(Io_url url) {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).QueryFil(url);} + public void UpdateFilAttrib(Io_url url, IoItmAttrib attrib) {IoEnginePool.Instance.Get_by(url.Info().EngineKey()).UpdateFilAttrib(url, attrib);} + public void UpdateFilModifiedTime(Io_url url, DateAdp modified) {IoEnginePool.Instance.Get_by(url.Info().EngineKey()).UpdateFilModifiedTime(url, modified);} + + public boolean ExistsDir(Io_url url) {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).ExistsDir(url);} + public void CreateDir(Io_url url) {IoEnginePool.Instance.Get_by(url.Info().EngineKey()).CreateDir(url);} + public boolean CreateDirIfAbsent(Io_url url) { + boolean exists = ExistsDir(url); + if (!exists) { + CreateDir(url); + return true; + } + return false; + } + public void Create_fil_ary(Io_fil[] fil_ary) { + for (Io_fil fil : fil_ary) + SaveFilStr(fil.Url(), fil.Data()); + } + public Io_url[] QueryDir_fils(Io_url dir) {return QueryDir_args(dir).ExecAsUrlAry();} + public IoEngine_xrg_queryDir QueryDir_args(Io_url dir) {return IoEngine_xrg_queryDir.new_(dir);} + public void DeleteDirSubs(Io_url url) {IoEngine_xrg_deleteDir.new_(url).Exec();} + public IoEngine_xrg_deleteDir DeleteDir_cmd(Io_url url) {return IoEngine_xrg_deleteDir.new_(url);} + public void DeleteDirDeep(Io_url url) {IoEngine_xrg_deleteDir.new_(url).Recur_().Exec();} + public void DeleteDirDeep_ary(Io_url... urls) {for (Io_url url : urls) IoEngine_xrg_deleteDir.new_(url).Recur_().Exec();} + public int Delete_dir_empty(Io_url url) {return Io_mgr_.Delete_dir_empty(url);} + public void Delete_sub_by_wildcard() { + } + public boolean Truncate_fil(Io_url url, long size) {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).Truncate_fil(url, size);} + public void MoveDirDeep(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.move_(src, trg).Recur_().Exec();} + public IoEngine_xrg_xferDir CopyDir_cmd(Io_url src, Io_url trg) {return IoEngine_xrg_xferDir.copy_(src, trg);} + public void CopyDirSubs(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.copy_(src, trg).Exec();} + public void CopyDirDeep(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.copy_(src, trg).Recur_().Exec();} + public void DeleteDirIfEmpty(Io_url url) { + if (Array_.Len(QueryDir_fils(url)) == 0) + this.DeleteDirDeep(url); + } + public void AliasDir_sysEngine(String srcRoot, String trgRoot) {AliasDir(srcRoot, trgRoot, IoEngine_.SysKey);} + public void AliasDir(String srcRoot, String trgRoot, String engineKey) {IoUrlInfoRegy.Instance.Reg(IoUrlInfo_.alias_(srcRoot, trgRoot, engineKey));} + public IoStream OpenStreamRead(Io_url url) {return OpenStreamRead_args(url).ExecAsIoStreamOrFail();} + public IoEngine_xrg_openRead OpenStreamRead_args(Io_url url) {return IoEngine_xrg_openRead.new_(url);} + public String LoadFilStr(String url) {return LoadFilStr_args(Io_url_.new_fil_(url)).Exec();} + public String LoadFilStr(Io_url url) {return LoadFilStr_args(url).Exec();} + public IoEngine_xrg_loadFilStr LoadFilStr_args(Io_url url) {return IoEngine_xrg_loadFilStr.new_(url);} + public byte[] LoadFilBryOrNull(Io_url url) {return LoadFilBryOr(url, null);} + public byte[] LoadFilBryOr(Io_url url, byte[] or) {return ExistsFil(url) ? LoadFilBry(url) : or;} + public byte[] LoadFilBry(String url) {return LoadFilBry_reuse(Io_url_.new_fil_(url), Bry_.Empty, Int_obj_ref.New_zero());} + public byte[] LoadFilBry(Io_url url) {return LoadFilBry_reuse(url, Bry_.Empty, Int_obj_ref.New_zero());} + public void LoadFilBryByBfr(Io_url url, Bry_bfr bfr) { + Int_obj_ref len = Int_obj_ref.New_zero(); + byte[] bry = LoadFilBry_reuse(url, Bry_.Empty, len); + bfr.Bfr_init(bry, len.Val()); + } + public static final byte[] LoadFilBry_fail = Bry_.Empty; + public byte[] LoadFilBry_reuse(Io_url url, byte[] ary, Int_obj_ref ary_len) { + if (loader != null) { + byte[] rv = loader.Load_fil_as_bry(url); + if (rv != null) return rv; + } + if (!ExistsFil(url)) { + ary_len.Val_(0); + return LoadFilBry_fail; + } + IoStream stream = IoStream_.Null; + try { + stream = OpenStreamRead(url); + int stream_len = (int)stream.Len(); + ary_len.Val_(stream_len); + if (stream_len > ary.length) + ary = new byte[stream_len]; + stream.ReadAry(ary); + return ary; + } + catch (Exception e) {throw Err_.new_wo_type("failed to load file", "url", url.Xto_api(), "e", Err_.Message_lang(e));} + finally {stream.Rls();} + } + public byte[] LoadFilBry_loose(Io_url url) {return Bry_.new_u8(LoadFilStr_loose(url));} + public String LoadFilStr_loose(Io_url url) { + String rv = LoadFilStr_args(url).BomUtf8Convert_(Bool_.Y).MissingIgnored_(Bool_.Y).Exec(); + if (String_.Has(rv, "\r\n")) + rv = String_.Replace(rv, "\r\n", "\n"); + return rv; + } + public void AppendFilBfr(Io_url url, Bry_bfr bfr) {AppendFilByt(url, bfr.Bfr(), 0, bfr.Len()); bfr.ClearAndReset();} + public void AppendFilByt(Io_url url, byte[] val) {AppendFilByt(url, val, 0, val.length);} + public void AppendFilByt(Io_url url, byte[] val, int len) {AppendFilByt(url, val, 0, len);} + public void AppendFilByt(Io_url url, byte[] val, int bgn, int len) { + IoStream stream = IoStream_.Null; + try { + stream = OpenStreamWrite_args(url).Mode_(IoStream_.Mode_wtr_append).Exec(); + stream.Write(val, bgn, len); + } finally {stream.Rls();} + } + public void SaveFilBfr(Io_url url, Bry_bfr bfr) {SaveFilBry(url, bfr.Bfr(), bfr.Len()); bfr.Clear();} + public void SaveFilBry(String urlStr, byte[] val) {SaveFilBry(Io_url_.new_fil_(urlStr), val);} + public void SaveFilBry(Io_url url, byte[] val) {SaveFilBry(url, val, val.length);} + public void SaveFilBry(Io_url url, byte[] val, int len) {SaveFilBry(url, val, 0, len);} + public void SaveFilBry(Io_url url, byte[] val, int bgn, int len) { + IoStream stream = IoStream_.Null; + try { + stream = OpenStreamWrite(url); + stream.Write(val, bgn, len); + } finally {stream.Rls();} + } + public IoEngine InitEngine_mem() {return IoEngine_.Mem_init_();} + public IoEngine InitEngine_mem_(String key) { + IoEngine engine = IoEngine_.mem_new_(key); + IoEnginePool.Instance.Add_if_dupe_use_nth(engine); + IoUrlInfoRegy.Instance.Reg(IoUrlInfo_.mem_(key, key)); + return engine; + } + public boolean DownloadFil(String src, Io_url trg) {return IoEngine_xrg_downloadFil.new_(src, trg).Exec();} + public IoEngine_xrg_downloadFil DownloadFil_args(String src, Io_url trg) {return IoEngine_xrg_downloadFil.new_(src, trg);} + public static final Io_mgr Instance = new Io_mgr(); + public static final int Len_kb = 1024, Len_mb = 1048576, Len_gb = 1073741824, Len_gb_2 = 2147483647; + public static final long Len_mb_long = Len_mb; + public static final long Len_null = -1; + public static final String Evt__fil_created = "fil_created"; +} +class Io_mgr_ { + public static int Delete_dir_empty(Io_url url) { + IoItmDir dir = Io_mgr.Instance.QueryDir_args(url).ExecAsDir(); + int sub_dirs_len = dir.SubDirs().Count(); + int deleted_dirs = 0; + for (int i = 0; i < sub_dirs_len; ++i) { + IoItmDir sub_dir = (IoItmDir)dir.SubDirs().Get_at(i); + deleted_dirs += Io_mgr.Instance.Delete_dir_empty(sub_dir.Url()); + } + if ( dir.SubFils().Count() == 0 + && deleted_dirs == sub_dirs_len + ) { + Io_mgr.Instance.DeleteDirIfEmpty(url); + return 1; + } + else + return 0; + } +} diff --git a/100_core/src/gplx/Io_mgr__tst.java b/100_core/src/gplx/Io_mgr__tst.java new file mode 100644 index 000000000..3db917e98 --- /dev/null +++ b/100_core/src/gplx/Io_mgr__tst.java @@ -0,0 +1,100 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Io_mgr__tst { + @Before public void init() {fxt.Clear();} private final Io_mgr__fxt fxt = new Io_mgr__fxt(); + @Test public void Dir_delete_empty__basic() { + fxt.Exec_itm_create("mem/dir/"); + fxt.Exec_dir_delete_empty("mem/dir/"); + fxt.Test_itm_exists_n("mem/dir/"); + } + @Test public void Dir_delete_empty__no_delete() { + fxt.Exec_itm_create + ( "mem/dir/" + , "mem/dir/fil.txt" + ); + fxt.Exec_dir_delete_empty("mem/dir/"); + fxt.Test_itm_exists_y("mem/dir/"); + } + @Test public void Dir_delete_empty__nested_simple() { + fxt.Exec_itm_create + ( "mem/dir/" + , "mem/dir/1/" + , "mem/dir/1/11/" + ); + fxt.Exec_dir_delete_empty("mem/dir/"); + fxt.Test_itm_exists_n("mem/dir/"); + } + @Test public void Dir_delete_empty__nested_many() { + fxt.Exec_itm_create + ( "mem/dir/" + , "mem/dir/1/" + , "mem/dir/1/11/" + , "mem/dir/2/22/" + , "mem/dir/2/22/222a/" + , "mem/dir/2/22/222b/" + ); + fxt.Exec_dir_delete_empty("mem/dir/"); + fxt.Test_itm_exists_n("mem/dir/"); + } + @Test public void Dir_delete_empty__nested_some() { + fxt.Exec_itm_create + ( "mem/dir/" + , "mem/dir/1/" + , "mem/dir/1/11/" + , "mem/dir/2/22/" + , "mem/dir/2/22/a.txt" + , "mem/dir/2/22/222a/" + , "mem/dir/2/22/222b/" + ); + fxt.Exec_dir_delete_empty("mem/dir/"); + fxt.Test_itm_exists_n + ( "mem/dir/1/" + , "mem/dir/1/11/" + , "mem/dir/2/22/222a/" + , "mem/dir/2/22/222b/" + ); + fxt.Test_itm_exists_y + ( "mem/dir/" + , "mem/dir/2/22/" + ); + } +} +class Io_mgr__fxt { + public void Clear() {Io_mgr.Instance.InitEngine_mem();} + public void Exec_itm_create(String... ary) { + for (String itm : ary) { + Io_url url = Io_url_.new_any_(itm); + if (url.Type_dir()) + Io_mgr.Instance.CreateDir(url); + else + Io_mgr.Instance.SaveFilStr(url, url.NameAndExt()); + } + } + public void Exec_dir_delete_empty(String url) {Io_mgr.Instance.Delete_dir_empty(Io_url_.mem_dir_(url));} + public void Test_itm_exists_n(String... ary) {Test_itm_exists(Bool_.N, ary);} + public void Test_itm_exists_y(String... ary) {Test_itm_exists(Bool_.Y, ary);} + public void Test_itm_exists(boolean expd, String... ary) { + for (String itm : ary) { + Io_url url = Io_url_.new_any_(itm); + boolean actl = url.Type_dir() ? Io_mgr.Instance.ExistsDir(url) : Io_mgr.Instance.ExistsFil(url); + Tfds.Eq(expd, actl, itm); + } + } +} diff --git a/100_core/src/gplx/Io_url.java b/100_core/src/gplx/Io_url.java new file mode 100644 index 000000000..b0813b87d --- /dev/null +++ b/100_core/src/gplx/Io_url.java @@ -0,0 +1,91 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.core.ios.*; /*IoUrlInfo*/ import gplx.core.envs.*; import gplx.langs.htmls.*; import gplx.core.interfaces.*; +public class Io_url implements CompareAble, ParseAble, Gfo_invk { //_20101005 URL:doc/Io_url.txt + public IoUrlInfo Info() {return info;} IoUrlInfo info; + public String Raw() {return raw;} final String raw; + public byte[] RawBry() {return Bry_.new_u8(raw);} + public String To_http_file_str() {return String_.Len_eq_0(raw) ? String_.Empty : String_.Concat (Http_file_str, Http_file_str_encoder.Encode_str(raw));} + public byte[] To_http_file_bry() {return String_.Len_eq_0(raw) ? Bry_.Empty : Bry_.Add (Http_file_bry, Http_file_str_encoder.Encode_bry(raw));} + public static Url_encoder_interface Http_file_str_encoder = Url_encoder_interface_same.Instance; + public static final String Http_file_str = "file:///"; + public static final int Http_file_len = String_.Len(Http_file_str); + public static final byte[] Http_file_bry = Bry_.new_a7(Http_file_str); + public boolean Type_dir() {return info.IsDir(raw);} public boolean Type_fil() {return !info.IsDir(raw);} + public Io_url OwnerDir() {return Io_url_.new_inf_(info.OwnerDir(raw), info);} + public Io_url OwnerRoot() {return Io_url_.new_inf_(info.OwnerRoot(raw), info);} + public String NameAndExt() {return info.NameAndExt(raw);} + public String NameAndExt_noDirSpr() {return this.Type_dir() ? this.NameOnly() : this.NameAndExt();} + public String NameOnly() {return info.NameOnly(raw);} + public String Ext() {return info.Ext(raw);} + public String Xto_api() {return info.Xto_api(raw);} + public String XtoCaseNormalized() {return String_.CaseNormalize(info.CaseSensitive(), raw);} + public Io_url GenSubDir(String subDirName) {return Io_url_.new_inf_(String_.Concat(raw, subDirName, info.DirSpr()), info);} + public Io_url GenSubDir_nest(String... ary) {return GenSub(false, ary);} + public Io_url GenSubFil(String val) {return Io_url_.new_inf_(raw + val, info);} + public Io_url GenSubFil_ary(String... ary) {return Io_url_.new_inf_(raw + String_.Concat(ary), info);} + public Io_url GenSubFil_nest(String... ary) {return GenSub(true, ary);} + public Io_url GenNewNameAndExt(String val) {return this.OwnerDir().GenSubFil(val);} + public Io_url GenNewNameOnly(String val) {return this.OwnerDir().GenSubFil(val + this.Ext());} + public Io_url GenNewExt(String val) {return this.OwnerDir().GenSubFil(this.NameOnly() + val);} + public String Gen_sub_path_for_os(String val) { + if (Op_sys.Cur().Tid_is_wnt()) val = String_.Replace(val, Op_sys.Lnx.Fsys_dir_spr_str(), Op_sys.Wnt.Fsys_dir_spr_str()); + return raw + val; + } + public String GenRelUrl_orEmpty(Io_url dir) { + String dirRaw = dir.Raw(); + return String_.Has_at_bgn(raw, dirRaw) + ? String_.DelBgn(raw, String_.Len(dirRaw)) + : String_.Empty; + } + public List_adp XtoNames() { + List_adp list = List_adp_.New(); + Io_url cur = this; + while (!cur.EqNull()) { + list.Add(cur.NameAndExt_noDirSpr()); + cur = cur.OwnerDir(); + } + list.Reverse(); + return list; + } + public Io_url GenParallel(Io_url oldRoot, Io_url newRoot) {return newRoot.GenSubFil_ary(GenRelUrl_orEmpty(oldRoot));} + public boolean Eq(Object obj) {if (obj == null) return false; return String_.Eq(raw, ((Io_url)obj).raw);} + public boolean EqNull() {return this.Eq(Io_url_.Empty);} + Io_url GenSub(boolean isFil, String[] ary) { + String_bldr sb = String_bldr_.new_().Add(raw); + int len = Array_.Len(ary); + for (int i = 0; i < len; i++) { + sb.Add(ary[i]); + if (isFil && i == len - 1) break; // do not add closing backslash if last term + sb.Add(info.DirSpr()); + } + return Io_url_.new_inf_(sb.To_str(), info); + } + public Object ParseAsObj(String raw) {return Io_url_.new_any_(raw);} + @Override public String toString() {return raw;} + public int compareTo(Object obj) {return CompareAble_.Compare_obj(raw, ((Io_url)obj).raw);} + @Override public boolean equals(Object obj) {return String_.Eq(raw, Io_url_.as_(obj).raw);} + @Override public int hashCode() {return raw.hashCode();} + @gplx.Internal protected Io_url(String raw, IoUrlInfo info) {this.raw = raw; this.info = info;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_to_http_file)) return To_http_file_str(); + else if (ctx.Match(k, Invk_gen_sub_path_for_os)) return Gen_sub_path_for_os(m.ReadStr("v")); + else return Gfo_invk_.Rv_unhandled; + } static final String Invk_to_http_file = "to_http_file", Invk_gen_sub_path_for_os = "gen_sub_path_for_os"; +} diff --git a/100_core/src/gplx/Io_url_.java b/100_core/src/gplx/Io_url_.java new file mode 100644 index 000000000..9aa8e9aab --- /dev/null +++ b/100_core/src/gplx/Io_url_.java @@ -0,0 +1,113 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.ios.*; /*IoUrlInfo_*/ import gplx.core.stores.*; import gplx.core.envs.*; +public class Io_url_ { + public static final Io_url Empty = new Io_url("", IoUrlInfo_.Nil); + public static final Io_url NullPtr = null; + public static final Io_url Parser = new Io_url("", IoUrlInfo_.Nil); + public static Io_url as_(Object obj) {return obj instanceof Io_url ? (Io_url)obj : null;} + public static Io_url cast(Object obj) {try {return (Io_url)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, Io_url.class, obj);}} + public static Io_url Usr() { + if (usr_dir == null) { + switch (Op_sys.Cur().Tid()) { + case Op_sys.Tid_wnt: usr_dir = Io_url_.new_inf_("C:\\", IoUrlInfo_.Wnt); break; + case Op_sys.Tid_lnx: usr_dir = Io_url_.new_inf_(String_.Format("/home/{0}/", System_.Prop__user_name()), IoUrlInfo_.Lnx); break; + case Op_sys.Tid_osx: usr_dir = Io_url_.new_inf_(String_.Format("/Users/{0}/", System_.Prop__user_name()), IoUrlInfo_.Lnx); break; + case Op_sys.Tid_drd: usr_dir = Io_url_.new_inf_(String_.Format("/mnt/{0}/", System_.Prop__user_name()), IoUrlInfo_.Lnx); break; + default: throw Err_.new_unhandled(Op_sys.Cur().Tid()); + } + } + return usr_dir; + } static Io_url usr_dir; + public static Io_url Usr_Gplx() {return Usr().GenSubDir("gplx");} + public static Io_url mem_dir_(String raw) { + raw = EndsWith_or_add(raw, Op_sys.Lnx.Fsys_dir_spr_str()); + return new Io_url(raw, IoUrlInfoRegy.Instance.Match(raw)); + } + public static Io_url mem_fil_(String raw) {return new_inf_(raw, IoUrlInfoRegy.Instance.Match(raw));} + public static Io_url wnt_fil_(String raw) {return new_inf_(raw, IoUrlInfo_.Wnt);} + public static Io_url wnt_dir_(String raw) {return new_inf_(EndsWith_or_add(raw, Op_sys.Wnt.Fsys_dir_spr_str()), IoUrlInfo_.Wnt);} + public static Io_url lnx_fil_(String raw) {return new_inf_(raw, IoUrlInfo_.Lnx);} + public static Io_url lnx_dir_(String raw) {return new_inf_(EndsWith_or_add(raw, Op_sys.Lnx.Fsys_dir_spr_str()), IoUrlInfo_.Lnx);} + public static Io_url new_fil_(String raw) {return new_any_(raw);} + public static Io_url new_dir_(String raw) {return new_any_(raw);} // NOTE: for now, same as new_fil; stack overflow when doing new_dir + public static Io_url new_dir_infer(String raw) {return Op_sys.Cur().Tid_is_wnt() ? wnt_dir_(raw) : lnx_dir_(raw);} + public static Io_url new_any_(String raw) {return new_inf_(raw, IoUrlInfoRegy.Instance.Match(raw));} + public static Io_url new_inf_(String raw, IoUrlInfo info) {return String_.Eq(raw, "") ? Io_url_.Empty : new Io_url(raw, info);} + public static Io_url New__http_or_fail(String raw) {return New__http_or_fail(Bry_.new_u8(raw));} + public static Io_url New__http_or_fail(byte[] raw) { + Io_url rv = New__http_or_null(raw); + if (rv == null) throw Err_.new_wo_type("url:invalid http_file raw", "raw", raw); + return rv; + } + public static Io_url New__http_or_null(String raw) {return New__http_or_null(Bry_.new_u8(raw));} + public static Io_url New__http_or_null(byte[] raw) { + int len = raw.length; + if (!Bry_.Has_at_bgn(raw, Io_url.Http_file_bry, 0, len)) return null; // doesn't start with "file:///"; return null; + + // bld rv; note that wnt has to convert / to \ + byte[] rv = null; + if (Op_sys.Cur().Tid_is_wnt()) { + int rv_len = len - Io_url.Http_file_len; + rv = new byte[rv_len]; + for (int i = 0; i < rv_len; ++i) { + byte b = raw[i + Io_url.Http_file_len]; + if (b == Op_sys.Dir_spr__lnx) b = Op_sys.Dir_spr__wnt; + rv[i] = b; + } + } + else + rv = Bry_.Mid(raw, Io_url.Http_file_len); + return rv == null ? null : new_any_(String_.new_u8(rv)); + } + + public static Io_url store_orFail_(SrlMgr mgr, String key, Io_url v) { + String s = mgr.SrlStrOr(key, v.Raw()); + return (mgr.Type_rdr()) ? Io_url_.new_any_(s) : v; + } + public static Io_url store_orSelf_(SrlMgr mgr, String key, Io_url v) { + String s = mgr.SrlStrOr(key, v.Raw()); + return (mgr.Type_rdr()) ? Io_url_.new_any_(s) : v; + } + public static Io_url rdrOr_(DataRdr rdr, String key, Io_url or) { + String val = rdr.ReadStrOr(key, null); if (val == null) return or; // NOTE: val == null also checks for rdr == DataRdr_.Null + return Io_url_.new_any_(val); + } + static String EndsWith_or_add(String raw, String endsWith) { + if (String_.Has_at_end(raw, endsWith)) return raw; + return raw += endsWith; + } + public static Io_url Rel_dir(String s) {return IsAbs(s) ? Io_url_.new_dir_(s) : Env_.AppUrl().OwnerDir().GenSubDir(s);} + public static Io_url Rel_fil(String s) {return IsAbs(s) ? Io_url_.new_fil_(s) : Env_.AppUrl().OwnerDir().GenSubFil(s);} + static boolean IsAbs(String s) { + return String_.Has_at_bgn(s, Op_sys.Lnx.Fsys_dir_spr_str()) + || (String_.Len(s) > 2 + && ( (String_.CharAt(s, 1) == ':' && String_.CharAt(s, 2) == '\\') + || (String_.CharAt(s, 1) == '\\' && String_.CharAt(s, 2) == '\\') + ) + ); + } + public static Io_url[] Ary(String... ary) { + int len = ary.length; + Io_url[] rv = new Io_url[len]; + for (int i = 0; i < len; ++i) + rv[i] = Io_url_.new_any_(ary[i]); + return rv; + } +} diff --git a/100_core/src/gplx/Io_url__tst.java b/100_core/src/gplx/Io_url__tst.java new file mode 100644 index 000000000..83ef55a11 --- /dev/null +++ b/100_core/src/gplx/Io_url__tst.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; import gplx.core.tests.*; import gplx.core.envs.*; +public class Io_url__tst { + @Before public void init() {fxt.Clear();} private final Io_url__fxt fxt = new Io_url__fxt(); + @Test public void Basic__lnx() {fxt.Test__New__http_or_null(Bool_.N, "file:///C:/a.txt", "C:/a.txt");} + @Test public void Basic__wnt() {fxt.Test__New__http_or_null(Bool_.Y, "file:///C:/a.txt", "C:\\a.txt");} + @Test public void Null() {fxt.Test__New__http_or_null(Bool_.N, "C:/a.txt", null);} +} +class Io_url__fxt { + public void Clear() {Io_mgr.Instance.InitEngine_mem();} + public void Test__New__http_or_null(boolean os_is_wnt, String raw, String expd) { + Op_sys.Cur_(os_is_wnt ? Op_sys.Tid_wnt : Op_sys.Tid_lnx); + Gftest.Eq__obj_or_null(expd, Io_url_.New__http_or_null(raw)); + } +} diff --git a/100_core/src/gplx/Keyval.java b/100_core/src/gplx/Keyval.java new file mode 100644 index 000000000..d53c7d0d8 --- /dev/null +++ b/100_core/src/gplx/Keyval.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Keyval implements To_str_able { + @gplx.Internal protected Keyval(int key_tid, Object key, Object val) {this.key_tid = key_tid; this.key = key; this.val = val;} + public String Key() {return Object_.Xto_str_strict_or_null(key);} + public Object Key_as_obj() {return key;} private Object key; + public int Key_tid() {return key_tid;} private int key_tid; + public Object Val() {return val;} private Object val; + public String Val_to_str_or_empty() {return Object_.Xto_str_strict_or_empty(val);} + public String Val_to_str_or_null() {return Object_.Xto_str_strict_or_null(val);} + public byte[] Val_to_bry() {return Bry_.new_u8(Object_.Xto_str_strict_or_null(val));} + public Keyval Key_(Object v) {this.key = v; return this;} + public Keyval Val_(Object v) {this.val = v; return this;} + public String To_str() {return Key() + "=" + Object_.Xto_str_strict_or_null_mark(val);} + @Override public String toString() {return To_str();} +} diff --git a/100_core/src/gplx/Keyval_.java b/100_core/src/gplx/Keyval_.java new file mode 100644 index 000000000..c7001f2bd --- /dev/null +++ b/100_core/src/gplx/Keyval_.java @@ -0,0 +1,103 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; +public class Keyval_ { + public static final Keyval[] Ary_empty = new Keyval[0]; + public static Keyval[] Ary(Keyval... ary) {return ary;} + public static Keyval[] Ary_cast_(Object o) { + try {return (Keyval[])o;} + catch (Exception e) {throw Err_.new_cast(e, Keyval.class, o);} + } + public static Keyval[] Ary_insert(Keyval[] orig, boolean insert_at_end, Keyval... vals) { + int orig_len = orig.length, vals_len = vals.length; + int rv_len = orig_len + vals_len; + Keyval[] rv = new Keyval[rv_len]; + int vals_bgn = 0 , vals_end = vals_len; + int orig_bgn = vals_len , orig_end = rv_len; + if (insert_at_end) { + orig_bgn = 0 ; orig_end = orig_len; + vals_bgn = orig_len ; vals_end = rv_len; + } + for (int i = orig_bgn; i < orig_end; i++) + rv[i] = orig[i - orig_bgn]; + for (int i = vals_bgn; i < vals_end; i++) + rv[i] = vals[i - vals_bgn]; + return rv; + } + public static String Ary_to_str(Keyval... ary) { + String_bldr sb = String_bldr_.new_(); + int len = ary.length; + for (int i = 0; i < len; i++) { + Keyval itm = ary[i]; + sb.Add(itm.Key()).Add("="); + Object itm_val = itm.Val(); + if (Type_adp_.Eq_typeSafe(itm_val, Keyval[].class)) + sb.Add(Ary_to_str((Keyval[])itm_val)); + else + sb.Add(Object_.Xto_str_strict_or_null_mark(itm_val)); + sb.Add_char_nl(); + } + return sb.To_str(); + } + public static Object Ary_get_by_key_or_null(Keyval[] ary, String key) { + int len = ary.length; + for (int i = 0; i < len; i++) { + Keyval kv = ary[i]; + if (String_.Eq(kv.Key(), key)) return kv.Val(); + } + return null; + } + public static String Ary__to_str__nest(Keyval... ary) { + Bry_bfr bfr = Bry_bfr_.New(); + Ary__to_str__nest(bfr, 0, ary); + return bfr.To_str_and_clear(); + } + private static void Ary__to_str__nest(Bry_bfr bfr, int indent, Keyval[] ary) { + int len = ary.length; + for (int i = 0; i < len; ++i) { + Keyval itm = ary[i]; + if (indent > 0) + bfr.Add_byte_repeat(Byte_ascii.Space, indent * 2); // add indent : " " + bfr.Add_str_u8(Object_.Xto_str_strict_or_empty(itm.Key())).Add_byte_eq();// add key + eq : "key=" + Object val = itm.Val(); + if (val == null) + bfr.Add_str_a7(String_.Null_mark); + else { + Class val_type = Type_adp_.ClassOf_obj(val); + if (Type_adp_.Eq(val_type, Keyval[].class)) { // val is Keyval[]; recurse + bfr.Add_byte_nl(); // add nl : "\n" + Ary__to_str__nest(bfr, indent + 1, (Keyval[])val); + continue; // don't add \n below + } + else if (Type_adp_.Eq(val_type, Bool_.Cls_ref_type)) { // val is boolean + boolean val_as_bool = Bool_.cast(val); + bfr.Add(val_as_bool ? Bool_.True_bry : Bool_.False_bry); // add "true" or "false"; don't call toString + } + else + bfr.Add_str_u8(Object_.Xto_str_strict_or_null_mark(val)); // call toString() + } + bfr.Add_byte_nl(); + } + } + public static Keyval as_(Object obj) {return obj instanceof Keyval ? (Keyval)obj : null;} + public static Keyval new_(String key) {return new Keyval(Type_adp_.Tid__str, key, key);} + public static Keyval new_(String key, Object val) {return new Keyval(Type_adp_.Tid__str, key, val);} + public static Keyval int_(int key, Object val) {return new Keyval(Type_adp_.Tid__int, key, val);} + public static Keyval obj_(Object key, Object val) {return new Keyval(Type_adp_.Tid__obj, key, val);} +} diff --git a/100_core/src/gplx/Keyval_hash.java b/100_core/src/gplx/Keyval_hash.java new file mode 100644 index 000000000..275d4c831 --- /dev/null +++ b/100_core/src/gplx/Keyval_hash.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Keyval_hash { + private final Ordered_hash hash = Ordered_hash_.New(); + public int Count() {return hash.Count();} + public Keyval_hash Clear() {hash.Clear(); return this;} + public boolean Has(String key) {return hash.Has(key);} + public Keyval Get_at(int i) {return (Keyval)hash.Get_at(i);} + public Object Get_val_or(String key, Object or) {Keyval rv = Get_kvp_or_null(key); return rv == null ? or : rv.Val();} + public Object Get_val_or_null(String key) {return Get_val_or(key, null);} + public Object Get_val_or_fail(String key) {return Keyval_.as_(hash.Get_by_or_fail(key)).Val();} + public String Get_val_as_str_or_fail(String key) {return (String)Get_val_or_fail(key);} + public Keyval Get_kvp_or_null(String key) {return Keyval_.as_(hash.Get_by(key));} + public Keyval_hash Add(Keyval kv) {hash.Add(kv.Key(), kv); return this;} + public Keyval_hash Add(String key, Object val) {hash.Add(key, Keyval_.new_(key, val)); return this;} + public Keyval_hash Add_if_dupe_use_nth(String key, Object val) {hash.Add_if_dupe_use_nth(key, Keyval_.new_(key, val)); return this;} + public void Del(String key) {hash.Del(key);} + public Keyval[] To_ary() { + int len = this.Count(); + Keyval[] rv = new Keyval[len]; + for (int i = 0; i < len; ++i) + rv[i] = this.Get_at(i); + return rv; + } +} \ No newline at end of file diff --git a/100_core/src/gplx/Keyval_list.java b/100_core/src/gplx/Keyval_list.java new file mode 100644 index 000000000..17ab35e88 --- /dev/null +++ b/100_core/src/gplx/Keyval_list.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Keyval_list { + public int Count() {return list.Count();} private final List_adp list = List_adp_.New(); + public void Clear() {list.Clear();} + public Keyval Get_at(int i) {return (Keyval)list.Get_at(i);} + public Keyval_list Add(String key, Object val) {list.Add(Keyval_.new_(key, val)); return this;} + public Keyval[] To_ary() {return (Keyval[])list.To_ary(Keyval.class);} + public String To_str() { + Bry_bfr bfr = Bry_bfr_.New(); + int len = list.Count(); + for (int i = 0; i < len; ++i) { + Keyval kv = (Keyval)list.Get_at(i); + if (i == 0) bfr.Add_byte_space(); + bfr.Add_str_u8(kv.Key()).Add_byte_eq().Add_str_u8(kv.Val_to_str_or_empty()); + } + return bfr.To_str_and_clear(); + } + public static Keyval_list New_with_one(String key, Object val) {return new Keyval_list().Add(key, val);} +} diff --git a/100_core/src/gplx/List_adp.java b/100_core/src/gplx/List_adp.java new file mode 100644 index 000000000..8aa575830 --- /dev/null +++ b/100_core/src/gplx/List_adp.java @@ -0,0 +1,77 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.lists.*; /*EnumerAble,ComparerAble*/ +public interface List_adp extends EnumerAble, List_adp__getable { + int Count(); + Object Get_at_last(); + void Add(Object o); + void Add_at(int i, Object o); + void Add_many(Object... ary); + void Del(Object o); + void Del_at(int i); + void Del_range(int bgn, int end); + void Clear(); + int Idx_of(Object o); + int Idx_last(); + Object To_ary(Class memberType); + Object To_ary_and_clear(Class memberType); + String[] To_str_ary(); + String[] To_str_ary_and_clear(); + String To_str(); + Object[] To_obj_ary(); + void Resize_bounds(int i); + void Move_to(int src, int trg); + void Reverse(); + void Sort(); + void Sort_by(ComparerAble comparer); + void Shuffle(); +} +class List_adp_obj extends List_adp_base implements List_adp { + public List_adp_obj() {super();} + public List_adp_obj(int v) {super(v);} +} +class List_adp_noop implements List_adp { + public int Count() {return 0;} + public int Len() {return 0;} + public Object Get_at(int i) {return null;} + public Object Get_at_last() {return null;} + public Object PopLast() {return null;} + public void Add(Object o) {} + public void Add_at(int i, Object o) {} + public void Add_many(Object... ary) {} + public void Del(Object o) {} + public void Del_at(int i) {} + public void Del_range(int bgn, int end) {} + public void Clear() {} + public int Idx_last() {return -1;} + public int Idx_of(Object o) {return List_adp_.Not_found;} + public void Move_to(int elemPos, int newPos) {} + public void Resize_bounds(int i) {} + public Object To_ary(Class memberType) {return Object_.Ary_empty;} + public Object To_ary_and_clear(Class memberType) {return Object_.Ary_empty;} + public String[] To_str_ary() {return String_.Ary_empty;} + public String[] To_str_ary_and_clear() {return To_str_ary();} + public String To_str() {return "";} + public Object[] To_obj_ary() {return Object_.Ary_empty;} + public java.util.Iterator iterator() {return Iterator_null.Instance;} + public void Reverse() {} + public void Sort() {} + public void Sort_by(ComparerAble comparer) {} + public void Shuffle() {} +} diff --git a/100_core/src/gplx/List_adp_.java b/100_core/src/gplx/List_adp_.java new file mode 100644 index 000000000..ced994c1f --- /dev/null +++ b/100_core/src/gplx/List_adp_.java @@ -0,0 +1,55 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.lists.*; +public class List_adp_ { + public static final List_adp Noop = new List_adp_noop(); + public static List_adp New() {return new List_adp_obj();} + public static List_adp New_w_size(int v) {return new List_adp_obj(v);} + public static List_adp New_by_many(Object... ary) { + List_adp rv = new List_adp_obj(); + rv.Add_many(ary); + return rv; + } + public static void Del_at_last(List_adp list) {list.Del_at(list.Count() - 1);} + public static Object Pop(List_adp list) { + int lastIdx = list.Count() - 1; + Object rv = list.Get_at(lastIdx); + list.Del_at(lastIdx); + return rv; + } + public static Object Pop_first(List_adp list) { // NOTE: dirty way of implementing FIFO queue; should not be used with lists with many members + Object rv = list.Get_at(0); + list.Del_at(0); + return rv; + } + public static Object Pop_last(List_adp list) { + int last_idx = list.Count() - 1; + Object rv = list.Get_at(last_idx); + list.Del_at(last_idx); + return rv; + } + public static Object Pop_or(List_adp list, Object or) { + int list_len = list.Count(); if (list_len == 0) return or; + int last_idx = list_len - 1; + Object rv = list.Get_at(last_idx); + list.Del_at(last_idx); + return rv; + } + public static final int Not_found = -1, Base1 = 1; +} diff --git a/100_core/src/gplx/List_adp_base.java b/100_core/src/gplx/List_adp_base.java new file mode 100644 index 000000000..01235b044 --- /dev/null +++ b/100_core/src/gplx/List_adp_base.java @@ -0,0 +1,175 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.core.lists.*; +public abstract class List_adp_base implements List_adp, Gfo_invk { + private Object[] list; private int count; + public List_adp_base(int capacity) { + this.list = new Object[capacity]; + } + public java.util.Iterator iterator() { + if (count == 0) + return Iterator_null.Instance; + else + return new Iterator_objAry(list, count); + } + public void Add_many(Object... ary) {for (Object o : ary) Add_base(o);} + public int Len() {return count;} + public int Count() {return count;} + public int Idx_last() {return count - 1;} + protected Object Get_at_base(int index) {if (index >= count || index < 0) throw Err_.new_missing_idx(index, count); + return list[index]; + } + protected void Add_base(Object o) { + if (count == Array_.Len_obj(list)) Resize_expand(); + list[count] = o; + count++; + } + protected int Del_base(Object o) { + int index = IndexOf_base(o); if (index == List_adp_.Not_found) return List_adp_.Not_found; + this.Del_at(index); + return index; + } + public void Del_range(int delBgn, int delEnd) { + BoundsChk(delBgn, delEnd, count); + if (delBgn == 0 && delEnd == count - 1) { // entire list deleted; call .Clear, else will have 0 elem array + this.Clear(); + return; + } + int delLen = (delEnd - delBgn) + 1; // EX: 0,2 creates 3 len ary + int newLen = count - delLen; + Object[] newList = new Object[newLen]; + if (delBgn != 0) // copy elements < delBgn; skip if delBgn == 0 + Array_.Copy_to(list, 0, newList, 0, delBgn); + if (delEnd != count -1 ) // copy elements > delEnd; skip if delEnd == lastIdx + Array_.Copy_to(list, delEnd + 1, newList, delBgn, newLen - delBgn); + list = newList; + count = list.length; + } + protected int IndexOf_base(Object o) { + for (int i = 0; i < count; i++) + if (Object_.Eq(list[i], o)) return i; + return List_adp_.Not_found; + } + @gplx.Virtual public void Clear() { + for (int i = 0; i < count; i++) + list[i] = null; + count = 0; + } + @gplx.Virtual public void Del_at(int index) {if (index >= count || index < 0) throw Err_.new_missing_idx(index, count); + Collapse(index); + count--; + } + public void Move_to(int src, int trg) {if (src >= count || src < 0) throw Err_.new_missing_idx(src, count); if (trg >= count || trg < 0) throw Err_.new_missing_idx(trg, count); + if (src == trg) return; // position not changed + Object o = list[src]; + int dif = trg > src ? 1 : -1; + for (int i = src; i != trg; i += dif) + list[i] = list[i + dif]; + list[trg] = o; + } + protected void AddAt_base(int pos, Object o) { + if (count + 1 >= Array_.Len_obj(list)) Resize_expand(); + for (int i = count; i > pos; i--) + list[i] = list[i - 1]; + list[pos] = o; + count = count + 1; + } + public void Resize_bounds(int i) { + Resize_expand(i); + } + public void Sort() {Sort_by(null);} + public void Sort_by(ComparerAble comparer) {List_adp_sorter.new_().Sort(list, count, true, comparer);} + public void Reverse() { + int mid = count / 2; // no need to reverse pivot; ex: for 3 elements, only 1 and 3 need to be exchanged; 2 stays inplace + for (int lhs = 0; lhs < mid; lhs++) { + int rhs = count - lhs - 1; // -1 b/c list[count] is not real element + Object temp = list[lhs]; + list[lhs] = list[rhs]; + list[rhs] = temp; + } + } + @gplx.Virtual public void Shuffle() {// REF: Fisher-Yates shuffle + RandomAdp random = RandomAdp_.new_(); + for (int i = count; i > 1; i--) { + int rndIdx = random.Next(i); + Object tmp = list[rndIdx]; + list[rndIdx] = list[i-1]; + list[i-1] = tmp; + } + } + public Object Get_at(int i) {return Get_at_base(i);} + public Object Get_at_last() {if (count == 0) throw Err_.new_invalid_op("cannot call Get_at_last on empty list"); return Get_at_base(count - 1);} + public void Add(Object item) {Add_base(item);} + public void Add_at(int i, Object o) {AddAt_base(i, o);} + public void Del(Object item) {Del_base(item);} + public int Idx_of(Object o) {return IndexOf_base(o);} + public List_adp_base() { + list = new Object[Len_initial]; + } + private static final int Len_initial = 8; + public Object To_ary_and_clear(Class memberType) {Object rv = To_ary(memberType); this.Clear(); return rv;} + public Object To_ary(Class memberType) { + Object rv = Array_.Create(memberType, count); + for (int i = 0; i < count; i++) + Array_.Set_at(rv, i, list[i]); + return rv; + } + public String[] To_str_ary_and_clear() {String[] rv = To_str_ary(); this.Clear(); return rv;} + public String[] To_str_ary() {return (String[])To_ary(String.class);} + public Object[] To_obj_ary() { + Object[] rv = new Object[count]; + for (int i = 0; i < count; ++i) + rv[i] = list[i]; + return rv; + } + public String To_str() { + Bry_bfr bfr = Bry_bfr_.New(); + for (int i = 0; i < count; ++i) + bfr.Add_str_u8(Object_.Xto_str_strict_or_null_mark(list[i])).Add_byte_nl(); + return bfr.To_str_and_clear(); + } + private void BoundsChk(int bgn, int end, int len) { + if ( bgn >= 0 && bgn < len + && end >= 0 && end < len + && bgn <= end + ) return; + throw Err_.new_wo_type("bounds check failed", "bgn", bgn, "end", end, "len", len); + } + void Resize_expand() {Resize_expand(count * 2);} + void Resize_expand(int newCount) { + Object[] trg = new Object[newCount]; + for (int i = 0; i < count; i++) { + trg[i] = list[i]; + list[i] = null; + } + list = trg; + } + void Collapse(int index) { + for (int i = index; i < count; i++) { + list[i] = (i == count - 1) ? null : list[i + 1]; + } + } + @gplx.Internal protected int Capacity() {return Array_.Len_obj(list);} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_len)) return count; + else if (ctx.Match(k, Invk_get_at)) return Get_at(m.ReadInt("v")); + else return Gfo_invk_.Rv_unhandled; +// return this; + } private static final String Invk_len = "len", Invk_get_at = "get_at"; +} diff --git a/100_core/src/gplx/List_adp_tst.java b/100_core/src/gplx/List_adp_tst.java new file mode 100644 index 000000000..f101b8df8 --- /dev/null +++ b/100_core/src/gplx/List_adp_tst.java @@ -0,0 +1,222 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class List_adp_tst { + @Before public void setup() { + list = List_adp_.New(); + listBase = (List_adp_base)list; + } List_adp list; List_adp_base listBase; + @Test public void Add() { + Tfds.Eq(0, list.Count()); + + list.Add("0"); + Tfds.Eq(1, list.Count()); + } + @Test public void Add_changeCapacity() { + int capacity = 8; + for (int i = 0; i < capacity; i++) + list.Add("0"); + Tfds.Eq(capacity, list.Count()); + Tfds.Eq(capacity, listBase.Capacity()); + + list.Add(capacity); // forces resize + Tfds.Eq(capacity + 1, list.Count()); + Tfds.Eq(capacity * 2, listBase.Capacity()); + } + @Test public void Get_at() { + list.Add("0"); + + Tfds.Eq("0", list.Get_at(0)); + } + @Test public void Fetch_many() { + list_AddMany("0", "1"); + + Tfds.Eq("0", list.Get_at(0)); + Tfds.Eq("1", list.Get_at(1)); + } + @Test public void FetchAt_fail() { + try {list.Get_at(0);} + catch (Exception exc) {Err_.Noop(exc); return;} + Tfds.Fail("Get_at should fail for out of bound index"); + } + @Test public void Del_at() { + list.Add("0"); + Tfds.Eq(1, list.Count()); + + list.Del_at(0); + Tfds.Eq(0, list.Count()); + } + @Test public void DelAt_shiftDown() { + list_AddMany("0", "1"); + Tfds.Eq(list.Count(), 2); + + list.Del_at(0); + Tfds.Eq(1, list.Count()); + Tfds.Eq("1", list.Get_at(0)); + } + @Test public void DelAt_fail() { + try {list.Del_at(0);} + catch (Exception exc) {Err_.Noop(exc); return;} + Tfds.Fail("Del_at should fail for out of bound index"); + } + @Test public void Del() { + list.Add("0"); + Tfds.Eq(1, list.Count()); + + list.Del("0"); + Tfds.Eq(0, list.Count()); + } + @Test public void Del_matchMember() { + list_AddMany("0", "1"); + Tfds.Eq(2, list.Count()); + + list.Del("1"); + Tfds.Eq(1, list.Count()); + Tfds.Eq("0", list.Get_at(0)); + } + @Test public void Del_matchFirst() { + list_AddMany("0", "1", "0"); + Tfds.Eq(3, list.Count()); + + list.Del("0"); + tst_Enumerator("1", "0"); + } + @Test public void Enumerator() { + list_AddMany("0", "1", "2"); + tst_Enumerator("0", "1", "2"); + } + @Test public void Enumerator_stateLess() { // run 2x, to confirm no state is being cached + list_AddMany("0", "1", "2"); + tst_Enumerator("0", "1", "2"); + tst_Enumerator("0", "1", "2"); + } + @Test public void Enumerator_recursive() { // confirm separate enumerator objects are used + int pos = 0; + list_AddMany("0", "1", "2"); + for (Object valObj : list) { + String val = (String)valObj; + Tfds.Eq(Int_.To_str(pos++), val); + tst_Enumerator("0", "1", "2"); + } + } + @Test public void Clear() { + int capacity = 8; + for (int i = 0; i < capacity + 1; i++) + list.Add("0"); + Tfds.Eq(capacity * 2, listBase.Capacity()); + + list.Clear(); + Tfds.Eq(0, list.Count()); + Tfds.Eq(16, listBase.Capacity()); // check that capacity has increased + } + @Test public void Clear_empty() { // confirm no failure + list.Clear(); + Tfds.Eq(0, list.Count()); + } + @Test public void Reverse() { + list_AddMany("0", "1", "2"); + + list.Reverse(); + tst_Enumerator("2", "1", "0"); + } + @Test public void Reverse_empty() {list.Reverse();} + @Test public void Sort() { + list_AddMany("2", "0", "1"); + + list.Sort(); + tst_Enumerator("0", "1", "2"); + } + @Test public void Sort_empty() {list.Sort();} + @Test public void Xto_bry() { + list_AddMany("0", "1"); + String[] ary = (String[])list.To_ary(String.class); + Tfds.Eq_nullNot(ary); + Tfds.Eq(2, Array_.Len(ary)); + } + @Test public void XtoAry_empty() { + String[] ary = (String[])list.To_ary(String.class); + Tfds.Eq_nullNot(ary); + Tfds.Eq(0, Array_.Len(ary)); + } + @Test public void Shuffle() { + for (int i = 0; i < 25; i++) + list.Add(i); + + list.Shuffle(); + int hasMovedCount = 0; + for (int i = 0; i < list.Count(); i++) { + int val = Int_.cast(list.Get_at(i)); + if (val != i) hasMovedCount++; + } + Tfds.Eq_true(hasMovedCount > 0, "all documents have the same index"); // NOTE: may still fail occasionally (1%) + + int count = list.Count(); + for (int i = 0; i < count; i++) + list.Del(i); + Tfds.Eq(0, list.Count(), "shuffled list does not have the same contents as original list"); + } + @Test public void Shuffle_empty() {list.Shuffle();} + @Test public void Move_to() { + run_ClearAndAdd("0", "1", "2").run_MoveTo(0, 1).tst_Order("1", "0", "2"); + run_ClearAndAdd("0", "1", "2").run_MoveTo(0, 2).tst_Order("1", "2", "0"); + run_ClearAndAdd("0", "1", "2").run_MoveTo(2, 1).tst_Order("0", "2", "1"); + run_ClearAndAdd("0", "1", "2").run_MoveTo(2, 0).tst_Order("2", "0", "1"); + } + @Test public void Del_range() { + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 2, "3"); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 3); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(1, 2, "0", "3"); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(1, 3, "0"); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 3); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 0, "1", "2", "3"); + } + void tst_DelRange(int bgn, int end, String... expd) { + list.Del_range(bgn, end); + Tfds.Eq_ary_str(expd, list.To_str_ary()); + } + List_adp_tst run_ClearAndAdd(String... ary) { + list.Clear(); + for (int i = 0; i < Array_.Len(ary); i++) { + String val = ary[i]; + list.Add(val); + } + return this; + } + List_adp_tst run_MoveTo(int elemPos, int newPos) {list.Move_to(elemPos, newPos); return this;} + List_adp_tst tst_Order(String... expd) { + String[] actl = (String[])list.To_ary(String.class); + Tfds.Eq_ary(expd, actl); + return this; + } + void list_AddMany(String... ary) { + for (int i = 0; i < Array_.Len(ary); i++) { + String val = ary[i]; + list.Add(val); + } + } + void tst_Enumerator(String... expd) { + int pos = 0; + int expdLength = Array_.Len(expd); + for (int i = 0; i < expdLength; i++) { + String val = expd[i]; + Tfds.Eq(expd[pos++], val); + } + Tfds.Eq(pos, expdLength); + } +} diff --git a/100_core/src/gplx/Long_.java b/100_core/src/gplx/Long_.java new file mode 100644 index 000000000..8c72ca707 --- /dev/null +++ b/100_core/src/gplx/Long_.java @@ -0,0 +1,112 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Long_ { + public static final String Cls_val_name = "long"; + public static final Class Cls_ref_type = Long.class; + public static final int Log10Ary_len = 21; + public static long[] Log10Ary = new long[] + { 1, 10, 100, 1000, 10000 + , 100000, 1000000, 10000000, 100000000, 1000000000 + , Long_.Pow(10, 10), Long_.Pow(10, 11), Long_.Pow(10, 12), Long_.Pow(10, 13), Long_.Pow(10, 14) + , Long_.Pow(10, 15), Long_.Pow(10, 16), Long_.Pow(10, 17), Long_.Pow(10, 18), Long_.Pow(10, 19) + , Long_.Max_value + }; + public static long parse(String raw) {try {return Long.parseLong(raw);} catch(Exception e) {throw Err_.new_parse_exc(e, long.class, raw);}} + public static long cast(Object obj) {try {return (Long)obj;} catch(Exception e) {throw Err_.new_type_mismatch_w_exc(e, long.class, obj);}} + public static long coerce_(Object v) { + try {String s = String_.as_(v); return s == null ? Long_.cast(v) : Long_.parse(s);} + catch (Exception e) {throw Err_.new_cast(e, long.class, v);} + } + public static String To_str(long v) {return Long.toString(v);} + public static String To_str_PadBgn(long v, int reqdPlaces) {return String_.Pad(To_str(v), reqdPlaces, "0", true);} // ex: 1, 3 returns 001 + public static long parse_or(String raw, long or) { + if (raw == null) return or; + try { + int rawLen = String_.Len(raw); + if (raw == null || rawLen == 0) return or; + long rv = 0, factor = 1; int tmp = 0; + for (int i = rawLen; i > 0; i--) { + tmp = Char_.To_int_or(String_.CharAt(raw, i - 1), Int_.Min_value); + if (tmp == Int_.Min_value) return or; + rv += (tmp * factor); + factor *= 10; + } + return rv; + } catch (Exception e) {Err_.Noop(e); return or;} + } + public static int Compare(long lhs, long rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } + public static int FindIdx(long[] ary, long find_val) { + int ary_len = ary.length; + int adj = 1; + int prv_pos = 0; + int prv_len = ary_len; + int cur_len = 0; + int cur_idx = 0; + long cur_val = 0; + while (true) { + cur_len = prv_len / 2; + if (prv_len % 2 == 1) ++cur_len; + cur_idx = prv_pos + (cur_len * adj); + if (cur_idx < 0) cur_idx = 0; + else if (cur_idx >= ary_len) cur_idx = ary_len - 1; + cur_val = ary[cur_idx]; + if (find_val < cur_val) adj = -1; + else if (find_val > cur_val) adj = 1; + else if (find_val == cur_val) return cur_idx; + if (cur_len == 1) { + if (adj == -1 && cur_idx > 0) + return --cur_idx; + return cur_idx; + } + prv_len = cur_len; + prv_pos = cur_idx; + } + } + public static int DigitCount(long v) { + int adj = Int_.Base1; + if (v < 0) { + if (v == Long_.Min_value) return 19; // NOTE: Long_.Min_value * -1 = Long_.Min_value + v *= -1; + ++adj; + } + return FindIdx(Log10Ary, v) + adj; + } + public static long Pow(int val, int exp) { + long rv = val; + for (int i = 1; i < exp; i++) + rv *= val; + return rv; + } + public static long Int_merge(int hi, int lo) {return (long)hi << 32 | (lo & 0xFFFFFFFFL);} + public static int Int_split_lo(long v) {return (int)(v);} + public static int Int_split_hi(long v) {return (int)(v >> 32);} + public static final long + Min_value = Long.MIN_VALUE + , Max_value = Long.MAX_VALUE + ; +} +/* alternate for Int_merge; does not work in java + public static long MergeInts(int lo, int hi) {return (uint)(hi << 32) | (lo & 0xffffffff);} + public static int SplitLo(long v) {return (int)(((ulong)v & 0x00000000ffffffff));} + public static int SplitHi(long v) {return (int)(((ulong)v & 0xffffffff00000000)) >> 32;} +*/ diff --git a/100_core/src/gplx/Long__tst.java b/100_core/src/gplx/Long__tst.java new file mode 100644 index 000000000..21294d06e --- /dev/null +++ b/100_core/src/gplx/Long__tst.java @@ -0,0 +1,49 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Long__tst { + @Test public void DigitCount() { + tst_DigitCount(0, 1); + tst_DigitCount(1, 1); + tst_DigitCount(9, 1); + tst_DigitCount(10, 2); + tst_DigitCount(100, 3); + tst_DigitCount(10000, 5); + tst_DigitCount(100000, 6); + tst_DigitCount(1000000, 7); + tst_DigitCount(1000000000, 10); + tst_DigitCount(10000000000L, 11); + tst_DigitCount(100000000000L, 12); + tst_DigitCount(10000000000000000L, 17); + tst_DigitCount(-1, 2); + } void tst_DigitCount(long val, int expd) {Tfds.Eq(expd, Long_.DigitCount(val));} + @Test public void Int_merge() { + tst_Int_merge(123, 456, 528280977864L); + tst_Int_merge(123, 457, 528280977865L); + } + void tst_Int_merge(int hi, int lo, long expd) { + Tfds.Eq(expd, Long_.Int_merge(hi, lo)); + Tfds.Eq(hi, Long_.Int_split_hi(expd)); + Tfds.Eq(lo, Long_.Int_split_lo(expd)); + } + @Test public void parse_or() { + parse_or_tst("10000000000", 10000000000L); + } + void parse_or_tst(String raw, long expd) {Tfds.Eq(expd, Long_.parse_or(raw, -1));} +} diff --git a/100_core/src/gplx/Math_.java b/100_core/src/gplx/Math_.java new file mode 100644 index 000000000..b970760d0 --- /dev/null +++ b/100_core/src/gplx/Math_.java @@ -0,0 +1,73 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Math_ { + public static double Pow(double val, double exponent) {return java.lang.Math.pow(val, exponent);} + public static double Pi = java.lang.Math.PI; + public static double E = java.lang.Math.E; + public static int Ceil_as_int(double v) {return (int)Ceil(v);} + public static double Ceil(double v) {return java.lang.Math.ceil(v);} + public static double Floor(double v) {return java.lang.Math.floor(v);} + public static double Round(double v, int places) { + return java.math.BigDecimal.valueOf(v).setScale(places, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); + } + public static int Trunc(double v) {return (int)v;} + public static double Exp(double v) {return java.lang.Math.exp(v);} + public static double Log(double v) {return java.lang.Math.log(v);} + public static double Sin(double v) {return java.lang.Math.sin(v);} + public static double Cos(double v) {return java.lang.Math.cos(v);} + public static double Tan(double v) {return java.lang.Math.tan(v);} + public static double Asin(double v) {return java.lang.Math.asin(v);} + public static double Acos(double v) {return java.lang.Math.acos(v);} + public static double Atan(double v) {return java.lang.Math.atan(v);} + public static double Sqrt(double v) {return java.lang.Math.sqrt(v);} + public static int Abs(int val) {return val > 0 ? val : val * -1;} + public static long Abs(long val) {return val > 0 ? val : val * -1;} + public static float Abs(float val) {return val > 0 ? val : val * -1;} + public static double Abs_double(double val) {return val > 0 ? val : val * -1;} + public static int Log10(int val) { + if (val <= 0) return Int_.Min_value; + int rv = -1, baseVal = 10; + while (val != 0) { + val = (val / baseVal); + rv++; + } + return rv; + } + public static int Div_safe_as_int(int val, int divisor) {return divisor == 0 ? 0 : val / divisor;} + public static long Div_safe_as_long(long val, long divisor) {return divisor == 0 ? 0 : val / divisor;} + public static double Div_safe_as_double(double val, double divisor) {return divisor == 0 ? 0 : val / divisor;} + public static int Min(int val0, int val1) {return val0 < val1 ? val0 : val1;} + public static int Max(int val0, int val1) {return val0 > val1 ? val0 : val1;} + public static int[] Base2Ary(int v, int max) { + int[] idxs = new int[32]; + int cur = v, mult = max, idx = 0; + while (mult > 0) { + int tmp = cur / mult; + if (tmp >= 1) { + idxs[idx++] = mult; + cur -= mult; + } + mult /= 2; + } + int[] rv = new int[idx]; + for (int i = 0; i < idx; i++) + rv[i] = idxs[idx - i - 1]; + return rv; + } +} \ No newline at end of file diff --git a/100_core/src/gplx/Math__tst.java b/100_core/src/gplx/Math__tst.java new file mode 100644 index 000000000..d89e7af1a --- /dev/null +++ b/100_core/src/gplx/Math__tst.java @@ -0,0 +1,61 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Math__tst { + @Test public void Abs() { + tst_Abs(1, 1); + tst_Abs(-1, 1); + tst_Abs(0, 0); + } void tst_Abs(int val, int expd) {Tfds.Eq(expd, Math_.Abs(val));} + @Test public void Log10() { + tst_Log10(0, Int_.Min_value); + tst_Log10(9, 0); + tst_Log10(10, 1); + tst_Log10(99, 1); + tst_Log10(100, 2); + } void tst_Log10(int val, int expd) {Tfds.Eq(expd, Math_.Log10(val));} + @Test public void Min() { + tst_Min(0, 1, 0); + tst_Min(1, 0, 0); + tst_Min(0, 0, 0); + } void tst_Min(int val0, int val1, int expd) {Tfds.Eq(expd, Math_.Min(val0, val1));} + @Test public void Pow() { + tst_Pow(2, 0, 1); + tst_Pow(2, 1, 2); + tst_Pow(2, 2, 4); + } void tst_Pow(int val, int exponent, double expd) {Tfds.Eq(expd, Math_.Pow(val, exponent));} + @Test public void Mult() { + tst_Mult(100, .01f, 1); + } void tst_Mult(int val, float multiplier, int expd) {Tfds.Eq(expd, Int_.Mult(val, multiplier));} + @Test public void Base2Ary() { + tst_Base2Ary( 1, 256, 1); + tst_Base2Ary( 2, 256, 2); + tst_Base2Ary( 3, 256, 1, 2); + tst_Base2Ary( 4, 256, 4); + tst_Base2Ary( 5, 256, 1, 4); + tst_Base2Ary( 6, 256, 2, 4); + tst_Base2Ary(511, 256, 1, 2, 4, 8, 16, 32, 64, 128, 256); + } void tst_Base2Ary(int v, int max, int... expd) {Tfds.Eq_ary(expd, Math_.Base2Ary(v, max));} + @Test public void Round() { + tst_Round(1.5 , 0, 2); + tst_Round(2.5 , 0, 3); + tst_Round(2.123 , 2, 2.12); + tst_Round(21.1 , -1, 20); + } void tst_Round(double v, int places, double expd) {Tfds.Eq(expd, Math_.Round(v, places));} +} diff --git a/100_core/src/gplx/New.java b/100_core/src/gplx/New.java new file mode 100644 index 000000000..4fe896357 --- /dev/null +++ b/100_core/src/gplx/New.java @@ -0,0 +1,19 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public @interface New {} diff --git a/100_core/src/gplx/ObjAry.java b/100_core/src/gplx/ObjAry.java new file mode 100644 index 000000000..e90849fd3 --- /dev/null +++ b/100_core/src/gplx/ObjAry.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class ObjAry { + public Object[] Ary() {return ary;} Object[] ary; + public Object Get(int i) {return ary[i];} + public Object Get0() {return ary[0];} + public Object Get1() {return ary[1];} + public static ObjAry pair_(Object val0, Object val1) { + ObjAry rv = new ObjAry(); + rv.ary = new Object[2]; + rv.ary[0] = val0; + rv.ary[1] = val1; + return rv; + } ObjAry() {} + public static ObjAry many_(Object... ary) { + ObjAry rv = new ObjAry(); + rv.ary = ary; + return rv; + } +} diff --git a/100_core/src/gplx/Object_.java b/100_core/src/gplx/Object_.java new file mode 100644 index 000000000..77911ef02 --- /dev/null +++ b/100_core/src/gplx/Object_.java @@ -0,0 +1,68 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Object_ { + public static final String Cls_val_name = "Object"; + public static final Object[] Ary_empty = new Object[0]; + public static Object[] Ary(Object... ary) {return ary;} + public static Object[] Ary_add(Object[] lhs, Object[] rhs) { + int lhs_len = lhs.length, rhs_len = rhs.length; + if (lhs_len == 0) return rhs; + else if (rhs_len == 0) return lhs; + int rv_len = lhs_len + rhs_len; + Object[] rv = new Object[rv_len]; + for (int i = 0; i < lhs_len; ++i) + rv[i] = lhs[i]; + for (int i = lhs_len; i < rv_len; ++i) + rv[i] = rhs[i - lhs_len]; + return rv; + } + public static String[] Ary__to_str_ary(Object[]... ary) { + int len = ary.length; + String[] rv = new String[len]; + for (int i = 0; i < len; ++i) + rv[i] = String_.Concat_with_obj("|", (Object[])ary[i]); + return rv; + } + public static boolean Eq(Object lhs, Object rhs) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + else return lhs.equals(rhs); + } + public static String Xto_str_or(Object v, String or) {return v == null ? or : ToString_lang(v);} + public static String Xto_str_strict_or_null(Object v) {return v == null ? null : ToString_lang(v);} + public static String Xto_str_strict_or_null_mark(Object v) {return v == null ? String_.Null_mark : ToString_lang(v);} + public static String Xto_str_strict_or_empty(Object v) {return v == null ? String_.Empty : ToString_lang(v);} + private static String ToString_lang(Object v) { + Class c = v.getClass(); + if (Type_adp_.Eq(c, String_.Cls_ref_type)) return (String)v; + else if (Type_adp_.Eq(c, Bry_.Cls_ref_type)) return String_.new_u8((byte[])v); + else return v.toString(); + } + public static String Xto_str_loose_or(Object v, String or) { // tries to pretty-print doubles; also standardizes true/false; DATE:2014-07-14 + if (v == null) return null; + Class c = Type_adp_.ClassOf_obj(v); + if (Type_adp_.Eq(c, String_.Cls_ref_type)) return (String)v; + else if (Type_adp_.Eq(c, Bry_.Cls_ref_type)) return String_.new_u8((byte[])v); + else if (Type_adp_.Eq(c, Bool_.Cls_ref_type)) return Bool_.cast(v) ? Bool_.True_str : Bool_.False_str; // always return "true" / "false" + else if (Type_adp_.Eq(c, Double_.Cls_ref_type)) return Double_.To_str_loose(Double_.cast(v)); + else return v.toString(); + } + public static final Object Null = null; + public static final byte[] Bry__null = Bry_.new_a7("null"); +} \ No newline at end of file diff --git a/100_core/src/gplx/Object__tst.java b/100_core/src/gplx/Object__tst.java new file mode 100644 index 000000000..43aeabdb1 --- /dev/null +++ b/100_core/src/gplx/Object__tst.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Object__tst { + @Before public void init() {} private Object__fxt fxt = new Object__fxt(); + @Test public void Eq() { + fxt.Test_eq(null, null, true); // both null + fxt.Test_eq(5, 5, true); // both non-null + fxt.Test_eq(5, null, false); // rhs non-null + fxt.Test_eq(null, 5, false); // lhs non-null + } + @Test public void Xto_str_loose_or_null() { + fxt.Test_xto_str_loose_or_null(null, null); + fxt.Test_xto_str_loose_or_null(2449.6000000000004d, "2449.6"); + } +} +class Object__fxt { + public void Test_eq(Object lhs, Object rhs, boolean expd) {Tfds.Eq(expd, Object_.Eq(lhs, rhs));} + public void Test_xto_str_loose_or_null(Object v, String expd) {Tfds.Eq(expd, Object_.Xto_str_loose_or(v, null));} +} diff --git a/100_core/src/gplx/Ordered_hash.java b/100_core/src/gplx/Ordered_hash.java new file mode 100644 index 000000000..c71b66cdb --- /dev/null +++ b/100_core/src/gplx/Ordered_hash.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.lists.*; /*EnumerAble,ComparerAble*/ +public interface Ordered_hash extends Hash_adp, List_adp__getable { + void Add_at(int i, Object o); + int Idx_of(Object item); + void Sort(); + void Sort_by(ComparerAble comparer); + void Resize_bounds(int i); + Object To_ary(Class t); + Object To_ary_and_clear(Class t); + void Move_to(int src, int trg); + void Lock(); +} diff --git a/100_core/src/gplx/Ordered_hash_.java b/100_core/src/gplx/Ordered_hash_.java new file mode 100644 index 000000000..a4c1c9a8e --- /dev/null +++ b/100_core/src/gplx/Ordered_hash_.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.primitives.*; +public class Ordered_hash_ { + public static Ordered_hash New() {return new Ordered_hash_base();} + public static Ordered_hash New_bry() {return new Ordered_hash_bry();} +} +class Ordered_hash_bry extends Ordered_hash_base { + private final Bry_obj_ref tmp_ref = Bry_obj_ref.New_empty(); + @Override protected void Add_base(Object key, Object val) {super.Add_base(Bry_obj_ref.New((byte[])key), val);} + @Override protected void Del_base(Object key) {synchronized (tmp_ref) {super.Del_base(tmp_ref.Val_((byte[])key));}} + @Override protected boolean Has_base(Object key) {synchronized (tmp_ref) {return super.Has_base(tmp_ref.Val_((byte[])key));}} + @Override protected Object Fetch_base(Object key) {synchronized (tmp_ref) {return super.Fetch_base(tmp_ref.Val_((byte[])key));}} +} diff --git a/100_core/src/gplx/Ordered_hash_base.java b/100_core/src/gplx/Ordered_hash_base.java new file mode 100644 index 000000000..9a1aaf8fe --- /dev/null +++ b/100_core/src/gplx/Ordered_hash_base.java @@ -0,0 +1,95 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.core.envs.*; +import gplx.core.lists.*; /*EnumerAble,ComparerAble*/ +public class Ordered_hash_base extends Hash_adp_base implements Ordered_hash, Gfo_invk { + private final List_adp ordered = List_adp_.New(); + @Override protected void Add_base(Object key, Object val) { + super.Add_base(key, val); + ordered.Add(val); + AssertCounts(); + } + @Override public void Del(Object key) { + if (!this.Has_base(key)) return; + Object val = this.Fetch_base(key); + this.Del_base(key); + ordered.Del(val); + AssertCounts(); + } + protected Object Get_at_base(int index) {return ordered.Get_at(index);} + protected int IndexOf_base(Object obj) {return ordered.Idx_of(obj);} + @Override public void Clear() { + if (locked) Lock_fail(); + super.Clear(); + ordered.Clear(); + } + public Object To_ary(Class type) {return ordered.To_ary(type);} + public Object To_ary_and_clear(Class t) { + Object rv = To_ary(t); + this.Clear(); + return rv; + } + @gplx.Virtual public void Sort() {if (locked) Lock_fail(); ordered.Sort();} // NOTE: uses item's .compareTo + public void Sort_by(ComparerAble comparer) {if (locked) Lock_fail(); ordered.Sort_by(comparer);} + @Override public java.util.Iterator iterator() {return ordered.iterator();} + public void Add_at(int i, Object key, Object val) { + if (locked) Lock_fail(); + super.Add_base(key, val); + ordered.Add_at(i, val); + AssertCounts(); + } + void AssertCounts() { + if (super.Count() != ordered.Count()) throw Err_.new_wo_type("counts do not match", "hash", super.Count(), "list", ordered.Count()); + } + public void Resize_bounds(int i) {if (locked) Lock_fail(); ordered.Resize_bounds(i);} + public void Lock() {locked = true;} private boolean locked = false; + void Lock_fail() {throw Err_.new_wo_type("collection is locked");} + static final String GRP_KEY = "gplx.core.lists.ordered_hash"; + public void Add_at(int i, Object o) {if (locked) Lock_fail(); ordered.Add_at(i, o);} + public Object Get_at(int i) {return Get_at_base(i);} + public int Idx_of(Object obj) {return this.IndexOf_base(obj);} + public void Move_to(int src, int trg) {if (locked) Lock_fail(); ordered.Move_to(src, trg);} + private String To_str_ui() { + String_bldr sb = String_bldr_.new_(); + int count = ordered.Count(); + int pad = String_.Len(Int_.To_str(count)); + for (int i = 0; i < count; i++) { + sb .Add(Int_.To_str_pad_bgn_zero(i, pad)) + .Add(":").Add(ordered.Get_at(i).toString()) + .Add(Op_sys.Cur().Nl_str()); + } + return sb.To_str(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_SetKeyOnly)) { + String s = m.ReadStr("v"); + if (ctx.Deny()) return this; + this.Add(s, s); + } + else if (ctx.Match(k, Invk_Print)) { + if (ctx.Deny()) return this; + return To_str_ui(); + } + else return Gfo_invk_.Rv_unhandled; + return this; + } static final String Invk_SetKeyOnly = "SetKeyOnly", Invk_Print = "Print"; + public int Len() {return ordered.Count();} + @Override public int Count() {return ordered.Count();} + public Ordered_hash_base() {} +} diff --git a/100_core/src/gplx/Ordered_hash_tst.java b/100_core/src/gplx/Ordered_hash_tst.java new file mode 100644 index 000000000..a9525af4d --- /dev/null +++ b/100_core/src/gplx/Ordered_hash_tst.java @@ -0,0 +1,39 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class Ordered_hash_tst { + @Before public void setup() { + hash = Ordered_hash_.New(); + } + @Test public void Get_at() { + hash.Add("key1", "val1"); + Tfds.Eq("val1", hash.Get_at(0)); + } + @Test public void iterator() { + hash.Add("key2", "val2"); + hash.Add("key1", "val1"); + + List_adp list = List_adp_.New(); + for (Object val : hash) + list.Add(val); + Tfds.Eq("val2", list.Get_at(0)); + Tfds.Eq("val1", list.Get_at(1)); + } + Ordered_hash hash; +} diff --git a/100_core/src/gplx/RandomAdp.java b/100_core/src/gplx/RandomAdp.java new file mode 100644 index 000000000..6ad67f181 --- /dev/null +++ b/100_core/src/gplx/RandomAdp.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.util.*; +public class RandomAdp { + private final Random under; + public RandomAdp(Random v) {this.under = v;} + public int Next(int max) {return under.nextInt(max);} +} diff --git a/100_core/src/gplx/RandomAdp_.java b/100_core/src/gplx/RandomAdp_.java new file mode 100644 index 000000000..1d8db0a82 --- /dev/null +++ b/100_core/src/gplx/RandomAdp_.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.util.*; +public class RandomAdp_ implements Gfo_invk { + public static RandomAdp new_() { + Random random = new Random(System.currentTimeMillis()); + return new RandomAdp(random); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Next)) return RandomAdp_.new_().Next(m.ReadInt("max")); + else return Gfo_invk_.Rv_unhandled; + } static final String Invk_Next = "Next"; + public static final RandomAdp_ Gfs = new RandomAdp_(); +} diff --git a/100_core/src/gplx/Rls_able.java b/100_core/src/gplx/Rls_able.java new file mode 100644 index 000000000..34ccb5c3b --- /dev/null +++ b/100_core/src/gplx/Rls_able.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface Rls_able { + void Rls(); +} diff --git a/100_core/src/gplx/Rls_able_.java b/100_core/src/gplx/Rls_able_.java new file mode 100644 index 000000000..19cde2e5f --- /dev/null +++ b/100_core/src/gplx/Rls_able_.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Rls_able_ { + public static Rls_able as_(Object obj) {return obj instanceof Rls_able ? (Rls_able)obj : null;} + public static Rls_able cast(Object obj) {try {return (Rls_able)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, Rls_able.class, obj);}} + public static final Rls_able Null = new Rls_able__noop(); +} +class Rls_able__noop implements Rls_able { + public void Rls() {} +} diff --git a/100_core/src/gplx/Short_.java b/100_core/src/gplx/Short_.java new file mode 100644 index 000000000..7b87f4740 --- /dev/null +++ b/100_core/src/gplx/Short_.java @@ -0,0 +1,23 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Short_ { + public static final Class Cls_ref_type = Short.class; + public static short cast(Object obj) {try {return (Short)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, short.class, obj);}} + public static short By_int(int v) {return (short)v;} +} diff --git a/100_core/src/gplx/String_.java b/100_core/src/gplx/String_.java new file mode 100644 index 000000000..18c55e6bd --- /dev/null +++ b/100_core/src/gplx/String_.java @@ -0,0 +1,563 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import java.lang.*; +import gplx.core.strings.*; import gplx.langs.gfs.*; import gplx.core.envs.*; +public class String_ implements Gfo_invk { + public static final Class Cls_ref_type = String.class; + public static final String Cls_val_name = "str" + "ing"; + public static final int Find_none = -1, Pos_neg1 = -1; + public static final String Null = null, Empty = "", Null_mark = "<>", Tab = "\t", Lf = "\n", CrLf = "\r\n"; + public static String cast(Object v) {return (String)v;} + public static String as_(Object obj) {return obj instanceof String ? (String)obj : null;} + public static String new_a7(byte[] v) {return v == null ? null : new_a7(v, 0, v.length);} + public static String new_a7(byte[] v, int bgn, int end) { + try { + return v == null + ? null + : new String(v, bgn, end - bgn, "ASCII"); + } + catch (Exception e) {throw Err_.new_exc(e, "core", "unsupported encoding");} + } + public static String new_u8(byte[] v) {return v == null ? null : new_u8(v, 0, v.length);} + public static String new_u8(byte[] v, int bgn, int end) { + try { + return v == null + ? null + : new String(v, bgn, end - bgn, "UTF-8"); + } + catch (Exception e) {Err_.Noop(e); throw Err_.new_("core", "unsupported encoding", "bgn", bgn, "end", end);} + } + public static String new_u8__by_len(byte[] v, int bgn, int len) { + int v_len = v.length; + if (bgn + len > v_len) len = v_len - bgn; + return new_u8(v, bgn, bgn + len); + } + public static String[] Ary_add(String[]... arys) { + if (arys == null) return String_.Ary_empty; + int arys_len = arys.length; + int rv_len = 0; + for (int i = 0; i < arys_len; i++) { + String[] ary = arys[i]; + rv_len += ary.length; + } + int rv_idx = 0; + String[] rv = new String[rv_len]; + for (int i = 0; i < arys_len; i++) { + String[] ary = arys[i]; + int ary_len = ary.length; + for (int j = 0; j < ary_len; j++) + rv[rv_idx++] = ary[j]; + } + return rv; + } + public static boolean Len_gt_0(String s) {return s != null && s.length() > 0;} + public static boolean Len_eq_0(String s) {return s == null || s.length() == 0;} + public static int Len(String s) {return s.length();} + public static String Lower(String s) {return s.toLowerCase();} + public static String Upper(String s) {return s.toUpperCase();} + public static String CaseNormalize(boolean caseSensitive, String s) {return caseSensitive ? s : String_.Lower(s);} + public static String Trim(String s) {return s.trim();} + public static String Mid(String s, int bgn) {return s.substring(bgn);} + public static String Replace(String s, String find, String replace) {return s.replace(find, replace);} + public static char[] XtoCharAry(String s) {return s.toCharArray();} + public static char CharAt(String s, int i) {return s.charAt(i);} + public static int CodePointAt(String s, int i) {return s.codePointAt(i);} + public static boolean Has(String s, String find) {return s.indexOf(find) != String_.Find_none;} + public static boolean Has_at_bgn(String s, String v) {return s.startsWith(v);} + public static boolean Has_at_end(String s, String v) {return s.endsWith(v);} + public static int FindFwd(String s, String find) {return s.indexOf(find);} + public static int FindFwd(String s, String find, int pos) {return s.indexOf(find, pos);} + public static int FindBwd(String s, String find) {return s.lastIndexOf(find);} + public static int FindBwd(String s, String find, int pos) { + return s.lastIndexOf(find, pos); + } + public static int FindBetween(String s, String find, int bgn, int end) { + int rv = FindFwd(s, find, bgn); + return (rv > end) ? String_.Find_none : rv; + } + public static int FindAfter(String s, String find, int bgn) { + int rv = FindFwd(s, find, bgn); + return rv == String_.Find_none ? String_.Find_none : rv + Len(find); + } + public static int FindAfterRev(String s, String find, int pos) { + int rv = FindBwd(s, find, pos); + return rv == String_.Find_none ? String_.Find_none : rv + Len(find); + } + public static int Count(String s, String part) { + int count = 0, pos = -1; // -1 b/c first pass must be 0 (see pos + 1 below) + do { + pos = FindFwd(s, part, pos + 1); + if (pos == String_.Find_none) break; + count++; + } while (true); + return count; + } + public static boolean Eq(String lhs, String rhs) {return lhs == null ? rhs == null : lhs.equals(rhs);} + public static boolean EqAny(String lhs, String... rhsAry) { + for (int i = 0; i < rhsAry.length; i++) + if (Eq(lhs, rhsAry[i])) return true; + return false; + } + public static boolean EqNot(String lhs, String rhs) {return !Object_.Eq(lhs, rhs);} + public static boolean EqEmpty(String lhs) {return lhs.equals("");} + public static String IfNullOrEmpty(String s, String or) {return s == null || s.length() == 0 ? or : s;} + public static int Compare_as_ordinals(String lhs, String rhs) {return lhs.compareTo(rhs);} + public static int Compare_ignoreCase(String lhs, String rhs) { + if (lhs == null && rhs != null) return CompareAble_.Less; + else if (lhs != null && rhs == null) return CompareAble_.More; + else if (lhs == null && rhs == null) return CompareAble_.Same; + else return lhs.compareToIgnoreCase(rhs); + //#- + /* + if (lhs == null && rhs != null) return CompareAble_.Less; + else if (lhs != null && rhs == null) return CompareAble_.More; + else if (lhs == null && rhs == null) return CompareAble_.Same; + else return lhs.compareToIgnoreCase(rhs); + */ + } + public static int Compare(String lhs, String rhs) { + int compare = lhs.compareTo(rhs); + if (compare == CompareAble_.Same) return CompareAble_.Same; + else if (compare < CompareAble_.Same) return CompareAble_.Less; + else /* (compare > CompareAble_.Same) */ return CompareAble_.More; + } + public static int Compare_byteAry(String lhs, String rhs) { + int lhsLen = lhs.length(), rhsLen = rhs.length(); + int aryLen = lhsLen < rhsLen ? lhsLen : rhsLen; + int[] lhsAry = XtoIntAry(lhs, aryLen), rhsAry = XtoIntAry(rhs, aryLen); + for (int i = 0; i < aryLen; i++) { + int comp = Int_.Compare(lhsAry[i], rhsAry[i]); + if (comp != CompareAble_.Same) return comp; + } + return Int_.Compare(lhsLen, rhsLen); + } + public static int[] XtoIntAry(String s, int len) { + int[] rv = new int[len]; + for (int i = 0; i < len; i++) + rv[i] = (int)s.charAt(i); + return rv; + } + public static String Coalesce(String s, String alt) {return Len_eq_0(s) ? alt : s;} + public static boolean In(String s, String... ary) { + for (String itm : ary) + if (String_.Eq(s, itm)) return true; + return false; + } + + public static String new_charAry_(char[] ary, int bgn, int len) {return new String(ary, bgn, len);} + public static String Mid(String s, int bgn, int end) { + try {return Mid_lang(s, bgn, end - bgn);} + catch (Exception e) { + int len = s == null ? 0 : Len(s); + String msg = ""; + if (s == null) msg = "s is null"; + else if (bgn > end) msg = "@bgn > @end"; + else if (bgn < 0 || bgn >= len) msg = "@bgn is invalid"; + else if (end < 0 || end > len) msg = "@end is invalid"; + throw Err_.new_exc(e, "core", msg, "s", s, "bgn", bgn, "end", end, "len", len); + } + } + public static String MidByLenSafe(String s, int bgn, int len) { + if (bgn + len >= Len(s)) len = Len(s) - bgn; + return Mid_lang(s, bgn, len); + } + public static String MidByLen(String s, int bgn, int len) {return Mid_lang(s, bgn, len);} + public static String GetStrBefore(String s, String spr) { + int sprPos = String_.FindFwd(s, spr); if (sprPos == String_.Find_none) throw Err_.new_wo_type("could not find spr", "s", s, "spr", spr); + return Mid(s, 0, sprPos); + } + public static String GetStrAfter(String s, String spr) { + int sprPos = String_.FindFwd(s, spr); if (sprPos == String_.Find_none) throw Err_.new_wo_type("could not find spr", "s", s, "spr", spr); + return Mid(s, sprPos + 1); + } + public static String LimitToFirst(String s, int len) { + if (len < 0) throw Err_.new_invalid_arg("< 0", "len", len); + int sLen = Len(s); if (len > sLen) return s; + return Mid_lang(s, 0, len); + } + public static String LimitToLast(String s, int len) { + if (len < 0) throw Err_.new_invalid_arg("< 0", "len", len); + int sLen = Len(s); if (len > sLen) return s; + return Mid_lang(s, sLen - len, len); + } + public static String DelBgn(String s, int count) { + if (count < 0) throw Err_.new_invalid_arg("< 0", "count", count); + if (s == null) throw Err_.new_null(); + int len = Len(s); if (count > len) throw Err_.new_invalid_arg("> @len", "count", count, "len", len); + return String_.Mid(s, count); + } + public static String DelBgnIf(String s, String find) { + if (s == null) throw Err_.new_null(); + if (find == null) throw Err_.new_null(); + return Has_at_bgn(s, find) ? String_.Mid(s, Len(find)) : s; + } + public static String DelEnd(String s, int count) { + if (count < 0) throw Err_.new_invalid_arg("< 0", "count", count); + if (s == null) throw Err_.new_null(); + int len = Len(s); if (count > len) throw Err_.new_invalid_arg("> len", "count", count, "len", len); + return Mid_lang(s, 0, len + -count); + } + public static String DelEndIf(String s, String find) { + if (s == null) throw Err_.new_null(); + if (find == null) throw Err_.new_null(); + return Has_at_end(s, find) ? Mid_lang(s, 0, Len(s) - Len(find)) : s; + } + public static String LowerFirst(String s) { + int len = Len(s); if (len == 0) return String_.Empty; + String char0 = Lower(Mid_lang(s, 0, 1)); + return len == 1 ? char0 : char0 + Mid(s, 1); + } + public static String UpperFirst(String s) { + int len = Len(s); if (len == 0) return String_.Empty; + String char0 = Upper(Mid_lang(s, 0, 1)); + return len == 1 ? char0 : char0 + Mid(s, 1); + } + public static String PadBgn(String s, int totalLen, String pad) {return Pad(s, totalLen, pad, true);} + public static String PadEnd(String s, int totalLen, String pad) {return Pad(s, totalLen, pad, false);} + @gplx.Internal protected static String Pad(String s, int totalLen, String pad, boolean bgn) { + int sLen = Len(s); + int padLen = totalLen - sLen; if (padLen < 0) return s; + String_bldr sb = String_bldr_.new_(); + if (!bgn) sb.Add(s); + for (int i = 0; i < padLen; i++) + sb.Add(pad); + if (bgn) sb.Add(s); + return sb.To_str(); + } + public static String TrimEnd(String s) {if (s == null) return null; + int len = String_.Len(s); + if (len == 0) return s; + int last = len; + for (int i = len; i > 0; i--) { + char c = s.charAt(i - 1); + last = i; + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { + break; + } + } + return (last == len) ? s : Mid_lang(s, 0, last); + } + public static String Repeat(String s, int count) { + if (count < 0) throw Err_.new_wo_type("count cannot be negative", "count", count, "s", s); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < count; i++) + sb.Add(s); + return sb.To_str(); + } + public static String Insert(String s, int pos, String toInsert) { + if (pos < 0 || pos >= String_.Len(s)) throw Err_.new_wo_type("String_.Insert failed; pos invalid", "pos", pos, "s", s, "toInsert", toInsert); + return s.substring(0, pos) + toInsert + s.substring(pos); + } + public static String Format(String fmt, Object... args) {return Format_do(fmt, args);} + public static String FormatOrEmptyStrIfNull(String fmt, Object arg) {return arg == null ? "" : Format(fmt, arg);} + public static String Concat(char... ary) {return new String(ary);} + public static String Concat(String s1, String s2, String s3) {return s1 + s2 + s3;} + public static String Concat(String... ary) { + String_bldr sb = String_bldr_.new_(); + for (String val : ary) + sb.Add(val); + return sb.To_str(); + } + public static String Concat_any(Object... ary) { + String_bldr sb = String_bldr_.new_(); + for (Object val : ary) + sb.Add_obj(val); + return sb.To_str(); + } + public static String Concat_with_obj(String separator, Object... ary) { + String_bldr sb = String_bldr_.new_(); + int aryLen = Array_.Len(ary); + for (int i = 0; i < aryLen; i++) { + if (i != 0) sb.Add(separator); + Object val = ary[i]; + sb.Add_obj(Object_.Xto_str_strict_or_empty(val)); + } + return sb.To_str(); + } + public static String Concat_with_str(String spr, String... ary) { + String_bldr sb = String_bldr_.new_(); + int len = ary.length; + for (int i = 0; i < len; i++) { + if (i != 0) sb.Add(spr); + sb.Add_obj(ary[i]); + } + return sb.To_str(); + } + public static String Concat_lines_crlf(String... values) { + String_bldr sb = String_bldr_.new_(); + for (String val : values) + sb.Add(val).Add(String_.CrLf); + return sb.To_str(); + } + public static String Concat_lines_crlf_skipLast(String... values) { + String_bldr sb = String_bldr_.new_(); + for (String val : values) { + if (sb.Count() != 0) sb.Add(String_.CrLf); + sb.Add(val); + } + return sb.To_str(); + } + public static String Concat_lines_nl(String... values) { + String_bldr sb = String_bldr_.new_(); + for (String val : values) + sb.Add(val).Add("\n"); + return sb.To_str(); + } + public static String Concat_lines_nl_apos_skip_last(String... lines) { + Bry_bfr bfr = Bry_bfr_.Get(); + try { + Bry_.New_u8_nl_apos(bfr, lines); + return bfr.To_str_and_clear(); + } + finally {bfr.Mkr_rls();} + } + public static String Concat_lines_nl_skip_last(String... ary) { + String_bldr sb = String_bldr_.new_(); + int ary_len = ary.length; int ary_end = ary_len - 1; + for (int i = 0; i < ary_len; i++) { + sb.Add(ary[i]); + if (i != ary_end) sb.Add("\n"); + } + return sb.To_str(); + } + + public static String[] Ary(String... ary) {return ary;} + public static String[] Ary_wo_null(String... ary) { + List_adp list = List_adp_.New(); + int len = ary.length; + for (int i = 0; i < len; ++i) { + String itm = ary[i]; + if (itm == null) continue; + list.Add(itm); + } + return list.To_str_ary(); + } + public static String AryXtoStr(String... ary) { + String_bldr sb = String_bldr_.new_(); + for (String s : ary) + sb.Add(s).Add(";"); + return sb.To_str(); + } + public static final String[] Ary_empty = new String[0]; + public static String[] Split(String raw, char dlm) {return Split(raw, dlm, false);} + public static String[] Split(String raw, char dlm, boolean addEmptyIfDlmIsLast) { + List_adp list = List_adp_.New(); String_bldr sb = String_bldr_.new_(); + int rawLen = String_.Len(raw); char c = '\0'; + for (int i = 0; i < rawLen; i++) { + c = String_.CharAt(raw, i); + if (c == dlm) { + if (!addEmptyIfDlmIsLast && sb.Count() == 0 && i == rawLen - 1) {} + else list.Add(sb.To_str_and_clear()); + } + else + sb.Add(c); + } + if (sb.Count() > 0) + list.Add(sb.To_str_and_clear()); + return list.To_str_ary(); + } + public static String[] Split(String s, String separator) {return Split_do(s, separator, false);} + public static String[] SplitLines_crlf(String s) {return Split(s, Op_sys.Wnt.Nl_str());} + public static String[] SplitLines_nl(String s) {return Split(s, Op_sys.Lnx.Nl_str());} + public static String[] SplitLines_any(String s) {return Split_do(s, Op_sys.Lnx.Nl_str(), true);} + public static String[] Split_lang(String s, char c) {return s.split(Character.toString(c));} + + static String Format_do(String s, Object[] ary) { + int aryLength = Array_.Len_obj(ary); if (aryLength == 0) return s; // nothing to format + String_bldr sb = String_bldr_.new_(); + char bracketBgn = '{', bracketEnd = '}'; + String aryVal = null; char c, next; + int pos = 0; int textLength = Len(s); String numberStr = ""; boolean bracketsOn = false; + while (true) { + if (pos == textLength) break; + c = CharAt(s, pos); + if (bracketsOn) { // mode=bracketsOn + if (c == bracketBgn) { // first bracketBgn is fake; add bracketBgn and whatever is in numberStr + sb.Add(bracketBgn).Add(numberStr); + numberStr = ""; + } + else if (c == bracketEnd) { + int aryIdx = Int_.parse_or(numberStr, Int_.Min_value); + if (aryIdx != Int_.Min_value && Int_.Between(aryIdx, 0, aryLength - 1)) // check (a) aryIdx is num; (b) aryIdx is in bounds + aryVal = Object_.Xto_str_strict_or_empty(ary[aryIdx]); + else + aryVal = String_.Concat_any(bracketBgn, numberStr, bracketEnd); // not valid, just add String + sb.Add(aryVal); + bracketsOn = false; + numberStr = ""; + } + else // char=anythingElse + numberStr += c; + } + else { // mode=bracketsOff + if (c == bracketBgn || c == bracketEnd) { + boolean isEnd = pos == textLength - 1; + if (isEnd) + sb.Add(c); + else { + next = CharAt(s, pos + 1); + if (next == c) { // "{{" or "}}": escape by doubling + sb.Add(c); + pos++; + } + else + bracketsOn = true; + } + } + else // char=anythingElse + sb.Add(c); + } + pos++; + } + if (Len(numberStr) > 0) // unclosed bracket; add bracketBgn and whatever is in numberStr; ex: "{0" + sb.Add(bracketBgn).Add(numberStr); + return sb.To_str(); + } + static String[] Split_do(String s, String spr, boolean skipChar13) { + if (String_.Eq(s, "") // "".Split('a') return array with one member: "" + || String_.Eq(spr, "")) // "a".Split('\0') returns array with one member: "a" + return new String[] {s}; + List_adp list = List_adp_.New(); String_bldr sb = String_bldr_.new_(); + int i = 0, sprPos = 0; boolean sprMatched = false; char spr0 = CharAt(spr, 0); + int textLength = Len(s); int sprLength = Len(spr); + while (true) { + if (sprMatched + || i == textLength) { // last pass; add whatever's in sb to list + list.Add(sb.To_str_and_clear()); + if (sprMatched && i == textLength) list.Add(""); // if s ends with spr and last pass, add emptyString as last + sprMatched = false; + } + if (i == textLength) break; + char c = CharAt(s, i); + if (skipChar13 && c == (char)13) {i++; continue;} + if (c == spr0) { // matches first char of spr + sprPos = 1; + while (true) { + if (sprPos == sprLength) { // made it to end, must be match + sprMatched = true; + break; + } + if (i + sprPos == textLength) break; // ran out of s; handles partial match at end of String; ab+, +- + if (CharAt(s, i + sprPos) != CharAt(spr, sprPos)) break; // no match + sprPos++; + } + if (!sprMatched) // add partial match to sb + sb.Add(Mid_lang(s, i, sprPos)); + i += sprPos; + } + else { // no spr match; just add char, increment pos + sb.Add(c); + i++; + } + } + return (String[])list.To_ary(String.class); + } + static String Mid_lang(String s, int bgn, int len) {return s.substring(bgn, bgn + len);} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Replace)) { + String s = m.ReadStr(GfsCore_.Arg_primitive), find = m.ReadStr("find"), replace = m.ReadStr("replace"); + if (ctx.Deny()) return this; + return Replace(s, find, replace); + } + else if (ctx.Match(k, Invk_Len)) { + String s = m.ReadStr(GfsCore_.Arg_primitive); + if (ctx.Deny()) return this; + return Len(s); + } + else if (ctx.Match(k, Invk_PadBgn)) { + String s = m.ReadStr(GfsCore_.Arg_primitive); int totalLen = m.ReadInt("totalLen"); String pad = m.ReadStr("pad"); + if (ctx.Deny()) return this; + return PadBgn(s, totalLen, pad); + } + else return Gfo_invk_.Rv_unhandled; + } public static final String Invk_Replace = "Replace", Invk_Len = "Len", Invk_PadBgn = "PadBgn"; + public static final String_ Gfs = new String_(); + public static String Extract_after_bwd(String src, String dlm) { + int dlm_pos = String_.FindBwd(src, dlm); if (dlm_pos == String_.Find_none) return String_.Empty; + int src_len = String_.Len(src); if (dlm_pos == src_len - 1) return String_.Empty; + return String_.Mid(src, dlm_pos + 1, src_len); + } + public static String Replace_by_pos(String v, int del_bgn, int del_end, String repl) { + return String_.Mid(v, 0, del_bgn) + repl + String_.Mid(v, del_end, String_.Len(v)); + } + public static String read_(Object obj) {// NOTE: same as cast; for consistent readability only + String rv = as_(obj); + if (rv == null && obj != null) throw Err_.new_type_mismatch(String.class, obj); // NOTE: obj != null needed; EX: cast(null) --> null + return rv; + } + public static String[] Ary(byte[]... ary) { + if (ary == null) return String_.Ary_empty; + int ary_len = ary.length; + String[] rv = new String[ary_len]; + for (int i = 0; i < ary_len; i++) + rv[i] = String_.new_u8(ary[i]); + return rv; + } + public static String [] Ary_filter(String[] src, String[] filter) { + Hash_adp hash = Hash_adp_.New(); + int len = filter.length; + for (int i = 0; i < len; i++) { + String itm = filter[i]; + hash.Add_if_dupe_use_nth(itm, itm); + } + List_adp rv = List_adp_.New(); + len = src.length; + for (int i = 0; i < len; i++) { + String itm = src[i]; + if (hash.Has(itm)) rv.Add(itm); + } + return rv.To_str_ary(); + } + public static String[] Ary_flatten(String[][] src_ary) { + int trg_len = 0; + int src_len = Array_.Len(src_ary); + for (int i = 0; i < src_len; i++) { + String[] itm = src_ary[i]; + if (itm != null) trg_len += Array_.Len(itm); + } + String[] trg_ary = new String[trg_len]; + trg_len = 0; + for (int i = 0; i < src_len; i++) { + String[] itm = src_ary[i]; + if (itm == null) continue; + int itm_len = Array_.Len(itm); + for (int j = 0; j < itm_len; j++) + trg_ary[trg_len++] = itm[j]; + } + return trg_ary; + } + public static boolean Ary_eq(String[] lhs, String[] rhs) { + int lhs_len = lhs.length, rhs_len = rhs.length; + if (lhs_len != rhs_len) return false; + for (int i = 0; i < lhs_len; ++i) + if (!String_.Eq(lhs[i], rhs[i])) return false; + return true; + } + public static String To_str__as_kv_ary(String... ary) { + int len = ary.length; + Bry_bfr bfr = Bry_bfr_.New(); + for (int i = 0; i < len; i+=2) { + bfr.Add_str_u8(ary[i]).Add_byte_eq(); + String val = i + 1 < len ? ary[i + 1] : null; + if (val != null) bfr.Add_str_u8(val); + bfr.Add_byte_nl(); + } + return bfr.To_str_and_clear(); + } +} diff --git a/100_core/src/gplx/String__tst.java b/100_core/src/gplx/String__tst.java new file mode 100644 index 000000000..295e6087d --- /dev/null +++ b/100_core/src/gplx/String__tst.java @@ -0,0 +1,184 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class String__tst { + @Test public void Len() { + tst_Len("", 0); + tst_Len("abc", 3); + } void tst_Len(String v, int expd) {Tfds.Eq(expd, String_.Len(v), "Len");} + + @Test public void LimitToFirst() { + tst_LimitToFirst("abc", 0, ""); + tst_LimitToFirst("abc", 1, "a"); + tst_LimitToFirst("abc", 2, "ab"); + tst_LimitToFirst("abc", 3, "abc"); + tst_LimitToFirst("abc", 4, "abc"); + err_LimitToFirst("abc", -1); + } + void tst_LimitToFirst(String s, int v, String expd) {Tfds.Eq(expd, String_.LimitToFirst(s, v));} + void err_LimitToFirst(String s, int v) {try {String_.LimitToFirst(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err.class); return;} Tfds.Fail_expdError();} + @Test public void LimitToLast() { + tst_LimitToLast("abc", 0, ""); + tst_LimitToLast("abc", 1, "c"); + tst_LimitToLast("abc", 2, "bc"); + tst_LimitToLast("abc", 3, "abc"); + tst_LimitToLast("abc", 4, "abc"); + err_LimitToLast("abc", -1); + } + void tst_LimitToLast(String s, int v, String expd) {Tfds.Eq(expd, String_.LimitToLast(s, v));} + void err_LimitToLast(String s, int v) {try {String_.LimitToLast(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err.class); return;} Tfds.Fail_expdError();} + @Test public void DelBgn() { + tst_DelBgn("abc", 0, "abc"); + tst_DelBgn("abc", 1, "bc"); + tst_DelBgn("abc", 2, "c"); + tst_DelBgn("abc", 3, ""); + err_DelBgn(null, 0); + err_DelBgn("abc", 4); + } + void tst_DelBgn(String s, int v, String expd) {Tfds.Eq(expd, String_.DelBgn(s, v));} + void err_DelBgn(String s, int v) {try {String_.DelBgn(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err.class); return;} Tfds.Fail_expdError();} + @Test public void DelBgnIf() { + tst_DelBgnIf("abc", "", "abc"); + tst_DelBgnIf("abc", "a", "bc"); + tst_DelBgnIf("abc", "ab", "c"); + tst_DelBgnIf("abc", "abc", ""); + tst_DelBgnIf("abc", "abcd", "abc"); + tst_DelBgnIf("abc", "bcd", "abc"); + err_DelBgnIf(null, "abc"); + err_DelBgnIf("abc", null); + } + void tst_DelBgnIf(String s, String v, String expd) {Tfds.Eq(expd, String_.DelBgnIf(s, v));} + void err_DelBgnIf(String s, String v) {try {String_.DelBgnIf(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err.class); return;} Tfds.Fail_expdError();} + @Test public void DelEnd() { + tst_DelEnd("abc", 0, "abc"); + tst_DelEnd("abc", 1, "ab"); + tst_DelEnd("abc", 2, "a"); + tst_DelEnd("abc", 3, ""); + err_DelEnd(null, 0); + err_DelEnd("abc", 4); + } + void tst_DelEnd(String s, int v, String expd) {Tfds.Eq(expd, String_.DelEnd(s, v));} + void err_DelEnd(String s, int v) {try {String_.DelEnd(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err.class); return;} Tfds.Fail_expdError();} + @Test public void DelEndIf() { + tst_DelEndIf("abc", "", "abc"); + tst_DelEndIf("abc", "c", "ab"); + tst_DelEndIf("abc", "bc", "a"); + tst_DelEndIf("abc", "abc", ""); + tst_DelEndIf("abc", "abcd", "abc"); + tst_DelEndIf("abc", "ab", "abc"); + err_DelEndIf(null, ""); + err_DelEndIf("", null); + } + void tst_DelEndIf(String s, String v, String expd) {Tfds.Eq(expd, String_.DelEndIf(s, v));} + void err_DelEndIf(String s, String v) {try {String_.DelEndIf(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err.class); return;} Tfds.Fail_expdError();} + @Test public void MidByPos() { + tst_MidByPos("abc", 0, 0, ""); + tst_MidByPos("abc", 0, 1, "a"); + tst_MidByPos("abc", 0, 2, "ab"); + tst_MidByPos("abc", 0, 3, "abc"); + tst_MidByPos("abc", 2, 3, "c"); + err_MidByPos("abc", 1, 5); +// err_MidByPos("abc", 0, 4); + } + void tst_MidByPos(String s, int bgn, int end, String expd) {Tfds.Eq(expd, String_.Mid(s, bgn, end));} + void err_MidByPos(String s, int bgn, int end) {try {String_.Mid(s, bgn, end);} catch (Exception e) {Tfds.Err_classMatch(e, Err.class); return;} Tfds.Fail_expdError();} + @Test public void TrimEnd() { + tst_TrimEnd("a", "a"); + tst_TrimEnd("a ", "a"); + tst_TrimEnd("a\t", "a"); + tst_TrimEnd("a\n", "a"); + tst_TrimEnd("a\r", "a"); + tst_TrimEnd("a\r\n \t", "a"); + tst_TrimEnd(" a", " a"); + tst_TrimEnd(null, null); + } + void tst_TrimEnd(String s, String expd) {Tfds.Eq(expd, String_.TrimEnd(s));} + + @Test public void Count() { + String text = "0 0 0"; + Tfds.Eq(3, String_.Count(text, "0")); + } + @Test public void Has() { + String text = "find word"; + Tfds.Eq_true(String_.Has(text, "word")); + Tfds.Eq_false(String_.Has(text, "nothing")); + } + @Test public void Repeat() { + Tfds.Eq("333", String_.Repeat("3", 3)); + } + @Test public void Format() { + tst_Format("", ""); // empty + tst_Format("no args", "no args"); // no args + tst_Format("0", "{0}", 0); // one + tst_Format("0 and 1", "{0} and {1}", 0, 1); // many + tst_Format("{", "{{", 0); // escape bracketBgn + tst_Format("}", "}}", 0); // escape bracketEnd + tst_Format("{a0c}", "{a{0}c}", 0); // nested; + tst_Format("{a{b}c}", "{a{b}c}", 0); // invalid invalid + tst_Format("{1}", "{1}", 1); // invalid array index + tst_Format("{a} {b}", "{a} {b}", 0); // invalid many + tst_Format("{a}0{b}1", "{a}{0}{b}{1}", 0, 1); // invalid and valid + tst_Format("{0", "{0", 0); // invalid dangling + } void tst_Format(String expd, String fmt, Object... ary) {Tfds.Eq(expd, String_.Format(fmt, ary));} + @Test public void Split() { + tst_Split("ab", " ", "ab"); // no match -> return array with original input + tst_Split("ab cd", " ", "ab", "cd"); // separator.length = 1 + tst_Split("ab+!cd", "+!", "ab", "cd"); // separator.length = 2 + tst_Split("ab+!cd+!ef", "+!", "ab", "cd", "ef"); // terms = 3 + tst_Split("ab+!cd+!", "+!", "ab", "cd", ""); // closing separator + tst_Split("+!ab", "+!", "", "ab"); // opening separator + tst_Split("ab+cd+!ef", "+!", "ab+cd", "ef"); // ignore partial matches + tst_Split("ab+!cd+", "+!", "ab", "cd+"); // ignore partial matches; end of String + + // boundary + tst_Split("ab", "", "ab"); // separator.length = 0 -> return array with input as only member + tst_Split("", " ", ""); // empty input -> return array with empty input + + // acceptance + tst_Split("this\r\nis\na\rtest\r\n.", "\r\n", "this", "is\na\rtest", "."); + } void tst_Split(String text, String separator, String... expd) {Tfds.Eq_ary(expd, String_.Split(text, separator));} + @Test public void Concat_with_obj() { + tst_ConcatWith_any("a|b", "|", "a", "b"); // do not append final delimiter + tst_ConcatWith_any("a||c", "|", "a", null, "c"); // null + tst_ConcatWith_any("a|b", "|", Object_.Ary("a", "b")); // pass array as arg + } void tst_ConcatWith_any(String expd, String delimiter, Object... array) {Tfds.Eq(expd, String_.Concat_with_obj(delimiter, array));} + @Test public void Compare_byteAry() { + tst_Compare_byteAry("a", "a", CompareAble_.Same); + tst_Compare_byteAry("a", "b", CompareAble_.Less); + tst_Compare_byteAry("b", "a", CompareAble_.More); + tst_Compare_byteAry("ab", "ac", CompareAble_.Less); + tst_Compare_byteAry("ac", "ab", CompareAble_.More); + tst_Compare_byteAry("a", "ab", CompareAble_.Less); + tst_Compare_byteAry("ab", "a", CompareAble_.More); + tst_Compare_byteAry("101", "1-0-1", CompareAble_.More); // NOTE: regular String_.Compare_as_ordinals returns Less in .NET, More in Java + tst_Compare_byteAry("1-0-1", "101 (album)", CompareAble_.Less); + } void tst_Compare_byteAry(String lhs, String rhs, int expd) {Tfds.Eq(expd, String_.Compare_byteAry(lhs, rhs));} + @Test public void FindBwd() { // WORKAROUND.CS:String.LastIndexOf returns -1 for multi-chars; + tst_FindRev("abc", "a", 0, 0); + tst_FindRev("abc", "ab", 0, 0); // 2 chars + tst_FindRev("abc", "abc", 0, 0); // 3 chars + tst_FindRev("ab", "abc", 0, -1); // out of index error + tst_FindRev("ababab", "ab", 2, 2); // make sure cs implementation doesn't pick up next + } void tst_FindRev(String s, String find, int pos, int expd) {Tfds.Eq(expd, String_.FindBwd(s, find, pos));} + @Test public void Extract_after_bwd() { + Extract_after_bwd_tst("a/b", "/", "b"); + Extract_after_bwd_tst("a/", "/", ""); + Extract_after_bwd_tst("a", "/", ""); + } void Extract_after_bwd_tst(String src, String dlm, String expd) {Tfds.Eq(expd, String_.Extract_after_bwd(src, dlm));} +} diff --git a/100_core/src/gplx/Tfds.java b/100_core/src/gplx/Tfds.java new file mode 100644 index 000000000..4f244e5ba --- /dev/null +++ b/100_core/src/gplx/Tfds.java @@ -0,0 +1,236 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.core.consoles.*; import gplx.core.tests.*; +public class Tfds { // URL:doc/gplx.tfds/Tfds.txt + public static boolean SkipDb = false; + public static void Eq_bool (boolean expd , boolean actl) {Eq_exec_y(expd, actl, "", Object_.Ary_empty);} + public static void Eq_bool (boolean expd , boolean actl, String fmt, Object... args) {Eq_exec_y(expd, actl, fmt, args);} + public static void Eq_byte (byte expd , byte actl, String fmt, Object... args) {Eq_exec_y(expd, actl, fmt, args);} + public static void Eq_int (int expd , int actl) {Eq_exec_y(expd, actl, "", Object_.Ary_empty);} + public static void Eq_int (int expd , int actl, String fmt, Object... args) {Eq_exec_y(expd, actl, fmt, args);} + public static void Eq_double(double expd, double actl) {Eq_exec_y(expd, actl, "", Object_.Ary_empty);} + public static void Eq_str (byte[] expd, byte[] actl, String fmt, Object... args) {Eq_exec_y(String_.new_u8(expd), String_.new_u8(actl), fmt, args);} + public static void Eq_str (byte[] expd, String actl, String fmt, Object... args) {Eq_exec_y(String_.new_u8(expd), actl, fmt, args);} + public static void Eq_str (String expd, byte[] actl, String fmt, Object... args) {Eq_exec_y(expd, String_.new_u8(actl), fmt, args);} + public static void Eq_str (String expd, String actl) {Eq_exec_y(expd, actl, "", Object_.Ary_empty);} + public static void Eq_str (String expd, String actl, String fmt, Object... args) {Eq_exec_y(expd, actl, fmt, args);} + + public static void Eq(Object expd, Object actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_byte(byte expd, byte actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_long(long expd, long actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_float(float expd, float actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_decimal(Decimal_adp expd, Decimal_adp actl) {Eq_wkr(expd.To_double(), actl.To_double(), true, EmptyStr);} + public static void Eq_date(DateAdp expd, DateAdp actl) {Eq_wkr(expd.XtoStr_gplx(), actl.XtoStr_gplx(), true, EmptyStr);} + public static void Eq_date(DateAdp expd, DateAdp actl, String fmt, Object... args){Eq_wkr(expd.XtoStr_gplx(), actl.XtoStr_gplx(), true, String_.Format(fmt, args));} + public static void Eq_url(Io_url expd, Io_url actl) {Eq_wkr(expd.Raw(), actl.Raw(), true, EmptyStr);} + public static void Eq_str(String expd, byte[] actl) {Eq_wkr(expd, String_.new_u8(actl), true, EmptyStr);} + public static void Eq_bry(String expd, byte[] actl) {Eq_wkr(expd, String_.new_u8(actl), true, EmptyStr);} + public static void Eq_bry(byte[] expd, byte[] actl) {Eq_wkr(String_.new_u8(expd), String_.new_u8(actl), true, EmptyStr);} + public static void Eq_str_intf(To_str_able expd, To_str_able actl, String msg) {Eq_wkr(expd.To_str(), actl.To_str(), true, msg);} + public static void Eq_str_intf(To_str_able expd, To_str_able actl) {Eq_wkr(expd.To_str(), actl.To_str(), true, String_.Empty);} + public static void Eq_str_lines(String lhs, String rhs) {Eq_str_lines(lhs, rhs, EmptyStr);} + public static void Eq_str_lines(String lhs, String rhs, String note) { + if (lhs == null) lhs = ""; + if (rhs == null) rhs = ""; + Eq_ary_wkr(String_.Split(lhs, Char_.NewLine), String_.Split(rhs, Char_.NewLine), false, note); + } + public static void Eq(Object expd, Object actl, String fmt, Object... args) {Eq_wkr(expd, actl, true, String_.Format(fmt, args));} + public static void Eq_rev(Object actl, Object expd) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_rev(Object actl, Object expd, String fmt, Object... args) {Eq_wkr(expd, actl, true, String_.Format(fmt, args));} + public static void Eq_true(Object actl) {Eq_wkr(true, actl, true, EmptyStr);} + public static void Eq_true(Object actl, String fmt, Object... args) {Eq_wkr(true, actl, true, String_.Format(fmt, args));} + public static void Eq_false(Object actl) {Eq_wkr(false, actl, true, EmptyStr);} + public static void Eq_false(Object actl, String fmt, Object... args) {Eq_wkr(false, actl, true, String_.Format(fmt, args));} + public static void Eq_null(Object actl) {Eq_wkr(null, actl, true, EmptyStr);} + public static void Eq_null(Object actl, String fmt, Object... args) {Eq_wkr(null, actl, true, String_.Format(fmt, args));} + public static void Eq_nullNot(Object actl) {Eq_wkr(null, actl, false, EmptyStr);} + public static void Eq_nullNot(Object actl, String fmt, Object... args) {Eq_wkr(null, actl, false, String_.Format(fmt, args));} + public static void Fail_expdError() {Eq_wkr(true, false, true, "fail expd error");} + public static void Fail(String fmt, Object... args) {Eq_wkr(true, false, true, String_.Format(fmt, args));} + public static void Eq_ary(Object lhs, Object rhs) {Eq_ary_wkr(lhs, rhs, true, EmptyStr);} + public static void Eq_ary(Object lhs, Object rhs, String fmt, Object... args){Eq_ary_wkr(lhs, rhs, true, String_.Format(fmt, args));} + public static void Eq_ary_str(Object lhs, Object rhs, String note) {Eq_ary_wkr(lhs, rhs, false, note);} + public static void Eq_ary_str(Object lhs, Object rhs) {Eq_ary_wkr(lhs, rhs, false, EmptyStr);} + public static void Eq_list(List_adp lhs, List_adp rhs) {Eq_list_wkr(lhs, rhs, TfdsEqListItmStr_cls_default.Instance, EmptyStr);} + public static void Eq_list(List_adp lhs, List_adp rhs, TfdsEqListItmStr xtoStr) {Eq_list_wkr(lhs, rhs, xtoStr, EmptyStr);} + private static void Eq_exec_y(Object lhs, Object rhs, String fmt, Object[] args) { + if (Object_.Eq(lhs, rhs)) return; + String msg = msgBldr.Eq_xtoStr(lhs, rhs, String_.Format(fmt, args)); + throw Err_.new_wo_type(msg); + } + static void Eq_wkr(Object lhs, Object rhs, boolean expd, String customMsg) { + boolean actl = Object_.Eq(lhs, rhs); + if (expd == actl) return; + String msg = msgBldr.Eq_xtoStr(lhs, rhs, customMsg); + throw Err_.new_wo_type(msg); + } + static void Eq_ary_wkr(Object lhsAry, Object rhsAry, boolean compareUsingEquals, String customMsg) { + List_adp list = List_adp_.New(); boolean pass = true; + int lhsLen = Array_.Len(lhsAry), rhsLen = Array_.Len(rhsAry); + for (int i = 0; i < lhsLen; i++) { + Object lhs = Array_.Get_at(lhsAry, i); + Object rhs = i >= rhsLen ? "<>" : Array_.Get_at(rhsAry, i); + String lhsString = msgBldr.Obj_xtoStr(lhs); String rhsString = msgBldr.Obj_xtoStr(rhs); // even if compareUsingEquals, method does ToStr on each itm for failMsg + boolean isEq = compareUsingEquals + ? Object_.Eq(lhs, rhs) + : Object_.Eq(lhsString, rhsString); + Eq_ary_wkr_addItm(list, i, isEq, lhsString, rhsString); + if (!isEq) pass = false; + } + for (int i = lhsLen; i < rhsLen; i++) { + String lhsString = "<>"; + String rhsString = msgBldr.Obj_xtoStr(Array_.Get_at(rhsAry, i)); + Eq_ary_wkr_addItm(list, i, false, lhsString, rhsString); + pass = false; + } + if (pass) return; + String msg = msgBldr.Eq_ary_xtoStr(list, lhsLen, rhsLen, customMsg); + throw Err_.new_wo_type(msg); + } + static void Eq_list_wkr(List_adp lhsList, List_adp rhsList, TfdsEqListItmStr xtoStr, String customMsg) { + List_adp list = List_adp_.New(); boolean pass = true; + int lhsLen = lhsList.Count(), rhsLen = rhsList.Count(); + for (int i = 0; i < lhsLen; i++) { + Object lhs = lhsList.Get_at(i); + Object rhs = i >= rhsLen ? null : rhsList.Get_at(i); + String lhsStr = xtoStr.To_str(lhs, lhs); + String rhsStr = rhs == null ? "<>" : xtoStr.To_str(rhs, lhs); + boolean isEq = Object_.Eq(lhsStr, rhsStr); if (!isEq) pass = false; + Eq_ary_wkr_addItm(list, i, isEq, lhsStr, rhsStr); + } + for (int i = lhsLen; i < rhsLen; i++) { + String lhsStr = "<>"; + Object rhs = rhsList.Get_at(i); + String rhsStr = xtoStr.To_str(rhs, null); + Eq_ary_wkr_addItm(list, i, false, lhsStr, rhsStr); + pass = false; + } + if (pass) return; + String msg = msgBldr.Eq_ary_xtoStr(list, lhsLen, rhsLen, customMsg); + throw Err_.new_wo_type(msg); + } + static void Eq_ary_wkr_addItm(List_adp list, int i, boolean isEq, String lhsString, String rhsString) { + TfdsEqAryItm itm = new TfdsEqAryItm().Idx_(i).Eq_(isEq).Lhs_(lhsString).Rhs_(rhsString); + list.Add(itm); + } + public static void Err_classMatch(Exception exc, Class type) { + boolean match = Type_adp_.Eq_typeSafe(exc, type); + if (!match) throw Err_.new_("Tfds", "error types do not match", "expdType", Type_adp_.FullNameOf_type(type), "actlType", Type_adp_.NameOf_obj(exc), "actlMsg", Err_.Message_lang(exc)); + } + public static void Eq_err(Err expd, Exception actlExc) { + Tfds.Eq(Err_.Message_gplx_full(expd), Err_.Message_gplx_full(actlExc)); + } + public static void Err_has(Exception e, String hdr) { + Tfds.Eq_true(String_.Has(Err_.Message_gplx_full(e), hdr), "could not find '{0}' in '{1}'", hdr, Err_.Message_gplx_full(e)); + } + static final String EmptyStr = TfdsMsgBldr.EmptyStr; + static TfdsMsgBldr msgBldr = TfdsMsgBldr.new_(); + public static final Io_url RscDir = Io_url_.Usr().GenSubDir_nest("000", "200_dev", "190_tst"); + public static void WriteText(String text) {Console_adp__sys.Instance.Write_str(text);} + public static void Write(byte[] s, int b, int e) {Write(Bry_.Mid(s, b, e));} + public static void Write() {Write("tmp");} + public static void Dbg(Object... ary) {Write(ary);} + public static void Write(Object... ary) { + String_bldr sb = String_bldr_.new_(); + int aryLen = Array_.Len(ary); + for (int i = 0; i < aryLen; i++) + sb.Add_many("'", Object_.Xto_str_strict_or_null_mark(ary[i]), "'", " "); + WriteText(sb.To_str() + String_.Lf); + } +} +class TfdsEqListItmStr_cls_default implements TfdsEqListItmStr { + public String To_str(Object cur, Object actl) { + return Object_.Xto_str_strict_or_null_mark(cur); + } + public static final TfdsEqListItmStr_cls_default Instance = new TfdsEqListItmStr_cls_default(); TfdsEqListItmStr_cls_default() {} +} +class TfdsEqAryItm { + public int Idx() {return idx;} public TfdsEqAryItm Idx_(int v) {idx = v; return this;} int idx; + public String Lhs() {return lhs;} public TfdsEqAryItm Lhs_(String v) {lhs = v; return this;} private String lhs; + public String Rhs() {return rhs;} public TfdsEqAryItm Rhs_(String v) {rhs = v; return this;} private String rhs; + public boolean Eq() {return eq;} public TfdsEqAryItm Eq_(boolean v) {eq = v; return this;} private boolean eq; +} +class TfdsMsgBldr { + public String Eq_xtoStr(Object expd, Object actl, String customMsg) { + String expdString = Obj_xtoStr(expd); String actlString = Obj_xtoStr(actl); + String detail = String_.Concat + ( CustomMsg_xtoStr(customMsg) + , "\t\t", "expd: ", expdString, String_.CrLf + , "\t\t", "actl: ", actlString, String_.CrLf + ); + return WrapMsg(detail); + } + public String Eq_ary_xtoStr(List_adp list, int lhsAryLen, int rhsAryLen, String customMsg) { + String_bldr sb = String_bldr_.new_(); + sb.Add(CustomMsg_xtoStr(customMsg)); + if (lhsAryLen != rhsAryLen) + sb.Add_fmt_line("{0}element counts differ: {1} {2}", "\t\t", lhsAryLen, rhsAryLen); + int lhsLenMax = 0, rhsLenMax = 0; + for (int i = 0; i < list.Count(); i++) { + TfdsEqAryItm itm = (TfdsEqAryItm)list.Get_at(i); + int lhsLen = String_.Len(itm.Lhs()), rhsLen = String_.Len(itm.Rhs()); + if (lhsLen > lhsLenMax) lhsLenMax = lhsLen; + if (rhsLen > rhsLenMax) rhsLenMax = rhsLen; + } + for (int i = 0; i < list.Count(); i++) { + TfdsEqAryItm itm = (TfdsEqAryItm)list.Get_at(i); + String eq_str = itm.Eq() ? "==" : ""; + if (!itm.Eq()) { +// if (lhsLenMax < 8 ) +// eq_str = "!="; +// else + eq_str = "\n!= "; + } + sb.Add_fmt_line("{0}: {1} {2} {3}" + , Int_.To_str_pad_bgn_zero(itm.Idx(), 4) + , itm.Lhs() // String_.PadBgn(itm.Lhs(), lhsLenMax, " ") + , eq_str + , itm.Rhs() // String_.PadBgn(itm.Rhs(), rhsLenMax, " ") + ); + } +// String compSym = isEq ? " " : "!="; +// String result = String_.Format("{0}: {1}{2} {3} {4}", Int_.To_str_pad_bgn_zero(i, 4), lhsString, String_.CrLf + "\t\t", compSym, rhsString); +// foreach (Object obj in list) { +// String itmComparison = (String)obj; +// sb.Add_fmt_line("{0}{1}", "\t\t", itmComparison); +// } + return WrapMsg(sb.To_str()); + } + String CustomMsg_xtoStr(String customMsg) { + return (customMsg == EmptyStr) + ? "" + : String_.Concat(customMsg, String_.CrLf); + } + public String Obj_xtoStr(Object obj) { + String s = String_.as_(obj); + if (s != null) return String_.Concat("'", s, "'"); // if Object is String, put quotes around it for legibility + To_str_able xtoStrAble = To_str_able_.as_(obj); + if (xtoStrAble != null) return xtoStrAble.To_str(); + return Object_.Xto_str_strict_or_null_mark(obj); + } + String WrapMsg(String text) { + return String_.Concat(String_.CrLf + , "************************************************************************************************", String_.CrLf + , text + , "________________________________________________________________________________________________" + ); + } + public static TfdsMsgBldr new_() {return new TfdsMsgBldr();} TfdsMsgBldr() {} + public static final String EmptyStr = ""; +} diff --git a/100_core/src/gplx/TfdsTstr_fxt.java b/100_core/src/gplx/TfdsTstr_fxt.java new file mode 100644 index 000000000..e651388af --- /dev/null +++ b/100_core/src/gplx/TfdsTstr_fxt.java @@ -0,0 +1,99 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; +import gplx.core.lists.*; +public class TfdsTstr_fxt { + public TfdsTstr_fxt Eq_str(Object expd, Object actl, String name) { + int nameLen = String_.Len(name); if (nameLen > nameLenMax) nameLenMax = nameLen; + TfdsTstrItm itm = TfdsTstrItm.new_().Expd_(expd).Actl_(actl).Name_(name); + list.Add(itm); + return this; + } + public void SubName_push(String s) { + stack.Push(s); + TfdsTstrItm itm = TfdsTstrItm.new_(); + itm.SubName_make(stack); + itm.TypeOf = 1; + list.Add(itm); + } StackAdp stack = StackAdp_.new_(); + public void Fail() { + manualFail = true; + }boolean manualFail = false; + public int List_Max(List_adp expd, List_adp actl) {return Math_.Max(expd.Count(), actl.Count());} + public int List_Max(String[] expd, String[] actl) {return Math_.Max(expd.length, actl.length);} + public Object List_FetchAtOrNull(List_adp l, int i) {return (i >= l.Count()) ? null : l.Get_at(i);} + + public void SubName_pop() {stack.Pop();} + int nameLenMax = 0; + public void tst_Equal(String hdr) { + boolean pass = true; + for (int i = 0; i < list.Count(); i++) { + TfdsTstrItm itm = (TfdsTstrItm)list.Get_at(i); + if (!itm.Compare()) pass = false; // don't break early; Compare all vals + } + if (pass && !manualFail) return; + String_bldr sb = String_bldr_.new_(); + sb.Add_char_crlf(); + sb.Add_str_w_crlf(hdr); + for (int i = 0; i < list.Count(); i++) { + TfdsTstrItm itm = (TfdsTstrItm)list.Get_at(i); + if (itm.TypeOf == 1) { + sb.Add_fmt_line(" /{0}", itm.SubName()); + continue; + } + boolean hasError = itm.CompareResult() != TfdsTstrItm.CompareResult_eq; + String errorKey = hasError ? "*" : " "; + sb.Add_fmt_line("{0}{1} {2}", errorKey, String_.PadEnd(itm.Name(), nameLenMax, " "), itm.Expd()); + if (hasError) + sb.Add_fmt_line("{0}{1} {2}", errorKey, String_.PadEnd("", nameLenMax, " "), itm.Actl()); + } + sb.Add(String_.Repeat("_", 80)); + throw Err_.new_wo_type(sb.To_str()); + } + List_adp list = List_adp_.New(); + public static TfdsTstr_fxt new_() {return new TfdsTstr_fxt();} TfdsTstr_fxt() {} +} +class TfdsTstrItm { + public String Name() {return name;} public TfdsTstrItm Name_(String val) {name = val; return this;} private String name; + public Object Expd() {return expd;} public TfdsTstrItm Expd_(Object val) {expd = val; return this;} Object expd; + public Object Actl() {return actl;} public TfdsTstrItm Actl_(Object val) {actl = val; return this;} Object actl; + public String SubName() {return subName;} private String subName = ""; + public int TypeOf; + public void SubName_make(StackAdp stack) { + if (stack.Count() == 0) return; + List_adp list = stack.XtoList(); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < list.Count(); i++) { + if (i != 0) sb.Add("."); + sb.Add((String)list.Get_at(i)); + } + subName = sb.To_str(); + } + public int CompareResult() {return compareResult;} public TfdsTstrItm CompareResult_(int val) {compareResult = val; return this;} int compareResult; + public boolean Compare() { + boolean eq = Object_.Eq(expd, actl); + compareResult = eq ? 1 : 0; + return eq; + } + public String CompareSym() { + return compareResult == 1 ? "==" : "!="; + } + public static TfdsTstrItm new_() {return new TfdsTstrItm();} TfdsTstrItm() {} + public static final int CompareResult_none = 0, CompareResult_eq = 1, CompareResult_eqn = 2; +} diff --git a/100_core/src/gplx/Time_span.java b/100_core/src/gplx/Time_span.java new file mode 100644 index 000000000..b0461e8ad --- /dev/null +++ b/100_core/src/gplx/Time_span.java @@ -0,0 +1,83 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; +public class Time_span implements CompareAble { // NOTE: gplx.Time_span b/c System.TimeSpan + public long Fracs() {return fracs;} long fracs; public int FracsAsInt() {return (int)fracs;} + public Decimal_adp Total_days() {return Decimal_adp_.divide_(fracs, Time_span_.Divisors[Time_span_.Idx_Hour] * 24);} + public Decimal_adp Total_hours() {return Decimal_adp_.divide_(fracs, Time_span_.Divisors[Time_span_.Idx_Hour]);} + public Decimal_adp Total_mins() {return Decimal_adp_.divide_(fracs, Time_span_.Divisors[Time_span_.Idx_Min]);} + public Decimal_adp Total_secs() {return Decimal_adp_.divide_(fracs, Time_span_.Divisors[Time_span_.Idx_Sec]);} + public int[] Units() {return Time_span_.Split_long(fracs, Time_span_.Divisors);} + public int Units_fracs() { + int[] ary = Time_span_.Split_long(fracs, Time_span_.Divisors); + return ary[Time_span_.Idx_Frac]; + } + public Time_span Add(Time_span val) {return new Time_span(fracs + val.fracs);} + public Time_span Add_fracs(long val) {return new Time_span(fracs + val);} + public Time_span Add_unit(int idx, int val) { + int[] units = Time_span_.Split_long(fracs, Time_span_.Divisors); + units[idx] += val; + int sign = fracs >= 0 ? 1 : -1; + long rv = sign * Time_span_.Merge_long(units, Time_span_.Divisors); + return Time_span_.fracs_(rv); + } + public Time_span Subtract(Time_span val) {return new Time_span(fracs - val.fracs);} + + public int compareTo(Object obj) {Time_span comp = Time_span_.cast(obj); return CompareAble_.Compare_obj(fracs, comp.fracs);} + public boolean Eq(Object o) { + Time_span comp = Time_span_.cast(o); if (comp == null) return false; + return fracs == comp.fracs; + } + @Override public String toString() {return To_str(Time_span_.Fmt_Default);} + @Override public boolean equals(Object obj) {Time_span comp = Time_span_.cast(obj); return Object_.Eq(fracs, comp.fracs);} + @Override public int hashCode() {return super.hashCode();} + + public String To_str() {return Time_span_.To_str(fracs, Time_span_.Fmt_Default);} + public String To_str(String format) { + return Time_span_.To_str(fracs, format); + } + public String XtoStrUiAbbrv() { + if (fracs == 0) return "0" + UnitAbbrv(0); + int[] units = Units(); + boolean started = false; + String_bldr sb = String_bldr_.new_(); + for (int i = units.length - 1; i > -1; i--) { + int unit = units[i]; + if (!started) { + if (unit == 0) + continue; + else + started = true; + } + if (sb.Count() != 0) sb.Add(" "); + sb.Add_obj(unit).Add(UnitAbbrv(i)); + } + return sb.To_str(); + } + String UnitAbbrv(int i) { + switch (i) { + case 0: return "f"; + case 1: return "s"; + case 2: return "m"; + case 3: return "h"; + default: return "unknown:<" + Int_.To_str(i) + ">"; + } + } + @gplx.Internal protected Time_span(long fracs) {this.fracs = fracs;} +} diff --git a/100_core/src/gplx/Time_span_.java b/100_core/src/gplx/Time_span_.java new file mode 100644 index 000000000..4466a62f0 --- /dev/null +++ b/100_core/src/gplx/Time_span_.java @@ -0,0 +1,163 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.core.envs.*; +public class Time_span_ { + public static final Time_span Zero = new Time_span(0); + public static final Time_span Null = new Time_span(-1); + public static Time_span fracs_(long val) {return new Time_span(val);} + public static Time_span seconds_(double seconds) { + long fracs = (long)(seconds * Divisors[Idx_Sec]); + return new Time_span(fracs); + } + public static Time_span decimal_(Decimal_adp seconds) { + return new Time_span(seconds.To_long_mult_1000()); + } + public static Time_span units_(int frc, int sec, int min, int hour) { + int[] units = new int[] {frc, sec, min, hour}; + long fracs = Merge_long(units, Time_span_.Divisors); + return Time_span_.fracs_(fracs); + } + public static Time_span from_(long bgn) {return Time_span_.fracs_(System_.Ticks() - bgn);} + public static final long parse_null = Long_.Min_value; + public static Time_span parse(String raw) { + byte[] bry = Bry_.new_u8(raw); + long fracs = parse_to_fracs(bry, 0, bry.length, false); + return fracs == parse_null ? null : Time_span_.fracs_(fracs); + } + public static long parse_to_fracs(byte[] raw, int bgn, int end, boolean fail_if_ws) { + int sign = 1, val_f = 0, val_s = 0, val_m = 0, val_h = 0, colon_pos = 0, unit_val = 0, unit_multiple = 1; + for (int i = end - 1; i >= bgn; i--) { // start from end; fracs should be lowest unit + byte b = raw[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + int unit_digit = Byte_ascii.To_a7_int(b); + unit_val = (unit_multiple == 1) ? unit_digit : unit_val + (unit_digit * unit_multiple); + switch (colon_pos) { + case 0: val_s = unit_val; break; + case 1: val_m = unit_val; break; + case 2: val_h = unit_val; break; + default: return parse_null; // only hour:minute:second supported for ':' separator; ':' count should be <= 2 + } + unit_multiple *= 10; + break; + case Byte_ascii.Dot: + double factor = (double)1000 / (double)unit_multiple; // factor is necessary to handle non-standard decimals; ex: .1 -> 100; .00199 -> .001 + val_f = (int)((double)val_s * factor); // move val_s unit_val to val_f; logic is indirect, b/c of differing inputs: "123" means 123 seconds; ".123" means 123 fractionals + val_s = 0; + unit_multiple = 1; + break; + case Byte_ascii.Colon: + colon_pos++; + unit_multiple = 1; + break; + case Byte_ascii.Dash: + if (i == 0 && unit_val > 0) // only if first char && unit_val > 0 + sign = -1; + break; + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + if (fail_if_ws) return parse_null; + break; + default: + return parse_null; // invalid char; return null; + } + } + return sign * (val_f + (val_s * Divisors[1]) + (val_m * Divisors[2]) + (val_h * Divisors[3])); + } + public static String To_str(long frc, String fmt) { + String_bldr sb = String_bldr_.new_(); + int[] units = Split_long(frc, Divisors); + + if (String_.Eq(fmt, Time_span_.Fmt_Short)) { + for (int i = Idx_Hour; i > -1; i--) { + int val = units[i]; + if (val == 0 && i == Idx_Hour) continue; // skip hour if 0; ex: 01:02, instead of 00:01:02 + if (i == Idx_Frac) continue; // skip frac b/c fmt is short + if (sb.Count() > 0) // sb already has unit; add delimiter + sb.Add(Sprs[i]); + if (val < 10) // zeroPad + sb.Add("0"); + sb.Add(Int_.To_str(val)); + } + return sb.To_str_and_clear(); + } + boolean fmt_fracs = !String_.Eq(fmt, Time_span_.Fmt_NoFractionals); + boolean fmt_padZeros = String_.Eq(fmt, Time_span_.Fmt_PadZeros); + if (frc == 0) return fmt_padZeros ? "00:00:00.000" : "0"; + + int[] padZerosAry = ZeroPadding; + boolean first = true; + String dlm = ""; + int zeros = 0; + if (frc < 0) sb.Add("-"); // negative sign + for (int i = Idx_Hour; i > -1; i--) { // NOTE: "> Idx_Frac" b/c frc will be handled below + int val = units[i]; + if (i == Idx_Frac // only write fracs... + && !(val == 0 && fmt_padZeros) // ... if val == 0 && fmt is PadZeros + && !(val != 0 && fmt_fracs) // ... or val != 0 && fmt is PadZeros or Default + ) continue; + if (first && val == 0 && !fmt_padZeros) continue; // if first and val == 0, don't full pad (avoid "00:") + zeros = first && !fmt_padZeros ? 1 : padZerosAry[i]; // if first, don't zero pad (avoid "01") + dlm = first ? "" : Sprs[i]; // if first, don't use dlm (avoid ":01") + sb.Add(dlm); + sb.Add(Int_.To_str_pad_bgn_zero(val, zeros)); + first = false; + } + return sb.To_str(); + } + @gplx.Internal protected static int[] Split_long(long fracs, int[] divisors) { + int divLength = Array_.Len(divisors); + int[] rv = new int[divLength]; + long cur = Math_.Abs(fracs); + for (int i = divLength - 1; i > -1; i--) { + int divisor = divisors[i]; + long factor = cur / divisor; + rv[i] = (int)factor; + cur -= (factor * divisor); + } + return rv; + } + @gplx.Internal protected static long Merge_long(int[] vals, int[] divisors) { + long rv = 0; int valLength = Array_.Len(vals); + for (int i = 0; i < valLength; i++) { + rv += vals[i] * divisors[i]; + } + return rv; + } + public static final String Fmt_PadZeros = "00:00:00.000"; // u,h00:m00:s00.f000 + public static final String Fmt_Short = "short"; // u,h##:m#0:s00; + public static final String Fmt_Default = "0.000"; // v,#.000 + public static final String Fmt_NoFractionals = "0"; // v,# + @gplx.Internal protected static final int[] Divisors = { + 1, //1 fracs + 1000, //1,000 fracs in a second + 60000, //60,000 fracs in a minute (60 seconds * 1,000) + 3600000, //3,600,000 fracs in an hour (60 minutes * 60,000) + }; + public static final String MajorDelimiter = ":"; + public static final int + Idx_Frac = 0 + , Idx_Sec = 1 + , Idx_Min = 2 + , Idx_Hour = 3; + static int[] ZeroPadding = {3, 2, 2, 2,}; + static String[] Sprs = {".", MajorDelimiter, MajorDelimiter, "",}; + public static Time_span cast(Object arg) {try {return (Time_span)arg;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, Time_span.class, arg);}} + public static final double Ratio_f_to_s = 1000; +} diff --git a/100_core/src/gplx/To_str_able.java b/100_core/src/gplx/To_str_able.java new file mode 100644 index 000000000..69946ef14 --- /dev/null +++ b/100_core/src/gplx/To_str_able.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface To_str_able { + String To_str(); +} diff --git a/100_core/src/gplx/To_str_able_.java b/100_core/src/gplx/To_str_able_.java new file mode 100644 index 000000000..f44291933 --- /dev/null +++ b/100_core/src/gplx/To_str_able_.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +class To_str_able_ { + public static To_str_able as_(Object obj) {return obj instanceof To_str_able ? (To_str_able)obj : null;} +} diff --git a/100_core/src/gplx/Type_adp_.java b/100_core/src/gplx/Type_adp_.java new file mode 100644 index 000000000..80d7f7d8e --- /dev/null +++ b/100_core/src/gplx/Type_adp_.java @@ -0,0 +1,81 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class Type_adp_ { + public static boolean Eq(Class lhs, Class rhs) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + else return lhs.equals(rhs); + } + public static boolean Eq_typeSafe(Object o, Class expd) {if (o == null) return false; + Class actl = o.getClass(); + return Object_.Eq(expd, actl); + } + public static boolean IsAssignableFrom(Class lhs, Class rhs) {return lhs.isAssignableFrom(rhs);} + public static boolean Implements_intf_obj(Object cur, Class type) {return cur == null ? false : IsAssignableFrom(type, cur.getClass());} + public static boolean Is_array(Class t) {return t.isArray();} + public static Class ClassOf_obj(Object o) {return o.getClass();} + public static Class ClassOf_primitive(Object o) { + Class rv = o.getClass(); + if (rv == Integer.class) rv = int.class; + else if (rv == Long.class) rv = long.class; + else if (rv == Byte.class) rv = byte.class; + else if (rv == Short.class) rv = short.class; + return rv; + } + public static String FullNameOf_obj(Object o) {return FullNameOf_type(o.getClass());} + public static String FullNameOf_type(Class type) {return type.getCanonicalName();} + public static String NameOf_type(Class type) {return type.getName();} + public static String NameOf_obj(Object obj) {return obj == null ? String_.Null_mark : obj.getClass().getName();} + public static int To_tid_obj(Object o) { + if (o == null) return Tid__null; + Class type = o.getClass(); + return To_tid_type(type); + } + public static int To_tid_type(Class type) { + if (Type_adp_.Eq(type, Int_.Cls_ref_type)) return Tid__int; + else if (Type_adp_.Eq(type, String_.Cls_ref_type)) return Tid__str; + else if (Type_adp_.Eq(type, byte[].class)) return Tid__bry; + else if (Type_adp_.Eq(type, Bool_.Cls_ref_type)) return Tid__bool; + else if (Type_adp_.Eq(type, Byte_.Cls_ref_type)) return Tid__byte; + else if (Type_adp_.Eq(type, Long_.Cls_ref_type)) return Tid__long; + else if (Type_adp_.Eq(type, Double_.Cls_ref_type)) return Tid__double; + else if (Type_adp_.Eq(type, Decimal_adp_.Cls_ref_type)) return Tid__decimal; + else if (Type_adp_.Eq(type, DateAdp_.Cls_ref_type)) return Tid__date; + else if (Type_adp_.Eq(type, Float_.Cls_ref_type)) return Tid__float; + else if (Type_adp_.Eq(type, Short_.Cls_ref_type)) return Tid__short; + else if (Type_adp_.Eq(type, Char_.Cls_ref_type)) return Tid__char; + else return Tid__obj; + } + public static final int + Tid__obj = 0 + , Tid__null = 1 + , Tid__bool = 2 + , Tid__byte = 3 + , Tid__short = 4 + , Tid__int = 5 + , Tid__long = 6 + , Tid__float = 7 + , Tid__double = 8 + , Tid__char = 9 + , Tid__str = 10 + , Tid__bry = 11 + , Tid__date = 12 + , Tid__decimal = 13 + ; +} diff --git a/100_core/src/gplx/UsrDlg.java b/100_core/src/gplx/UsrDlg.java new file mode 100644 index 000000000..232a9baee --- /dev/null +++ b/100_core/src/gplx/UsrDlg.java @@ -0,0 +1,51 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class UsrDlg { + public int Verbosity() {return verbosity;} public UsrDlg Verbosity_(int v) {verbosity = v; return this;} int verbosity = UsrMsgWkr_.Type_Note; + public void Note(String text, Object... ary) {Exec(text, ary, noteWkrs);} + public void Warn(String text, Object... ary) {Exec(text, ary, warnWkrs);} + public void Stop(String text, Object... ary) {Exec(text, ary, stopWkrs);} + public void Note(UsrMsg msg) {Exec(UsrMsgWkr_.Type_Note, msg);} + public void Warn(UsrMsg msg) {Exec(UsrMsgWkr_.Type_Warn, msg);} + public void Stop(UsrMsg msg) {Exec(UsrMsgWkr_.Type_Stop, msg);} + public void Exec(int type, UsrMsg umsg) { + UsrMsgWkrList list = GetList(type); + list.Exec(umsg); + } + void Exec(String text, Object[] ary, UsrMsgWkrList list) { + String msg = String_.Format(text, ary); + list.Exec(UsrMsg.new_(msg)); + } + public void Reg(int type, UsrMsgWkr wkr) { + UsrMsgWkrList list = GetList(type); + list.Add(wkr); + } + public void RegOff(int type, UsrMsgWkr wkr) { + UsrMsgWkrList list = GetList(type); + list.Del(wkr); + } + UsrMsgWkrList GetList(int type) { + if (type == UsrMsgWkr_.Type_Note) return noteWkrs; + else if (type == UsrMsgWkr_.Type_Warn) return warnWkrs; + else if (type == UsrMsgWkr_.Type_Stop) return stopWkrs; + else throw Err_.new_unhandled(type); + } + UsrMsgWkrList noteWkrs = new UsrMsgWkrList(UsrMsgWkr_.Type_Note), warnWkrs = new UsrMsgWkrList(UsrMsgWkr_.Type_Warn), stopWkrs = new UsrMsgWkrList(UsrMsgWkr_.Type_Stop); + public static UsrDlg new_() {return new UsrDlg();} +} diff --git a/100_core/src/gplx/UsrDlg_.java b/100_core/src/gplx/UsrDlg_.java new file mode 100644 index 000000000..7d25013e6 --- /dev/null +++ b/100_core/src/gplx/UsrDlg_.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class UsrDlg_ { + public static final UsrDlg Instance = UsrDlg.new_(); +} diff --git a/100_core/src/gplx/UsrMsg.java b/100_core/src/gplx/UsrMsg.java new file mode 100644 index 000000000..47e05c432 --- /dev/null +++ b/100_core/src/gplx/UsrMsg.java @@ -0,0 +1,68 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.strings.*; import gplx.core.envs.*; +public class UsrMsg { + public int VisibilityDuration() {return visibilityDuration;} public UsrMsg VisibilityDuration_(int v) {visibilityDuration = v; return this;} int visibilityDuration = 3000; + public String Hdr() {return hdr;} public UsrMsg Hdr_(String val) {hdr = val; return this;} private String hdr; + public Ordered_hash Args() {return args;} Ordered_hash args = Ordered_hash_.New(); + public UsrMsg Add(String k, Object v) { + args.Add(k, Keyval_.new_(k, v)); + return this; + } + public UsrMsg Add_if_dupe_use_nth(String k, Object v) { + args.Add_if_dupe_use_nth(k, Keyval_.new_(k, v)); + return this; + } + public String XtoStrSingleLine() {return To_str(" ");} + public String To_str() {return To_str(Op_sys.Cur().Nl_str());} + String To_str(String spr) { + if (hdr == null) { + GfoMsg m = GfoMsg_.new_cast_(cmd); + for (int i = 0; i < args.Count(); i++) { + Keyval kv = (Keyval)args.Get_at(i); + m.Add(kv.Key(), kv.Val()); + } + return Object_.Xto_str_strict_or_null_mark(invk.Invk(GfsCtx.Instance, 0, cmd, m)); + } + String_bldr sb = String_bldr_.new_(); + sb.Add(hdr).Add(spr); + for (int i = 0; i < args.Count(); i++) { + Keyval kv = (Keyval)args.Get_at(i); + sb.Add_spr_unless_first("", " ", i); + sb.Add_fmt("{0}={1}", kv.Key(), kv.Val(), spr); + } + return sb.To_str(); + } + public static UsrMsg fmt_(String hdr, Object... ary) { + UsrMsg rv = new UsrMsg(); + rv.hdr = String_.Format(hdr, ary); + return rv; + } UsrMsg() {} + public static UsrMsg new_(String hdr) { + UsrMsg rv = new UsrMsg(); + rv.hdr = hdr; + return rv; + } + public static UsrMsg invk_(Gfo_invk invk, String cmd) { + UsrMsg rv = new UsrMsg(); + rv.invk = invk; + rv.cmd = cmd; + return rv; + } Gfo_invk invk; String cmd; +} diff --git a/100_core/src/gplx/UsrMsgWkr.java b/100_core/src/gplx/UsrMsgWkr.java new file mode 100644 index 000000000..b9437e916 --- /dev/null +++ b/100_core/src/gplx/UsrMsgWkr.java @@ -0,0 +1,50 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public interface UsrMsgWkr { + void ExecUsrMsg(int type, UsrMsg umsg); +} +class UsrMsgWkrList { + public void Add(UsrMsgWkr v) { + if (wkr == null && list == null) + wkr = v; + else { + if (list == null) { + list = List_adp_.New(); + list.Add(wkr); + wkr = null; + } + list.Add(v); + } + } + public void Del(UsrMsgWkr v) { +// list.Del(v); + } + public void Exec(UsrMsg umsg) { + if (wkr != null) + wkr.ExecUsrMsg(type, umsg); + else if (list != null) { + for (Object lObj : list) { + UsrMsgWkr l = (UsrMsgWkr)lObj; + l.ExecUsrMsg(type, umsg); + } + } + } + List_adp list; UsrMsgWkr wkr; int type; + public UsrMsgWkrList(int type) {this.type = type;} +} diff --git a/100_core/src/gplx/UsrMsgWkr_.java b/100_core/src/gplx/UsrMsgWkr_.java new file mode 100644 index 000000000..2bedb2294 --- /dev/null +++ b/100_core/src/gplx/UsrMsgWkr_.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class UsrMsgWkr_ { + public static final int + Type_None = 0 + , Type_Stop = 1 + , Type_Warn = 2 + , Type_Note = 4 + , Type_Log = 8 + ; +} diff --git a/100_core/src/gplx/UsrMsgWkr_console.java b/100_core/src/gplx/UsrMsgWkr_console.java new file mode 100644 index 000000000..c1ac51fdc --- /dev/null +++ b/100_core/src/gplx/UsrMsgWkr_console.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.consoles.*; +public class UsrMsgWkr_console implements UsrMsgWkr { + public void ExecUsrMsg(int type, UsrMsg umsg) { + String text = umsg.To_str(); + if (type == UsrMsgWkr_.Type_Warn) + text = "!!!!" + text; + else if (type == UsrMsgWkr_.Type_Stop) + text = "****" + text; + Console_adp__sys.Instance.Write_str(text); + } + public static void RegAll(UsrDlg dlg) { + UsrMsgWkr wkr = new UsrMsgWkr_console(); + dlg.Reg(UsrMsgWkr_.Type_Note, wkr); + dlg.Reg(UsrMsgWkr_.Type_Stop, wkr); + dlg.Reg(UsrMsgWkr_.Type_Warn, wkr); + } +} diff --git a/100_core/src/gplx/UsrMsgWkr_test.java b/100_core/src/gplx/UsrMsgWkr_test.java new file mode 100644 index 000000000..f24746a71 --- /dev/null +++ b/100_core/src/gplx/UsrMsgWkr_test.java @@ -0,0 +1,38 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class UsrMsgWkr_test implements UsrMsgWkr { + public void ExecUsrMsg(int type, UsrMsg m) { + msgs.Add(m); + } + public boolean HasWarn(UsrMsg um) { + for (int i = 0; i < msgs.Count(); i++) { + UsrMsg found = (UsrMsg)msgs.Get_at(i); + if (String_.Eq(um.To_str(), found.To_str())) return true; + } + return false; + } + public static UsrMsgWkr_test RegAll(UsrDlg dlg) { + UsrMsgWkr_test wkr = new UsrMsgWkr_test(); + dlg.Reg(UsrMsgWkr_.Type_Note, wkr); + dlg.Reg(UsrMsgWkr_.Type_Stop, wkr); + dlg.Reg(UsrMsgWkr_.Type_Warn, wkr); + return wkr; + } + List_adp msgs = List_adp_.New(); +} diff --git a/100_core/src/gplx/Virtual.java b/100_core/src/gplx/Virtual.java new file mode 100644 index 000000000..aeac6477e --- /dev/null +++ b/100_core/src/gplx/Virtual.java @@ -0,0 +1,19 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public @interface Virtual {} diff --git a/100_core/src/gplx/Yn.java b/100_core/src/gplx/Yn.java new file mode 100644 index 000000000..fc4a227a7 --- /dev/null +++ b/100_core/src/gplx/Yn.java @@ -0,0 +1,79 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import gplx.core.stores.*; +public class Yn { + public static final String Y = "y", N = "n"; + public static boolean parse_by_char_or(String v, boolean or) { + if (String_.Eq(v, Y)) return true; + else if (String_.Eq(v, N)) return false; + else return or; + } + public static boolean parse_or_n_(String v) {return parse_or(v, false);} + public static int parse_as_int(String v) { + if (v == null) return Bool_.__int; + else if (String_.Eq(v, "y")) return Bool_.Y_int; + else if (String_.Eq(v, "n")) return Bool_.N_int; + else return Bool_.__int; + } + public static boolean parse_or(String v, boolean or) { + int v_int = parse_as_int(v); + switch (v_int) { + case Bool_.N_int: return false; + case Bool_.Y_int: return true; + case Bool_.__int: return or; + default: throw Err_.new_unhandled(v_int); + } + } + public static boolean parse(String v) { + int v_int = parse_as_int(v); + if (v_int == Bool_.__int) Err_.new_unhandled(v); + return v_int == Bool_.Y_int; + } + public static String To_str(boolean v) {return v ? "y" : "n";} + public static String To_nullable_str(byte v) { + switch (v) { + case Bool_.Y_byte: return "y"; + case Bool_.N_byte: return "n"; + case Bool_.__byte: return "?"; + default: throw Err_.new_unhandled(v); + } + } + public static byte To_nullable_byte(String v) { + if (v != null && String_.Len(v) == 1) { + char c = String_.CharAt(v, 0); + switch (c) { + case 'y': return Bool_.Y_byte; + case 'n': return Bool_.N_byte; + case '?': return Bool_.__byte; + } + } + throw Err_.new_unhandled(v); + } + public static boolean store_bool_or(SrlMgr mgr, String key, boolean or) { + String v = mgr.SrlStrOr(key, ""); + return mgr.Type_rdr() ? parse_or(v, or) : or; + } + public static boolean coerce_(Object o) {String s = String_.as_(o); return s != null ? parse_or(s, false) : Bool_.cast(o);} + public static boolean readOrFalse_(DataRdr rdr, String key) {return read_(rdr, key, false);} + public static boolean readOrTrue_(DataRdr rdr, String key) {return read_(rdr, key, true);} + static boolean read_(DataRdr rdr, String key, boolean or) { + String v = rdr.ReadStrOr(key, null); + return parse_or(v, or); + } +} \ No newline at end of file diff --git a/100_core/src/gplx/core/bits/Bitmask_.java b/100_core/src/gplx/core/bits/Bitmask_.java new file mode 100644 index 000000000..cf377b359 --- /dev/null +++ b/100_core/src/gplx/core/bits/Bitmask_.java @@ -0,0 +1,40 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.bits; import gplx.*; import gplx.core.*; +public class Bitmask_ { + public static boolean Has_int(int val, int find) {return find == (val & find);} + public static int Flip_int(boolean enable, int val, int find) { + boolean has = find == (val & find); + return (has ^ enable) ? val ^ find : val; + } + public static int Add_int(int lhs, int rhs) {return lhs | rhs;} + public static int Add_int_ary(int... ary) { + int rv = 0; + int len = ary.length; + for (int i = 0; i < len; ++i) { + int itm = ary[i]; + if (rv == 0) + rv = itm; + else + rv = Flip_int(true, rv, itm); + } + return rv; + } + public static boolean Has_byte(byte val, byte find) {return find == (val & find);} + public static byte Add_byte(byte flag, byte itm) {return (byte)(flag | itm);} +} diff --git a/100_core/src/gplx/core/brys/Bfr_arg.java b/100_core/src/gplx/core/brys/Bfr_arg.java new file mode 100644 index 000000000..0c3312825 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bfr_arg.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public interface Bfr_arg { + void Bfr_arg__add(Bry_bfr bfr); +} diff --git a/100_core/src/gplx/core/brys/Bfr_arg_.java b/100_core/src/gplx/core/brys/Bfr_arg_.java new file mode 100644 index 000000000..8a58fba9f --- /dev/null +++ b/100_core/src/gplx/core/brys/Bfr_arg_.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +import gplx.core.brys.args.*; import gplx.core.brys.fmtrs.*; +public class Bfr_arg_ { + public static Bfr_arg__int New_int(int v) {return new Bfr_arg__int(v);} + public static Bfr_arg__byte New_byte(byte v) {return new Bfr_arg__byte(v);} + public static Bfr_arg__bry New_bry(String v) {return Bfr_arg__bry.New(Bry_.new_u8(v));} + public static Bfr_arg__bry New_bry(byte[] v) {return Bfr_arg__bry.New(v);} + public static Bfr_arg__bry_fmtr New_bry_fmtr__null() {return new Bfr_arg__bry_fmtr(null, null);} + public static Bfr_arg__bry_fmtr New_bry_fmtr(Bry_fmtr v, Bfr_arg... arg_ary) {return new Bfr_arg__bry_fmtr(v, arg_ary);} + public static final Bfr_arg Noop = new Bfr_arg___noop(); + public static void Clear(Bfr_arg_clearable... ary) { + for (Bfr_arg_clearable arg : ary) + arg.Bfr_arg__clear(); + } +} +class Bfr_arg___noop implements gplx.core.brys.Bfr_arg { + public void Bfr_arg__add(Bry_bfr bfr) {} +} diff --git a/100_core/src/gplx/core/brys/Bfr_arg_clearable.java b/100_core/src/gplx/core/brys/Bfr_arg_clearable.java new file mode 100644 index 000000000..84382c32e --- /dev/null +++ b/100_core/src/gplx/core/brys/Bfr_arg_clearable.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public interface Bfr_arg_clearable extends Bfr_arg { + void Bfr_arg__clear(); + boolean Bfr_arg__missing(); +} diff --git a/100_core/src/gplx/core/brys/Bry_bfr_able.java b/100_core/src/gplx/core/brys/Bry_bfr_able.java new file mode 100644 index 000000000..f0bd4a909 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_bfr_able.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public interface Bry_bfr_able { + void To_bfr(Bry_bfr bfr); +} diff --git a/100_core/src/gplx/core/brys/Bry_bfr_able_.java b/100_core/src/gplx/core/brys/Bry_bfr_able_.java new file mode 100644 index 000000000..f7fa3da78 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_bfr_able_.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public class Bry_bfr_able_ { + public static byte[][] To_bry_ary(Bry_bfr tmp_bfr, Bry_bfr_able[] ary) { + int len = ary.length; + byte[][] rv = new byte[len][]; + for (int i = 0; i < len; ++i) { + Bry_bfr_able itm = ary[i]; + if (itm != null) { + itm.To_bfr(tmp_bfr); + rv[i] = tmp_bfr.To_bry_and_clear(); + } + } + return rv; + } + public static byte[] To_bry_or_null(Bry_bfr tmp_bfr, Bry_bfr_able itm) { + if (itm == null) return null; + itm.To_bfr(tmp_bfr); + return tmp_bfr.To_bry_and_clear(); + } +} diff --git a/100_core/src/gplx/core/brys/Bry_bfr_mkr.java b/100_core/src/gplx/core/brys/Bry_bfr_mkr.java new file mode 100644 index 000000000..0239140a6 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_bfr_mkr.java @@ -0,0 +1,42 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public class Bry_bfr_mkr { + public Bry_bfr Get_b128() {return mkr_b128.Get();} private final Bry_bfr_mkr_mgr mkr_b128 = new Bry_bfr_mkr_mgr(Tid_b128, 128); + public Bry_bfr Get_b512() {return mkr_b512.Get();} private final Bry_bfr_mkr_mgr mkr_b512 = new Bry_bfr_mkr_mgr(Tid_b512, 512); + public Bry_bfr Get_k004() {return mkr_k004.Get();} private final Bry_bfr_mkr_mgr mkr_k004 = new Bry_bfr_mkr_mgr(Tid_k004, 4 * Io_mgr.Len_kb); + public Bry_bfr Get_m001() {return mkr_m001.Get();} private final Bry_bfr_mkr_mgr mkr_m001 = new Bry_bfr_mkr_mgr(Tid_m001, 1 * Io_mgr.Len_mb); + public void Clear() { + for (byte i = Tid_b128; i <= Tid_m001; i++) + mkr(i).Clear(); + } + public void Clear_fail_check() { + for (byte i = Tid_b128; i <= Tid_m001; i++) + mkr(i).Clear_fail_check(); + } + private Bry_bfr_mkr_mgr mkr(byte tid) { + switch (tid) { + case Tid_b128: return mkr_b128; + case Tid_b512: return mkr_b512; + case Tid_k004: return mkr_k004; + case Tid_m001: return mkr_m001; + default: throw Err_.new_unhandled(tid); + } + } + public static final byte Tid_b128 = 0, Tid_b512 = 1, Tid_k004 = 2, Tid_m001 = 3; +} diff --git a/100_core/src/gplx/core/brys/Bry_bfr_mkr_mgr.java b/100_core/src/gplx/core/brys/Bry_bfr_mkr_mgr.java new file mode 100644 index 000000000..556b3b25c --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_bfr_mkr_mgr.java @@ -0,0 +1,100 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public class Bry_bfr_mkr_mgr { + private final Object thread_lock = new Object(); + private final byte mgr_id; private final int reset; + private Bry_bfr[] used = Bry_bfr_.Ary_empty; private int used_len = 0, used_max = 0; + private int[] free; private int free_len; + public Bry_bfr_mkr_mgr(byte mgr_id, int reset) {// NOTE: random IndexOutOfBounds errors in Get around free[--free_len] with free_len being -1; put member variable initialization within thread_lock to try to avoid; DATE:2014-09-21 + this.mgr_id = mgr_id; + this.reset = reset; + this.free = Int_.Ary_empty; + this.free_len = 0; + } + public Bry_bfr Get() { + synchronized (thread_lock) { + Bry_bfr rv = null; int rv_idx = -1; + if (free_len > 0) { + try {rv_idx = free[--free_len];} catch (Exception e) {throw Err_.new_exc(e, "core", "failed to get free", "idx", free_len, "free_len", free.length);} + try {rv = used[rv_idx];} catch (Exception e) {throw Err_.new_exc(e, "core", "failed to get used", "idx", rv_idx, "used_len", used.length);} + } + else { + if (used_len == used_max) Expand(); + rv_idx = used_len++; + rv = used[rv_idx]; + if (rv == null) { + rv = Bry_bfr_.Reset(reset); + used[rv_idx] = rv; + } + } + rv.Mkr_init(this, rv_idx); + return rv.Clear(); // NOTE: ALWAYS call Clear when doing Get. caller may forget to call Clear, and reused bfr may have leftover bytes. unit tests will not catch, and difficult to spot in app + } + } + public void Rls(int idx) { + synchronized (thread_lock) { + if (idx == -1) throw Err_.new_wo_type("rls called on bfr that was not created by factory"); + int new_used_len = used_len - 1; + if (idx == new_used_len) + used_len = new_used_len; + else + free[free_len++] = idx; + } + } + public void Clear_fail_check() { + synchronized (thread_lock) { + for (int i = 0; i < used_max; i++) { + Bry_bfr itm = used[i]; + if (itm != null) { + if (!itm.Mkr_idx_is_null()) throw Err_.new_wo_type("failed to clear bfr", "mgr_id", mgr_id, "idx", Int_.To_str(i)); + itm.Clear(); + } + used[i] = null; + } + used = Bry_bfr_.Ary_empty; + free = Int_.Ary_empty; + free_len = used_len = used_max = 0; + } + } + public void Clear() { + synchronized (thread_lock) { + for (int i = 0; i < used_max; i++) { + Bry_bfr itm = used[i]; + if (itm != null) itm.Clear(); + used[i] = null; + } + used = Bry_bfr_.Ary_empty; + free = Int_.Ary_empty; + free_len = 0; + used_len = used_max = 0; + } + } + @gplx.Internal protected Bry_bfr[] Used() {return used;} + @gplx.Internal protected int Used_len() {return used_len;} + private void Expand() { + int new_max = used_max == 0 ? 2 : used_max * 2; + Bry_bfr[] new_ary = new Bry_bfr[new_max]; + Array_.Copy_to(used, 0, new_ary, 0, used_max); + used = new_ary; + used_max = new_max; + int[] new_free = new int[used_max]; + Array_.Copy_to(free, 0, new_free, 0, free_len); + free = new_free; + } +} diff --git a/100_core/src/gplx/core/brys/Bry_bfr_mkr_tst.java b/100_core/src/gplx/core/brys/Bry_bfr_mkr_tst.java new file mode 100644 index 000000000..5919a2c17 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_bfr_mkr_tst.java @@ -0,0 +1,57 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Bry_bfr_mkr_tst { + private final Bry_bfr_mkr_fxt fxt = new Bry_bfr_mkr_fxt(); + @Before public void setup() {fxt.Clear();} + @Test public void Get_1() {fxt.Clear().Get().Test__used(0);} + @Test public void Get_2() {fxt.Clear().Get().Get().Test__used(0, 1);} + @Test public void Get_3() {fxt.Clear().Get().Get().Get().Test__used(0, 1, 2);} + @Test public void Rls() {fxt.Clear().Get().Rls(0).Test__used();} + @Test public void Rls_skip_1() { + fxt.Clear().Get().Get().Rls(0).Test__used(-1, 1); + fxt.Get().Test__used(0, 1); + } + @Test public void Rls_skip_2_1() { + fxt.Clear().Get().Get().Get().Rls(1).Rls(0).Test__used(-1, -1, 2); + fxt.Get().Test__used(0, -1, 2); + fxt.Get().Test__used(0, 1, 2); + fxt.Get().Test__used(0, 1, 2, 3); + } + @Test public void Get_rls_get() { // PURPOSE: defect in which last rls failed b/c was not doing ++ if rv existed + fxt.Clear().Get().Rls(0).Get().Get().Rls(1).Rls(0).Test__used(); + } +} +class Bry_bfr_mkr_fxt { + private final Bry_bfr_mkr_mgr mkr = new Bry_bfr_mkr_mgr(Byte_.Zero, 32); + public Bry_bfr_mkr_fxt Clear() {mkr.Clear(); return this;} + public Bry_bfr_mkr_fxt Get() {mkr.Get(); return this;} + public Bry_bfr_mkr_fxt Rls(int i) {mkr.Used()[i].Mkr_rls(); return this;} + public Bry_bfr_mkr_fxt Test__used(int... expd) { + int actl_len = mkr.Used_len(); + int[] actl = new int[actl_len]; + for (int i = 0; i < actl_len; i++) { + Bry_bfr bfr = mkr.Used()[i]; + int actl_val = bfr == null ? -2 : bfr.Mkr_idx(); + actl[i] = actl_val; + } + Tfds.Eq_ary(expd, actl); + return this; + } +} diff --git a/100_core/src/gplx/core/brys/Bry_err_wkr.java b/100_core/src/gplx/core/brys/Bry_err_wkr.java new file mode 100644 index 000000000..abdeb566a --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_err_wkr.java @@ -0,0 +1,53 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +import gplx.core.errs.*; +public class Bry_err_wkr { + private String sect; private int sect_bgn; + public byte[] Src() {return src;} private byte[] src; + public String Page() {return page;} private String page; + public void Fail_throws_err_(boolean v) {this.fail_throws_err = v;} private boolean fail_throws_err = true; + public void Init_by_page(String page, byte[] src) {this.page = page; this.src = src;} + public void Init_by_sect(String sect, int sect_bgn) {this.sect = sect; this.sect_bgn = sect_bgn;} + public void Warn(String msg, Object... args) { + boolean old = fail_throws_err; + fail_throws_err = false; + this.Fail(msg, args); + fail_throws_err = old; + } + public int Fail(String msg, Object... args) {return Fail(msg, sect_bgn, sect_bgn + 255, args);} + private int Fail(String msg, int excerpt_bgn, int excerpt_end, Object[] args) { + String err_msg = Make_msg(msg, excerpt_bgn, excerpt_end, args); + Gfo_usr_dlg_.Instance.Warn_many("", "", err_msg); + if (fail_throws_err) throw Err_.new_("Bry_err_wkr", err_msg).Logged_y_(); + return Bry_find_.Not_found; + } + private String Make_msg(String msg, int excerpt_bgn, int excerpt_end, Object[] args) { + int args_len = args.length; + args_len += 6; + args = (Object[])Array_.Resize(args, args_len); + args[args_len - 6] = "page"; args[args_len - 5] = Quote(page); + args[args_len - 4] = "sect"; args[args_len - 3] = Quote(sect); + args[args_len - 2] = "text"; args[args_len - 1] = Bry_.Escape_ws(Bry_.Mid_safe(src, excerpt_bgn, excerpt_end)); + for (int i = 0; i < args_len - 6; i += 2) { + args[i + 1] = Quote(Object_.Xto_str_strict_or_null_mark(args[i + 1])); + } + return Err_msg.To_str(msg, args); + } + private static String Quote(String v) {return "'" + v + "'";} +} diff --git a/100_core/src/gplx/core/brys/Bry_rdr.java b/100_core/src/gplx/core/brys/Bry_rdr.java new file mode 100644 index 000000000..80ee6884f --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_rdr.java @@ -0,0 +1,231 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +import gplx.core.errs.*; import gplx.core.btries.*; +public class Bry_rdr { + private final gplx.core.primitives.Int_obj_ref pos_ref = gplx.core.primitives.Int_obj_ref.New_neg1(); + private final Btrie_rv trv = new Btrie_rv(); + public byte[] Src() {return src;} protected byte[] src; + public int Src_end() {return src_end;} protected int src_end; + public int Pos() {return pos;} protected int pos; + public boolean Pos_is_eos() {return pos == src_end;} + public Bry_rdr Dflt_dlm_(byte b) {this.dflt_dlm = b; return this;} private byte dflt_dlm; + public Bry_rdr Fail_throws_err_(boolean v) {err_wkr.Fail_throws_err_(v); return this;} + public Bry_rdr Init_by_src(byte[] src) {err_wkr.Init_by_page("", src); this.pos = 0; this.src = src; this.src_end = src.length; return this;} + public Bry_rdr Init_by_page(byte[] page, byte[] src, int src_len) {err_wkr.Init_by_page(String_.new_u8(page), src); this.pos = 0; this.src = src; this.src_end = src_len; return this;} + public Bry_rdr Init_by_sect(String sect, int sect_bgn, int pos) {err_wkr.Init_by_sect(sect, sect_bgn); this.pos = pos; return this;} + public Bry_rdr Init_by_wkr (Bry_err_wkr wkr, String sect, int pos, int src_end) { + this.pos = pos; this.src = wkr.Src(); this.src_end = src_end; + err_wkr.Init_by_page(wkr.Page(), src); + err_wkr.Init_by_sect(sect, pos); + return this; + } + public Bry_err_wkr Err_wkr() {return err_wkr;} private Bry_err_wkr err_wkr = new Bry_err_wkr(); + public int Move_to(int v) {this.pos = v; return pos;} + public int Move_by_one() {return Move_by(1);} + public int Move_by(int v) {this.pos += v; return pos;} + public int Find_fwd_lr() {return Find_fwd(dflt_dlm , Bool_.Y, Bool_.N, Fail_if_missing);} + public int Find_fwd_lr(byte find) {return Find_fwd(find , Bool_.Y, Bool_.N, Fail_if_missing);} + public int Find_fwd_lr_or(byte find, int or) + {return Find_fwd(find , Bool_.Y, Bool_.N, or);} + public int Find_fwd_lr(byte[] find) {return Find_fwd(find , Bool_.Y, Bool_.N, Fail_if_missing);} + public int Find_fwd_rr() {return Find_fwd(dflt_dlm , Bool_.N, Bool_.N, Fail_if_missing);} + public int Find_fwd_rr(byte find) {return Find_fwd(find , Bool_.N, Bool_.N, Fail_if_missing);} + public int Find_fwd_rr(byte[] find) {return Find_fwd(find , Bool_.N, Bool_.N, Fail_if_missing);} + public int Find_fwd_rr_or(byte[] find, int or) + {return Find_fwd(find , Bool_.N, Bool_.N, or);} + private int Find_fwd(byte find, boolean ret_lhs, boolean pos_lhs, int or) { + int find_pos = Bry_find_.Find_fwd(src, find, pos, src_end); + if (find_pos == Bry_find_.Not_found) { + if (or == Fail_if_missing) { + err_wkr.Fail("find failed", "find", Byte_ascii.To_str(find)); + return Bry_find_.Not_found; + } + else + return or; + } + pos = find_pos + (pos_lhs ? 0 : 1); + return ret_lhs ? find_pos : pos; + } + private int Find_fwd(byte[] find, boolean ret_lhs, boolean pos_lhs, int or) { + int find_pos = Bry_find_.Find_fwd(src, find, pos, src_end); + if (find_pos == Bry_find_.Not_found) { + if (or == Fail_if_missing) { + err_wkr.Fail("find failed", "find", String_.new_u8(find)); + return Bry_find_.Not_found; + } + else + return or; + } + pos = find_pos + (pos_lhs ? 0 : find.length); + return ret_lhs ? find_pos : pos; + } + public byte Read_byte() { + byte rv = src[pos]; + ++pos; + return rv; + } + public byte Read_byte_to() {return Read_byte_to(dflt_dlm);} + public byte Read_byte_to(byte to_char) { + byte rv = src[pos]; + ++pos; + if (pos < src_end) { + if (src[pos] != to_char) {err_wkr.Fail("read byte to failed", "to", Byte_ascii.To_str(to_char)); return Byte_.Max_value_127;} + ++pos; + } + return rv; + } + public double Read_double_to() {return Read_double_to(dflt_dlm);} + public double Read_double_to(byte to_char) { + byte[] bry = Read_bry_to(to_char); + return Double_.parse(String_.new_a7(bry)); + } + public int Read_int_to() {return Read_int_to(dflt_dlm);} + public int Read_int_to_non_num() {return Read_int_to(Byte_ascii.Null);} + public int Read_int_to(byte to_char) { + int bgn = pos; + int rv = 0; + int negative = 1; + while (pos < src_end) { + byte b = src[pos++]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv = (rv * 10) + (b - Byte_ascii.Num_0); + break; + case Byte_ascii.Dash: + if (negative == -1) { // 2nd negative + err_wkr.Fail("invalid int", "mid", String_.new_u8(src, bgn, pos)); + return Int_.Min_value; + } + else // 1st negative + negative = -1; // flag negative + break; + default: { + boolean match = b == to_char; + if (to_char == Byte_ascii.Null) {// hack for Read_int_to_non_num + --pos; + match = true; + } + if (!match) { + err_wkr.Fail("invalid int", "mid", String_.new_u8(src, bgn, pos)); + return Int_.Min_value; + } + return rv * negative; + } + } + } + if (bgn == pos) {err_wkr.Fail("int is empty"); return Int_.Min_value;} + return rv * negative; + } + public int Read_hzip_int(int reqd) { + int rv = gplx.core.encoders.Gfo_hzip_int_.Decode(reqd, src, src_end, pos, pos_ref); + pos = pos_ref.Val(); + return rv; + } + public byte[] Read_bry_to() {return Read_bry_to(dflt_dlm);} + public byte[] Read_bry_to(byte b) { + int bgn = pos; + return Bry_.Mid(src, bgn, Find_fwd_lr(b)); + } + public boolean Is(byte find) { + boolean rv = src[pos] == find; + if (rv) ++pos; // only advance if match; + return rv; + } + public boolean Is(byte[] find) { + int find_len = find.length; + int find_end = pos + find_len; + boolean rv = Bry_.Match(src, pos, find_end, find, 0, find_len); + if (rv) pos = find_end; // only advance if match; + return rv; + } + public int Chk(byte find) { + if (src[pos] != find) {err_wkr.Fail("failed check", "chk", Byte_.To_str(find)); return Bry_find_.Not_found;} + ++pos; + return pos; + } + public int Chk(byte[] find) { + int find_end = pos + find.length; + if (!Bry_.Match(src, pos, find_end, find)) {err_wkr.Fail("failed check", "chk", String_.new_u8(find)); return Bry_find_.Not_found;} + pos = find_end; + return pos; + } + public byte Chk(Btrie_slim_mgr trie) {return Chk(trie, pos, src_end);} + public void Chk_trie_val(Btrie_slim_mgr trie, byte val) { + byte rv = Chk_or(trie, Byte_.Max_value_127); + if (rv == Byte_.Max_value_127) err_wkr.Fail("failed trie check", "mid", String_.new_u8(Bry_.Mid_by_len_safe(src, pos, 16))); + } + public Object Chk_trie_as_obj(Btrie_rv trv, Btrie_slim_mgr trie) { + Object rv = trie.Match_at(trv, src, pos, src_end); if (rv == null) err_wkr.Fail("failed trie check", "mid", String_.new_u8(Bry_.Mid_by_len_safe(src, pos, 16))); + return rv; + } + public byte Chk_or(Btrie_rv trv, Btrie_slim_mgr trie, byte or) { + Object rv_obj = trie.Match_at(trv, src, pos, src_end); + return rv_obj == null ? or : ((gplx.core.primitives.Byte_obj_val)rv_obj).Val(); + } + public byte Chk_or(Btrie_slim_mgr trie, byte or) {return Chk_or(trie, pos, src_end, or);} + public byte Chk(Btrie_slim_mgr trie, int itm_bgn, int itm_end) { + byte rv = Chk_or(trie, itm_bgn, itm_end, Byte_.Max_value_127); + if (rv == Byte_.Max_value_127) {err_wkr.Fail("failed trie check", "mid", String_.new_u8(Bry_.Mid_by_len_safe(src, pos, 16))); return Byte_.Max_value_127;} + return rv; + } + public byte Chk_or(Btrie_slim_mgr trie, int itm_bgn, int itm_end, byte or) { + Object rv_obj = trie.Match_at(trv, src, itm_bgn, itm_end); + if (rv_obj == null) return or; + pos = trv.Pos(); + return ((gplx.core.primitives.Byte_obj_val)rv_obj).Val(); + } + @gplx.Virtual public Bry_rdr Skip_ws() { + while (pos < src_end) { + switch (src[pos]) { + case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: case Byte_ascii.Space: + ++pos; + break; + default: + return this; + } + } + return this; + } + public Bry_rdr Skip_alpha_num_under() { + while (pos < src_end) { + switch (src[pos]) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + case Byte_ascii.Underline: + ++pos; + break; + default: + return this; + } + } + return this; + } + private static final int Fail_if_missing = Int_.Min_value; +} diff --git a/100_core/src/gplx/core/brys/Bry_rdr_old.java b/100_core/src/gplx/core/brys/Bry_rdr_old.java new file mode 100644 index 000000000..7bcb26858 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_rdr_old.java @@ -0,0 +1,169 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public class Bry_rdr_old { + private byte[] scope = Bry_.Empty; + public byte[] Src() {return src;} protected byte[] src; + public int Src_len() {return src_len;} protected int src_len; + public void Init(byte[] src) {this.Init(Bry_.Empty, src, 0);} + public void Init(byte[] src, int pos) {this.Init(Bry_.Empty, src, pos);} + public void Init(byte[] scope, byte[] src, int pos) { + this.src = src; this.src_len = src.length; this.pos = pos; + this.scope = scope; + } + public int Pos() {return pos;} public Bry_rdr_old Pos_(int v) {this.pos = v; return this;} protected int pos; + public void Pos_add(int v) {pos += v;} + public boolean Pos_is_eos() {return pos == src_len;} + public boolean Pos_is_reading() {return pos < src_len;} + public void Pos_add_one() {++pos;} + public int Or_int() {return or_int;} public void Or_int_(int v) {or_int = v;} private int or_int = Int_.Min_value; + public byte[] Or_bry() {return or_bry;} public void Or_bry_(byte[] v) {or_bry = v;} private byte[] or_bry; + public int Find_fwd(byte find) {return Bry_find_.Find_fwd(src, find, pos);} + public int Find_fwd_ws() {return Bry_find_.Find_fwd_until_ws(src, pos, src_len);} + public int Find_fwd__pos_at_lhs(byte[] find_bry) {return Find_fwd__pos_at(find_bry, Bool_.N);} + public int Find_fwd__pos_at_rhs(byte[] find_bry) {return Find_fwd__pos_at(find_bry, Bool_.Y);} + public int Find_fwd__pos_at(byte[] find_bry, boolean pos_at_rhs) { + int find_pos = Bry_find_.Find_fwd(src, find_bry, pos, src_len); + if (pos_at_rhs) find_pos += find_bry.length; + if (find_pos != Bry_find_.Not_found) pos = find_pos; + return find_pos; + } + public byte Read_byte() {return src[pos];} + public int Read_int_to_semic() {return Read_int_to(Byte_ascii.Semic);} + public int Read_int_to_comma() {return Read_int_to(Byte_ascii.Comma);} + public int Read_int_to_pipe() {return Read_int_to(Byte_ascii.Pipe);} + public int Read_int_to_nl() {return Read_int_to(Byte_ascii.Nl);} + public int Read_int_to_quote() {return Read_int_to(Byte_ascii.Quote);} + public int Read_int_to_non_num(){return Read_int_to(Byte_ascii.Null);} + public int Read_int_to(byte to_char) { + int bgn = pos; + int rv = 0; + int negative = 1; + while (pos < src_len) { + byte b = src[pos++]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv = (rv * 10) + (b - Byte_ascii.Num_0); + break; + case Byte_ascii.Dash: + if (negative == -1) // 2nd negative + return or_int; // return or_int + else // 1st negative + negative = -1; // flag negative + break; + default: { + boolean match = b == to_char; + if (to_char == Byte_ascii.Null) {// hack for Read_int_to_non_num + --pos; + match = true; + } + return match ? rv * negative : or_int; + } + } + } + return bgn == pos ? or_int : rv * negative; + } + public byte[] Read_bry_to_nl() {return Read_bry_to(Byte_ascii.Nl);} + public byte[] Read_bry_to_semic() {return Read_bry_to(Byte_ascii.Semic);} + public byte[] Read_bry_to_pipe() {return Read_bry_to(Byte_ascii.Pipe);} + public byte[] Read_bry_to_quote() {return Read_bry_to(Byte_ascii.Quote);} + public byte[] Read_bry_to_apos() {return Read_bry_to(Byte_ascii.Apos);} + public byte[] Read_bry_to(byte to_char) { + int bgn = pos; + while (pos < src_len) { + byte b = src[pos]; + if (b == to_char) + return Bry_.Mid(src, bgn, pos++); + else + ++pos; + } + return bgn == pos ? or_bry : Bry_.Mid(src, bgn, src_len); + } + public boolean Read_yn_to_pipe() {return Read_byte_to_pipe() == Byte_ascii.Ltr_y;} + public byte Read_byte_to_pipe() { + byte rv = src[pos]; + pos += 2; // 1 for byte; 1 for pipe; + return rv; + } + public double Read_double_to_pipe() {return Read_double_to(Byte_ascii.Pipe);} + public double Read_double_to(byte to_char) { + byte[] double_bry = Read_bry_to(to_char); + return Double_.parse(String_.new_a7(double_bry)); // double will never have utf8 + } + @gplx.Virtual public Bry_rdr_old Skip_ws() { + while (pos < src_len) { + switch (src[pos]) { + case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: case Byte_ascii.Space: + ++pos; + break; + default: + return this; + } + } + return this; + } + public Bry_rdr_old Skip_alpha_num_under() { + while (pos < src_len) { + switch (src[pos]) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + case Byte_ascii.Underline: + ++pos; + break; + default: + return this; + } + } + return this; + } + public void Chk_bry_or_fail(byte[] bry) { + int bry_len = bry.length; + boolean match = Bry_.Match(src, pos, pos + bry_len, bry); + if (match) pos += bry_len; + else throw Err_.new_wo_type("bry.rdr:chk failed", "bry", bry, "pos", pos); + } + public void Chk_byte_or_fail(byte b) { + boolean match = pos < src_len ? src[pos] == b : false; + if (match) ++pos; + else throw Err_.new_wo_type("bry.rdr:chk failed", "byte", b, "pos", pos); + } + public byte[] Mid_by_len_safe(int len) { + int end = pos + len; if (end > src_len) end = src_len; + return Bry_.Mid(src, pos, end); + } + public boolean Chk_bry_wo_move(byte[] bry, int pos) { + int bry_len = bry.length; + return Bry_.Match(src, pos, pos + bry_len, bry); + } + public int Warn(String err, int bgn, int rv) { + int end = rv + 256; if (end > src_len) end = src_len; + Gfo_usr_dlg_.Instance.Warn_many("", "", "read failed: scope=~{0} err=~{1} mid=~{2}", scope, err, String_.new_u8(src, bgn, end)); + return rv + 1; // rv is always hook_bgn; add +1 to set at next character, else infinite loop + } +} \ No newline at end of file diff --git a/100_core/src/gplx/core/brys/Bry_rdr_tst.java b/100_core/src/gplx/core/brys/Bry_rdr_tst.java new file mode 100644 index 000000000..0d404ae22 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_rdr_tst.java @@ -0,0 +1,56 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Bry_rdr_tst { + @Before public void init() {fxt.Clear();} private Bry_rdr_fxt fxt = new Bry_rdr_fxt(); + @Test public void Int() { + fxt.Init_src("12|3456|789"); + fxt.Test_read_int(12); + fxt.Test_read_int(3456); + fxt.Test_read_int(789); + fxt.Test_read_int(Int_.Min_value); + } + @Test public void Int_negative() { + fxt.Init_src("-1|-2"); + fxt.Test_read_int(-1); + fxt.Test_read_int(-2); + } + @Test public void Bry() { + fxt.Init_src("abc|d||ef"); + fxt.Test_read_bry("abc"); + fxt.Test_read_bry("d"); + fxt.Test_read_bry(""); + fxt.Test_read_bry("ef"); + fxt.Test_read_bry(null); + } +} +class Bry_rdr_fxt { + private Bry_rdr_old rdr; + public void Clear() {rdr = new Bry_rdr_old();} + public Bry_rdr_fxt Init_src(String v) {rdr.Init(Bry_.new_u8(v)); return this;} + public Bry_rdr_fxt Init_pos(int v) {rdr.Pos_(v); return this;} + public void Test_read_int(int expd_val) { + Tfds.Eq(expd_val, rdr.Read_int_to_pipe()); + } + public void Test_read_bry(String expd_str) { + byte[] actl_bry = rdr.Read_bry_to_pipe(); + String actl_str = actl_bry == null ? null : String_.new_u8(actl_bry); + Tfds.Eq(expd_str, actl_str); + } +} diff --git a/100_core/src/gplx/core/brys/Bry_split_wkr.java b/100_core/src/gplx/core/brys/Bry_split_wkr.java new file mode 100644 index 000000000..2c0747021 --- /dev/null +++ b/100_core/src/gplx/core/brys/Bry_split_wkr.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys; import gplx.*; import gplx.core.*; +public interface Bry_split_wkr { + int Split(byte[] src, int itm_bgn, int itm_end); +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__bry.java b/100_core/src/gplx/core/brys/args/Bfr_arg__bry.java new file mode 100644 index 000000000..f1168ea6d --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__bry.java @@ -0,0 +1,43 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bfr_arg__bry implements Bfr_arg_clearable { + private int tid; + private byte[] src; private int src_bgn, src_end; + private Bfr_arg arg; + public void Set_by_mid(byte[] src, int bgn, int end) {this.tid = Tid_mid; this.src = src; this.src_bgn = bgn; this.src_end = end;} + public void Set_by_val(byte[] src) {this.tid = Tid_val; this.src = src;} + public void Set_by_arg(Bfr_arg arg) {this.tid = Tid_arg; this.arg = arg;} + public void Bfr_arg__clear() { + tid = Tid_nil; + src = null; src_bgn = src_end = -1; + arg = null; + } + public boolean Bfr_arg__missing() {return tid == Tid_nil;} + public void Bfr_arg__add(Bry_bfr bfr) { + switch (tid) { + case Tid_val: bfr.Add(src); break; + case Tid_mid: bfr.Add_mid(src, src_bgn, src_end); break; + case Tid_arg: arg.Bfr_arg__add(bfr); break; + case Tid_nil: break; + } + } + public static Bfr_arg__bry New_empty() {return new Bfr_arg__bry();} + public static Bfr_arg__bry New(byte[] v) {Bfr_arg__bry rv = new Bfr_arg__bry(); rv.Set_by_val(v); return rv;} + private static final int Tid_nil = 0, Tid_val = 1, Tid_mid = 2, Tid_arg = 3; +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__bry_ary.java b/100_core/src/gplx/core/brys/args/Bfr_arg__bry_ary.java new file mode 100644 index 000000000..b03015787 --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__bry_ary.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bfr_arg__bry_ary implements Bfr_arg { + private byte[][] bry_ary; + public Bfr_arg__bry_ary Set(byte[]... v) {this.bry_ary = v; return this;} + public void Bfr_arg__add(Bry_bfr bfr) { + for (byte[] bry : bry_ary) + bfr.Add(bry); + } +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmt.java b/100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmt.java new file mode 100644 index 000000000..860a4f756 --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmt.java @@ -0,0 +1,38 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import gplx.core.brys.*; +public class Bfr_arg__bry_fmt implements Bfr_arg { + private final Bry_fmt fmt; + private Object[] arg_ary; + public Bfr_arg__bry_fmt(Bry_fmt fmt, Object... arg_ary) { + this.fmt = fmt; + Args_(arg_ary); + } + public void Bfr_arg__clear() {arg_ary = null;} + public boolean Bfr_arg__missing() {return arg_ary == null;} + + public Bfr_arg__bry_fmt Args_(Object... arg_ary) { + this.arg_ary = arg_ary; + return this; + } + public void Bfr_arg__add(Bry_bfr bfr) { + if (Bfr_arg__missing()) return; + fmt.Bld_many(bfr, arg_ary); + } +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmtr.java b/100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmtr.java new file mode 100644 index 000000000..7698416dd --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__bry_fmtr.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import gplx.core.brys.fmtrs.*; +public class Bfr_arg__bry_fmtr implements Bfr_arg { + private Bry_fmtr fmtr; private Object[] arg_ary; + public Bfr_arg__bry_fmtr(Bry_fmtr fmtr, Object[] arg_ary) {Set(fmtr, arg_ary);} + public Bfr_arg__bry_fmtr Set(Bry_fmtr fmtr, Object... arg_ary) { + this.fmtr = fmtr; this.arg_ary = arg_ary; + return this; + } + public void Bfr_arg__add(Bry_bfr bfr) {fmtr.Bld_bfr_many(bfr, arg_ary);} +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__byte.java b/100_core/src/gplx/core/brys/args/Bfr_arg__byte.java new file mode 100644 index 000000000..34d695bba --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__byte.java @@ -0,0 +1,23 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bfr_arg__byte implements Bfr_arg { + private final byte byt; + public Bfr_arg__byte(byte byt) {this.byt = byt;} + public void Bfr_arg__add(Bry_bfr bfr) {bfr.Add_byte(byt);} +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__decimal_int.java b/100_core/src/gplx/core/brys/args/Bfr_arg__decimal_int.java new file mode 100644 index 000000000..8942b1b75 --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__decimal_int.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bfr_arg__decimal_int implements Bfr_arg { + public int Val() {return val;} public Bfr_arg__decimal_int Val_(int v) {val = v; return this;} int val; + public Bfr_arg__decimal_int Places_(int v) {places = v; multiple = (int)Math_.Pow(10, v); return this;} int multiple = 1000, places = 3; + public void Bfr_arg__add(Bry_bfr bfr) { + bfr.Add_int_variable(val / multiple).Add_byte(Byte_ascii.Dot).Add_int_fixed(val % multiple, places); + } +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__int.java b/100_core/src/gplx/core/brys/args/Bfr_arg__int.java new file mode 100644 index 000000000..2715e0947 --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__int.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bfr_arg__int implements Bfr_arg { + private int val, val_digits; + public Bfr_arg__int(int v) {Set(v);} + public Bfr_arg__int Set(int v) { + this.val = Int_.cast(v); + this.val_digits = Int_.DigitCount(val); + return this; + } + public void Bfr_arg__add(Bry_bfr bfr) {bfr.Add_int_digits(val_digits, val);} +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__time.java b/100_core/src/gplx/core/brys/args/Bfr_arg__time.java new file mode 100644 index 000000000..bc1f7a742 --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__time.java @@ -0,0 +1,55 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bfr_arg__time implements Bfr_arg { + public Bfr_arg__time() { + units_len = units.length; + } + public long Seconds() {return seconds;} public Bfr_arg__time Seconds_(long v) {seconds = v; return this;} long seconds; + byte[][] segs = new byte[][] + { Bry_.new_a7("d") + , Bry_.new_a7("h") + , Bry_.new_a7("m") + , Bry_.new_a7("s") + }; + int[] units = new int[] {86400, 3600, 60, 1}; + int units_len; + byte[] spr = new byte[] {Byte_ascii.Space}; + public void Bfr_arg__add(Bry_bfr bfr) { + if (seconds == 0) { // handle 0 separately (since it will always be < than units[*] + bfr.Add_int_fixed(0, 2).Add(segs[units_len - 1]); + return; + } + long val = seconds; + boolean dirty = false; + for (int i = 0; i < units_len; i++) { + long unit = units[i]; + long seg = 0; + if (val >= unit) { // unit has value; EX: 87000 > 86400, so unit is 1 day + seg = val / unit; + val = val - (seg * unit); + } + if (seg > 0 || dirty) { // dirty check allows for 0 in middle units (EX: 1h 0m 1s) + if (dirty) bfr.Add(spr); + if (seg < 10) bfr.Add_byte(Byte_ascii.Num_0); // 0 pad + bfr.Add_long_variable(seg).Add(segs[i]); + dirty = true; + } + } + } +} diff --git a/100_core/src/gplx/core/brys/args/Bfr_arg__time_tst.java b/100_core/src/gplx/core/brys/args/Bfr_arg__time_tst.java new file mode 100644 index 000000000..235b4eb8e --- /dev/null +++ b/100_core/src/gplx/core/brys/args/Bfr_arg__time_tst.java @@ -0,0 +1,42 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.args; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import org.junit.*; +public class Bfr_arg__time_tst { + @Test public void Basic() { + Time_fmtr_arg_fxt fxt = new Time_fmtr_arg_fxt().Clear(); + fxt.XferAry( 1, "01s"); // seconds + fxt.XferAry( 62, "01m 02s"); // minutes + fxt.XferAry( 3723, "01h 02m 03s"); // hours + fxt.XferAry( 93784, "01d 02h 03m 04s"); // days + fxt.XferAry( 0, "00s"); // handle 0 seconds + fxt.XferAry( 3601, "01h 00m 01s"); // handle 0 in middle unit + } +} +class Time_fmtr_arg_fxt { + public Time_fmtr_arg_fxt Clear() { + if (arg == null) arg = new Bfr_arg__time(); + return this; + } Bfr_arg__time arg; + public void XferAry(int seconds, String expd) { + Bry_bfr bfr = Bry_bfr_.Reset(255); + arg.Seconds_(seconds); + arg.Bfr_arg__add(bfr); + Tfds.Eq(expd, bfr.To_str()); + } +} diff --git a/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr.java b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr.java new file mode 100644 index 000000000..8fead38ad --- /dev/null +++ b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr.java @@ -0,0 +1,266 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmtrs; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import gplx.core.brys.*; import gplx.core.primitives.*; import gplx.core.strings.*; +public class Bry_fmtr { + public byte[] Fmt() {return fmt;} private byte[] fmt = Bry_.Empty; + public boolean Fmt_null() {return fmt.length == 0;} + public Bry_fmtr_eval_mgr Eval_mgr() {return eval_mgr;} public Bry_fmtr Eval_mgr_(Bry_fmtr_eval_mgr v) {eval_mgr = v; return this;} Bry_fmtr_eval_mgr eval_mgr = Bry_fmtr_eval_mgr_gfs.Instance; + public Bry_fmtr Fmt_(byte[] v) {fmt = v; dirty = true; return this;} public Bry_fmtr Fmt_(String v) {return Fmt_(Bry_.new_u8(v));} + public Bry_fmtr Keys_(String... ary) { + if (keys == null) keys = Hash_adp_.New(); + else keys.Clear(); + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) + keys.Add(Bry_obj_ref.New(Bry_.new_u8(ary[i])), new Int_obj_val(i)); + dirty = true; + return this; + } Hash_adp keys = null; + public void Bld_bfr(Bry_bfr bfr, byte[]... args) { + if (dirty) Compile(); + int args_len = args.length; + for (int i = 0; i < itms_len; i++) { + Bry_fmtr_itm itm = itms[i]; + if (itm.Arg) { + int arg_idx = itm.ArgIdx; + if (arg_idx < args_len) + bfr.Add(args[arg_idx]); + else + bfr.Add(missing_bgn).Add_int_variable(arg_idx + missing_adj).Add(missing_end); + } + else + bfr.Add(itm.Dat); + } + } + public void Bld_bfr_none(Bry_bfr bfr) { + if (dirty) Compile(); + for (int i = 0; i < itms_len; i++) { + Bry_fmtr_itm itm = itms[i]; + if (itm.Arg) + bfr.Add_byte(char_escape).Add_byte(char_arg_bgn).Add_int_variable(itm.ArgIdx).Add_byte(char_arg_end); + else + bfr.Add(itm.Dat); + } + } + public void Bld_bfr(Bry_bfr bfr, Bfr_arg... args) { + if (dirty) Compile(); + for (int i = 0; i < itms_len; i++) { + Bry_fmtr_itm itm = itms[i]; + if (itm.Arg) + args[itm.ArgIdx].Bfr_arg__add(bfr); + else + bfr.Add(itm.Dat); + } + } + public void Bld_bfr_one(Bry_bfr bfr, Object val) { + Bld_bfr_one_ary[0] = val; + Bld_bfr_ary(bfr, Bld_bfr_one_ary); + } Object[] Bld_bfr_one_ary = new Object[1]; + public void Bld_bfr_many(Bry_bfr bfr, Object... args) {Bld_bfr_ary(bfr, args);} + public void Bld_bfr_ary(Bry_bfr bfr, Object[] args) { + if (dirty) Compile(); + int args_len = args.length; + for (int i = 0; i < itms_len; i++) { + Bry_fmtr_itm itm = itms[i]; + if (itm.Arg) { + int arg_idx = itm.ArgIdx; + if (arg_idx > -1 && arg_idx < args_len) + bfr.Add_obj(args[itm.ArgIdx]); + else + bfr.Add_byte(char_escape).Add_byte(char_arg_bgn).Add_int_variable(arg_idx).Add_byte(char_arg_end); + } + else + bfr.Add(itm.Dat); + } + } + public byte[] Bld_bry_none(Bry_bfr bfr) {Bld_bfr_ary(bfr, Object_.Ary_empty); return bfr.To_bry_and_clear();} + public byte[] Bld_bry_many(Bry_bfr bfr, Object... args) { + Bld_bfr_ary(bfr, args); + return bfr.To_bry_and_clear(); + } + public String Bld_str_many(Bry_bfr bfr, String fmt, Object... args) { + this.Fmt_(fmt).Bld_bfr_many(bfr, args); + return bfr.To_str_and_clear(); + } + public String Bld_str_many(String... args) { + if (dirty) Compile(); + String_bldr rv = String_bldr_.new_(); + int args_len = args.length; + for (int i = 0; i < itms_len; i++) { + Bry_fmtr_itm itm = itms[i]; + if (itm.Arg) { + int arg_idx = itm.ArgIdx; + if (arg_idx < args_len) + rv.Add(args[arg_idx]); + else + rv.Add(missing_bgn).Add(arg_idx + missing_adj).Add(missing_end); + } + else + rv.Add(itm.DatStr()); + } + return rv.To_str(); + } private Bry_fmtr_itm[] itms; int itms_len; + public byte[] Missing_bgn() {return missing_bgn;} public Bry_fmtr Missing_bgn_(byte[] v) {missing_bgn = v; return this;} private byte[] missing_bgn = missing_bgn_static; static byte[] missing_bgn_static = Bry_.new_u8("~{"), missing_end_static = Bry_.new_u8("}"); + public byte[] Missing_end() {return missing_end;} public Bry_fmtr Missing_end_(byte[] v) {missing_end = v; return this;} private byte[] missing_end = missing_end_static; + public int Missing_adj() {return missing_adj;} public Bry_fmtr Missing_adj_(int v) {missing_adj = v; return this;} int missing_adj; + public boolean Fail_when_invalid_escapes() {return fail_when_invalid_escapes;} public Bry_fmtr Fail_when_invalid_escapes_(boolean v) {fail_when_invalid_escapes = v; return this;} private boolean fail_when_invalid_escapes = true; + public Bry_fmtr Compile() { + synchronized (this) { // THREAD: DATE:2015-04-29 + Bry_bfr lkp_bfr = Bry_bfr_.New_w_size(16); + int fmt_len = fmt.length; int fmt_end = fmt_len - 1; int fmt_pos = 0; + byte[] trg_bry = new byte[fmt_len]; int trg_pos = 0; + boolean lkp_is_active = false, lkp_is_numeric = true; + byte nxt_byte, tmp_byte; + List_adp list = List_adp_.New(); + fmt_args_exist = false; + while (true) { + if (fmt_pos > fmt_end) break; + byte cur_byte = fmt[fmt_pos]; + if (lkp_is_active) { + if (cur_byte == char_arg_end) { + if (lkp_is_numeric) + list.Add(Bry_fmtr_itm.arg_(lkp_bfr.To_int(0) - baseInt)); + else { + byte[] key_fmt = lkp_bfr.To_bry(); + Object idx_ref = keys.Get_by(Bry_obj_ref.New(key_fmt)); + if (idx_ref == null) { + int lkp_bfr_len = lkp_bfr.Len(); + byte[] lkp_bry = lkp_bfr.Bfr(); + trg_bry[trg_pos++] = char_escape; + trg_bry[trg_pos++] = char_arg_bgn; + for (int i = 0; i < lkp_bfr_len; i++) + trg_bry[trg_pos++] = lkp_bry[i]; + trg_bry[trg_pos++] = char_arg_end; + } + else { + list.Add(Bry_fmtr_itm.arg_(((Int_obj_val)idx_ref).Val() - baseInt)); + } + } + lkp_is_active = false; + lkp_bfr.Clear(); + fmt_args_exist = true; + } + else { + lkp_bfr.Add_byte(cur_byte); + switch (cur_byte) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + default: + lkp_is_numeric = false; + break; + } + } + fmt_pos += 1; + } + else if (cur_byte == char_escape) { + if (fmt_pos == fmt_end) { + if (fail_when_invalid_escapes) + throw Err_.new_wo_type("escape char encountered but no more chars left"); + else { + trg_bry[trg_pos] = cur_byte; + break; + } + } + nxt_byte = fmt[fmt_pos + 1]; + if (nxt_byte == char_arg_bgn) { + if (trg_pos > 0) {list.Add(Bry_fmtr_itm.dat_(trg_bry, trg_pos)); trg_pos = 0;} // something pending; add it to list + int eval_lhs_bgn = fmt_pos + 2; + if (eval_lhs_bgn < fmt_len && fmt[eval_lhs_bgn] == char_eval_bgn) { // eval found + fmt_pos = Compile_eval_cmd(fmt, fmt_len, eval_lhs_bgn, list); + continue; + } + else { + lkp_is_active = true; + lkp_is_numeric = true; + } + } + else { // ~{0}; ~~ -> ~; ~n -> newLine; ~t -> tab + if (nxt_byte == char_escape) tmp_byte = char_escape; + else if (nxt_byte == char_escape_nl) tmp_byte = Byte_ascii.Nl; + else if (nxt_byte == char_escape_tab) tmp_byte = Byte_ascii.Tab; + else { + if (fail_when_invalid_escapes) throw Err_.new_wo_type("unknown escape code", "code", Char_.By_int(nxt_byte), "fmt_pos", fmt_pos + 1); + else + tmp_byte = cur_byte; + } + trg_bry[trg_pos++] = tmp_byte; + } + fmt_pos += 2; + } + else { + trg_bry[trg_pos++] = cur_byte; + fmt_pos += 1; + } + } + if (lkp_is_active) throw Err_.new_wo_type("idx mode not closed"); + if (trg_pos > 0) {list.Add(Bry_fmtr_itm.dat_(trg_bry, trg_pos)); trg_pos = 0;} + itms = (Bry_fmtr_itm[])list.To_ary(Bry_fmtr_itm.class); + itms_len = itms.length; + return this; + } + } + int Compile_eval_cmd(byte[] fmt, int fmt_len, int eval_lhs_bgn, List_adp list) { + int eval_lhs_end = Bry_find_.Find_fwd(fmt, char_eval_end, eval_lhs_bgn + Int_.Const_dlm_len, fmt_len); if (eval_lhs_end == Bry_find_.Not_found) throw Err_.new_wo_type("eval_lhs_end_invalid: could not find eval_lhs_end", "snip", String_.new_u8(fmt, eval_lhs_bgn, fmt_len)); + byte[] eval_dlm = Bry_.Mid(fmt, eval_lhs_bgn , eval_lhs_end + Int_.Const_dlm_len); + int eval_rhs_bgn = Bry_find_.Find_fwd(fmt, eval_dlm , eval_lhs_end + Int_.Const_dlm_len, fmt_len); if (eval_rhs_bgn == Bry_find_.Not_found) throw Err_.new_wo_type("eval_rhs_bgn_invalid: could not find eval_rhs_bgn", "snip", String_.new_u8(fmt, eval_lhs_end, fmt_len)); + byte[] eval_cmd = Bry_.Mid(fmt, eval_lhs_end + Int_.Const_dlm_len, eval_rhs_bgn); + byte[] eval_rslt = eval_mgr.Eval(eval_cmd); + int eval_rhs_end = eval_rhs_bgn + Int_.Const_dlm_len + eval_dlm.length; + if (eval_rslt == null) eval_rslt = Bry_.Mid(fmt, eval_lhs_bgn - 2, eval_rhs_end); // not found; return original argument + list.Add(Bry_fmtr_itm.dat_bry_(eval_rslt)); + return eval_rhs_end; + } + static final String GRP_KEY = "gplx.Bry_fmtr"; + public boolean Fmt_args_exist() {return fmt_args_exist;} private boolean fmt_args_exist; + boolean dirty = true; + int baseInt = 0; + public static final byte char_escape = Byte_ascii.Tilde, char_arg_bgn = Byte_ascii.Curly_bgn, char_arg_end = Byte_ascii.Curly_end, char_escape_nl = Byte_ascii.Ltr_n, char_escape_tab = Byte_ascii.Ltr_t, char_eval_bgn = Byte_ascii.Lt, char_eval_end = Byte_ascii.Gt; + public static final Bry_fmtr Null = new Bry_fmtr().Fmt_(""); + public static Bry_fmtr New__tmp() {return new Bry_fmtr().Fmt_("").Keys_();} + public static Bry_fmtr new_(String fmt, String... keys) {return new Bry_fmtr().Fmt_(fmt).Keys_(keys);} // NOTE: keys may seem redundant, but are needed to align ordinals with proc; EX: fmt may be "~{A} ~{B}" or "~{B} ~{A}"; call will always be Bld(a, b); passing in "A", "B" guarantees A is 0 and B is 1; + public static Bry_fmtr new_(byte[] fmt, String... keys) {return new Bry_fmtr().Fmt_(fmt).Keys_(keys);} // NOTE: keys may seem redundant, but are needed to align ordinals with proc; EX: fmt may be "~{A} ~{B}" or "~{B} ~{A}"; call will always be Bld(a, b); passing in "A", "B" guarantees A is 0 and B is 1; + public static Bry_fmtr new_() {return new Bry_fmtr();} + public static Bry_fmtr keys_(String... keys) {return new Bry_fmtr().Keys_(keys);} + public static Bry_fmtr new_bry_(byte[] fmt, String... keys) {return new Bry_fmtr().Fmt_(fmt).Keys_(keys);} + public static String New_fmt_str(String key, Object[] args) { + tmp_bfr.Clear(); + tmp_bfr.Add_str_u8(key); + tmp_bfr.Add_byte(Byte_ascii.Colon); + int args_len = args.length; + for (int i = 0; i < args_len; i++) { // add " 0='~{0}'" + tmp_bfr.Add_byte(Byte_ascii.Space); + tmp_bfr.Add_int_variable(i); + tmp_bfr.Add_byte(Byte_ascii.Eq); + tmp_bfr.Add_byte(Byte_ascii.Apos); + tmp_bfr.Add_byte(Byte_ascii.Tilde); + tmp_bfr.Add_byte(Byte_ascii.Curly_bgn); + tmp_bfr.Add_int_variable(i); + tmp_bfr.Add_byte(Byte_ascii.Curly_end); + tmp_bfr.Add_byte(Byte_ascii.Apos); + } + return tmp_bfr.To_str_and_clear(); + } static Bry_bfr tmp_bfr = Bry_bfr_.Reset(255); + public void Bld_bfr_many_and_set_fmt(Object... args) { + Bry_bfr bfr = Bry_bfr_.New(); + this.Bld_bfr_many(bfr, args); + byte[] bry = bfr.To_bry_and_clear(); + this.Fmt_(bry).Compile(); + } + public static String Escape_tilde(String v) {return String_.Replace(v, "~", "~~");} +} diff --git a/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr.java b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr.java new file mode 100644 index 000000000..a531534f8 --- /dev/null +++ b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmtrs; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public interface Bry_fmtr_eval_mgr { + boolean Enabled(); void Enabled_(boolean v); + byte[] Eval(byte[] cmd); +} diff --git a/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_.java b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_.java new file mode 100644 index 000000000..a3b4d0cc9 --- /dev/null +++ b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmtrs; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bry_fmtr_eval_mgr_ { + public static Io_url Eval_url(Bry_fmtr_eval_mgr eval_mgr, byte[] fmt) { + if (eval_mgr == null) return Io_url_.new_any_(String_.new_u8(fmt)); + Bry_bfr bfr = Bry_bfr_.Reset(255); + Bry_fmtr fmtr = Bry_fmtr.New__tmp(); + fmtr.Eval_mgr_(eval_mgr).Fmt_(fmt).Bld_bfr_none(bfr); + return Io_url_.new_any_(bfr.To_str_and_clear()); + } +} diff --git a/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_gfs.java b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_gfs.java new file mode 100644 index 000000000..a81514b57 --- /dev/null +++ b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_eval_mgr_gfs.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmtrs; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import gplx.langs.gfs.*; +public class Bry_fmtr_eval_mgr_gfs implements Bry_fmtr_eval_mgr { + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public byte[] Eval(byte[] cmd) { + return enabled ? Bry_.new_u8(Object_.Xto_str_strict_or_null_mark(GfsCore.Instance.ExecText(String_.new_u8(cmd)))) : null; + } + public static final Bry_fmtr_eval_mgr_gfs Instance = new Bry_fmtr_eval_mgr_gfs(); Bry_fmtr_eval_mgr_gfs() {} +} diff --git a/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_itm.java b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_itm.java new file mode 100644 index 000000000..7e5ce85d1 --- /dev/null +++ b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_itm.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmtrs; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bry_fmtr_itm { + Bry_fmtr_itm(boolean arg, int argIdx, byte[] dat) { + this.Arg = arg; this.ArgIdx = argIdx; this.Dat = dat; + } + public boolean Arg; + public int ArgIdx; + public byte[] Dat; + public String DatStr() { + if (datStr == null) datStr = String_.new_u8(Dat); + return datStr; + } String datStr; + public static Bry_fmtr_itm arg_(int idx) {return new Bry_fmtr_itm(true, idx, Bry_.Empty);} + public static Bry_fmtr_itm dat_(byte[] dat, int len) {return new Bry_fmtr_itm(false, -1, Bry_.Mid(dat, 0, len));} + public static Bry_fmtr_itm dat_bry_(byte[] bry) {return new Bry_fmtr_itm(false, -1, bry);} +} diff --git a/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_tst.java b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_tst.java new file mode 100644 index 000000000..409242a00 --- /dev/null +++ b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_tst.java @@ -0,0 +1,74 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmtrs; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import org.junit.*; +public class Bry_fmtr_tst { + private final Bry_fmtr_fxt fxt = new Bry_fmtr_fxt(); + @Test public void Text() {fxt.Clear().Fmt("a").Test("a");} + @Test public void Idx__1() {fxt.Clear().Fmt("~{0}").Args("a").Test("a");} + @Test public void Idx__3() {fxt.Clear().Fmt("~{0}~{1}~{2}").Args("a", "b", "c").Test("abc");} + @Test public void Idx__mix() {fxt.Clear().Fmt("a~{0}c~{1}e").Args("b", "d").Test("abcde");} + @Test public void Idx__missing() {fxt.Clear().Fmt("~{0}").Test("~{0}");} + + @Test public void Key__basic() {fxt.Clear().Fmt("~{key}").Keys("key").Args("a").Test("a");} + @Test public void Key__mult() {fxt.Clear().Fmt("~{key1}~{key2}").Keys("key1", "key2").Args("a", "b").Test("ab");} + @Test public void Key__repeat() {fxt.Clear().Fmt("~{key1}~{key1}").Keys("key1").Args("a").Test("aa");} + + @Test public void Mix() {fxt.Clear().Fmt("~{key1}~{1}").Keys("key1", "key2").Args("a", "b").Test("ab");} + + @Test public void Simple() { + fxt.Clear().Fmt("0~{key1}1~{key2}2").Keys("key1", "key2").Args(".", ",").Test("0.1,2"); + } + @Test public void Cmd() { + Bry_fmtr_tst_mok mok = new Bry_fmtr_tst_mok(); + Bry_fmtr fmtr = Bry_fmtr.new_("0~{key1}2~{<>3<>}4", "key1").Eval_mgr_(mok); + Tfds.Eq("012~{<>3<>}4", fmtr.Bld_str_many("1")); + mok.Enabled_(true); + Tfds.Eq("01234", fmtr.Bld_str_many("1")); + } + @Test public void Bld_bfr_many_and_set_fmt() { + fxt.Bld_bfr_many_and_set_fmt("a~{0}c", Object_.Ary("b"), "abc"); + } + @Test public void Escape_tilde() { + Tfds.Eq("~~~~~~", Bry_fmtr.Escape_tilde("~~~")); + } +} +class Bry_fmtr_tst_mok implements Bry_fmtr_eval_mgr { + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public byte[] Eval(byte[] cmd) { + return enabled ? cmd : null; + } +} +class Bry_fmtr_fxt { + private final Bry_fmtr fmtr = Bry_fmtr.new_(); + private final Bry_bfr bfr = Bry_bfr_.New(); + private Object[] args; + public Bry_fmtr_fxt Clear() {fmtr.Fmt_(String_.Empty).Keys_(String_.Empty); args = Object_.Ary_empty; return this;} + public Bry_fmtr_fxt Fmt (String fmt) {fmtr.Fmt_(fmt); return this;} + public Bry_fmtr_fxt Keys(String... args) {fmtr.Keys_(args); return this;} + public Bry_fmtr_fxt Args(Object... args) {this.args = args; return this;} + public void Test(String expd) { + fmtr.Bld_bfr_many(bfr, args); + Tfds.Eq(expd, bfr.To_str_and_clear()); + } + public void Bld_bfr_many_and_set_fmt(String fmt, Object[] args, String expd) { + fmtr.Fmt_(fmt); + fmtr.Bld_bfr_many_and_set_fmt(args); + Tfds.Eq(expd, String_.new_a7(fmtr.Fmt())); + } +} diff --git a/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_vals.java b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_vals.java new file mode 100644 index 000000000..be1affacb --- /dev/null +++ b/100_core/src/gplx/core/brys/fmtrs/Bry_fmtr_vals.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmtrs; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import gplx.core.brys.*; +public class Bry_fmtr_vals implements Bfr_arg { + private final Bry_fmtr fmtr; private Object[] vals; + Bry_fmtr_vals(Bry_fmtr fmtr) {this.fmtr = fmtr;} + public Bry_fmtr_vals Vals_(Object... v) {this.vals = v; return this;} + public void Bfr_arg__add(Bry_bfr bfr) { + fmtr.Bld_bfr_ary(bfr, vals); + } + public static Bry_fmtr_vals new_fmt(String fmt, String... keys) { + Bry_fmtr fmtr = Bry_fmtr.new_(fmt, keys); + return new Bry_fmtr_vals(fmtr); + } + public static Bry_fmtr_vals new_(Bry_fmtr fmtr) {return new Bry_fmtr_vals(fmtr);} +} diff --git a/100_core/src/gplx/core/brys/fmts/Bfr_fmt_arg.java b/100_core/src/gplx/core/brys/fmts/Bfr_fmt_arg.java new file mode 100644 index 000000000..35866f1d6 --- /dev/null +++ b/100_core/src/gplx/core/brys/fmts/Bfr_fmt_arg.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmts; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bfr_fmt_arg { + public Bfr_fmt_arg(byte[] key, Bfr_arg arg) {this.Key = key; this.Arg = arg;} + public byte[] Key; + public Bfr_arg Arg; + public static final Bfr_fmt_arg[] Ary_empty = new Bfr_fmt_arg[0]; +} diff --git a/100_core/src/gplx/core/brys/fmts/Bry_fmt_itm.java b/100_core/src/gplx/core/brys/fmts/Bry_fmt_itm.java new file mode 100644 index 000000000..95a9d8f9d --- /dev/null +++ b/100_core/src/gplx/core/brys/fmts/Bry_fmt_itm.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmts; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bry_fmt_itm { + public Bry_fmt_itm(int tid, int src_bgn, int src_end) { + this.Tid = tid; + this.Src_bgn = src_bgn; + this.Src_end = src_end; + } + public int Tid; + public int Src_bgn; + public int Src_end; + public int Key_idx; + public Bfr_arg Arg; + + public static final int Tid__txt = 0, Tid__key = 1, Tid__arg = 2; +} diff --git a/100_core/src/gplx/core/brys/fmts/Bry_fmt_parser_.java b/100_core/src/gplx/core/brys/fmts/Bry_fmt_parser_.java new file mode 100644 index 000000000..75e62202b --- /dev/null +++ b/100_core/src/gplx/core/brys/fmts/Bry_fmt_parser_.java @@ -0,0 +1,93 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmts; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +public class Bry_fmt_parser_ { + public static Bry_fmt_itm[] Parse(byte escape, byte grp_bgn, byte grp_end, Bfr_fmt_arg[] args, byte[][] keys, byte[] src) { + int src_len = src.length; + int pos = 0; + int txt_bgn = -1; + int key_idx = -1; + Hash_adp_bry keys_hash = Hash_adp_bry.cs(); + List_adp list = List_adp_.New(); + while (true) { + boolean is_last = pos == src_len; + byte b = is_last ? escape : src[pos]; + if (b == escape) { + if (txt_bgn != -1) list.Add(new Bry_fmt_itm(Bry_fmt_itm.Tid__txt, txt_bgn, pos)); + if (is_last) break; + ++pos; + if (pos == src_len) throw Err_.new_("bry_fmtr", "fmt cannot end with escape", "escape", Byte_ascii.To_str(escape), "raw", src); + b = src[pos]; + if (b == escape) { + list.Add(new Bry_fmt_itm(Bry_fmt_itm.Tid__txt, pos, pos + 1)); + ++pos; + } + else if (b == grp_bgn) { + ++pos; + int grp_end_pos = Bry_find_.Find_fwd(src, grp_end, pos); if (grp_end_pos == Bry_find_.Not_found) throw Err_.new_("bry_fmtr", "grp_end missing", "grp_bgn", Byte_ascii.To_str(grp_bgn), "grp_end", Byte_ascii.To_str(grp_end), "raw", src); + byte[] key_bry = Bry_.Mid(src, pos, grp_end_pos); + Bry_fmt_itm key_itm = (Bry_fmt_itm)keys_hash.Get_by_bry(key_bry); + if (key_itm == null) { + key_itm = new Bry_fmt_itm(Bry_fmt_itm.Tid__key, pos - 2, grp_end_pos + 1); // -2 to get "~{"; +1 to get "}" + key_itm.Key_idx = ++key_idx; + keys_hash.Add(key_bry, key_itm); + } + list.Add(key_itm); + pos = grp_end_pos + 1; + } + else throw Err_.new_("bry_fmtr", "escape must be followed by escape or group_bgn", "escape", Byte_ascii.To_str(escape), "group_bgn", Byte_ascii.To_str(escape), "raw", src); + txt_bgn = -1; + } + else { + if (txt_bgn == -1) txt_bgn = pos; + ++pos; + } + } + Bry_fmt_itm[] rv = (Bry_fmt_itm[])list.To_ary_and_clear(Bry_fmt_itm.class); + int len = args.length; + for (int i = 0; i < len; ++i) { + Bfr_fmt_arg arg = args[i]; + Bry_fmt_itm key_itm = (Bry_fmt_itm)keys_hash.Get_by(arg.Key); if (key_itm == null) continue; + key_itm.Tid = Bry_fmt_itm.Tid__arg; + key_itm.Arg = arg.Arg; + } + len = keys.length; + for (int i = 0; i < len; ++i) { + byte[] key = keys[i]; + Bry_fmt_itm key_itm = (Bry_fmt_itm)keys_hash.Get_by(key); if (key_itm == null) continue; // NOTE: ignore missing keys; EX: fmt=a~{b}c keys=b,d; do not fail b/c ~{d} is not in fmt; allows redefining from tests + key_itm.Key_idx = i; + } + return rv; + } + public static byte[][] Parse_keys(byte[] src) { + Ordered_hash list = Ordered_hash_.New_bry(); + int src_len = src.length; + int pos = -1; + while (pos < src_len) { + int lhs_pos = Bry_find_.Move_fwd(src, Bry_arg_lhs, pos + 1, src_len); + if (lhs_pos == Bry_find_.Not_found) break; // no more "~{" + int rhs_pos = Bry_find_.Find_fwd(src, Byte_ascii.Curly_end, lhs_pos, src_len); + if (rhs_pos == Bry_find_.Not_found) throw Err_.new_("bry_fmt", "unable to find closing }", "src", src); + if (rhs_pos - lhs_pos == 0) throw Err_.new_("bry_fmt", "{} will result in empty key", "src", src); + byte[] key = Bry_.Mid(src, lhs_pos, rhs_pos); + if (!list.Has(key)) list.Add(key, key); + pos = rhs_pos; // NOTE: auto-increment done at top of loop + } + return (byte[][])list.To_ary(byte[].class); + } private static final byte[] Bry_arg_lhs = Bry_.new_a7("~{"); +} diff --git a/100_core/src/gplx/core/brys/fmts/Bry_fmt_tst.java b/100_core/src/gplx/core/brys/fmts/Bry_fmt_tst.java new file mode 100644 index 000000000..a44d96bae --- /dev/null +++ b/100_core/src/gplx/core/brys/fmts/Bry_fmt_tst.java @@ -0,0 +1,52 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmts; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import org.junit.*; +public class Bry_fmt_tst { + private final Bry_fmt_fxt fxt = new Bry_fmt_fxt(); + @Test public void Text() {fxt.Clear().Fmt("a").Test("a");} + @Test public void Key__basic() {fxt.Clear().Fmt("~{key}").Vals("a").Test("a");} + @Test public void Key__mult() {fxt.Clear().Fmt("~{key1}~{key2}").Vals("a", "b").Test("ab");} + @Test public void Key__repeat() {fxt.Clear().Fmt("~{key1}~{key1}").Vals("a").Test("aa");} + @Test public void Key__missing() {fxt.Clear().Fmt("~{key}").Test("~{key}");} + @Test public void Tilde() {fxt.Clear().Fmt("~~~~").Test("~~");} + @Test public void Simple() {fxt.Clear().Fmt("0~{key1}1~{key2}2").Vals(".", ",").Test("0.1,2");} + @Test public void Arg() {fxt.Clear().Fmt("~{custom}").Args("custom", new Bfr_fmt_arg_mok(123)).Test("123");} + @Test public void Keys() {fxt.Clear().Fmt("~{b}~{c}~{a}").Keys("a", "b", "c").Vals("a", "b", "c").Test("bca");} +} +class Bfr_fmt_arg_mok implements Bfr_arg { + private int num; + public Bfr_fmt_arg_mok(int num) {this.num = num;} + public void Bfr_arg__add(Bry_bfr bfr) { + bfr.Add_int_variable(num); + } +} +class Bry_fmt_fxt { + private final Bry_fmt fmt = new Bry_fmt(Bry_.Empty, Bry_.Ary_empty, Bfr_fmt_arg.Ary_empty); + private final Bry_bfr bfr = Bry_bfr_.New(); + private Object[] vals; + public Bry_fmt_fxt Clear() {vals = Object_.Ary_empty; return this;} + public Bry_fmt_fxt Fmt(String s) {fmt.Fmt_(s); return this;} + public Bry_fmt_fxt Vals(Object... vals) {this.vals = vals; return this;} + public Bry_fmt_fxt Args(String key, Bfr_arg arg) {fmt.Args_(new Bfr_fmt_arg(Bry_.new_u8(key), arg)); return this;} + public Bry_fmt_fxt Keys(String... keys) {fmt.Keys_(Bry_.Ary(keys)); return this;} + public void Test(String expd) { + fmt.Bld_many(bfr, vals); + Tfds.Eq(expd, bfr.To_str_and_clear()); + } +} diff --git a/100_core/src/gplx/core/brys/fmts/Bry_keys_parser_tst.java b/100_core/src/gplx/core/brys/fmts/Bry_keys_parser_tst.java new file mode 100644 index 000000000..3c10fa7f4 --- /dev/null +++ b/100_core/src/gplx/core/brys/fmts/Bry_keys_parser_tst.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.brys.fmts; import gplx.*; import gplx.core.*; import gplx.core.brys.*; +import org.junit.*; +public class Bry_keys_parser_tst { + private final Bry_keys_parser_fxt fxt = new Bry_keys_parser_fxt(); + @Test public void None() {fxt.Test("a");} + @Test public void One() {fxt.Test("~{a}" , "a");} + @Test public void Many() {fxt.Test("~{a}b~{c}d~{e}" , "a", "c", "e");} + @Test public void Dupe() {fxt.Test("~{a}b~{a}" , "a");} + @Test public void Bug__space() {fxt.Test("~{a}~{b} ~{c}" , "a", "b", "c");} // DATE:2016-08-02 +} +class Bry_keys_parser_fxt { + public void Test(String fmt, String... expd) { + Tfds.Eq_ary(expd, String_.Ary(Bry_fmt_parser_.Parse_keys(Bry_.new_u8(fmt)))); + } +} diff --git a/100_core/src/gplx/core/btries/Btrie_bwd_mgr.java b/100_core/src/gplx/core/btries/Btrie_bwd_mgr.java new file mode 100644 index 000000000..2abe2d271 --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_bwd_mgr.java @@ -0,0 +1,119 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import gplx.core.primitives.*; +public class Btrie_bwd_mgr { + public int Match_pos() {return match_pos;} private int match_pos; + public Object Match_exact(byte[] src, int bgn_pos, int end_pos) { + Object rv = Match(src[bgn_pos], src, bgn_pos, end_pos); + return rv == null ? null : match_pos - bgn_pos == end_pos - bgn_pos ? rv : null; + } + + public Object Match_at(Btrie_rv rv, byte[] src, int bgn_pos, int end_pos) {return Match_at_w_b0(rv, src[bgn_pos], src, bgn_pos, end_pos);} + public Object Match_at_w_b0(Btrie_rv rv, byte b, byte[] src, int bgn_pos, int end_pos) { + // NOTE: bgn, end follows same semantics as fwd where bgn >= & end < except reversed: bgn <= & end >; EX: "abcde" should pass 5, -1 + Object rv_obj = null; + int rv_pos = bgn_pos; + int cur_pos = bgn_pos; + Btrie_slim_itm cur = root; + while (true) { + Btrie_slim_itm nxt = cur.Ary_find(b); + if (nxt == null) { // nxt does not have b; return rv_obj; + rv.Init(rv_pos, rv_obj); + return rv_obj; + } + --cur_pos; + if (nxt.Ary_is_empty()) { // nxt is leaf; return nxt.Val() (which should be non-null) + rv_obj = nxt.Val(); + rv.Init(cur_pos, rv_obj); + return rv_obj; + } + Object nxt_val = nxt.Val(); + if (nxt_val != null) {rv_pos = cur_pos; rv_obj = nxt_val;} // nxt is node; cache rv_obj (in case of false match) + if (cur_pos == end_pos) { // increment cur_pos and exit if end_pos + rv.Init(rv_pos, rv_obj); + return rv_obj; + } + b = src[cur_pos]; + cur = nxt; + } + } + + public Object Match_bgn(byte[] src, int bgn_pos, int end_pos) {return Match(src[bgn_pos], src, bgn_pos, end_pos);} + public Object Match(byte b, byte[] src, int bgn_pos, int end_pos) { + // NOTE: bgn, end follows same semantics as fwd where bgn >= & end < except reversed: bgn <= & end >; EX: "abcde" should pass 5, -1 + Object rv = null; int cur_pos = match_pos = bgn_pos; + Btrie_slim_itm cur = root; + while (true) { + Btrie_slim_itm nxt = cur.Ary_find(b); if (nxt == null) return rv; // nxt does not hav b; return rv; + --cur_pos; + if (nxt.Ary_is_empty()) {match_pos = cur_pos; return nxt.Val();} // nxt is leaf; return nxt.Val() (which should be non-null) + Object nxt_val = nxt.Val(); + if (nxt_val != null) {match_pos = cur_pos; rv = nxt_val;} // nxt is node; cache rv (in case of false match) + if (cur_pos == end_pos) return rv; // increment cur_pos and exit if src_len + b = src[cur_pos]; + cur = nxt; + } + } + public Btrie_bwd_mgr Add_str_byte(String key, byte val) {return Add(Bry_.new_u8(key), Byte_obj_val.new_(val));} + public Btrie_bwd_mgr Add_byteVal_strAry(byte val, String... ary) { + int ary_len = ary.length; + Byte_obj_val byteVal = Byte_obj_val.new_(val); + for (int i = 0; i < ary_len; i++) { + String itm = ary[i]; + Add(Bry_.new_u8(itm), byteVal); + } + return this; + } + public Btrie_bwd_mgr Add(String key, Object val) {return Add(Bry_.new_u8(key), val);} + public Btrie_bwd_mgr Add(byte[] key, Object val) { + if (val == null) throw Err_.new_wo_type("null objects cannot be registered", "key", String_.new_u8(key)); + int key_len = key.length; + Btrie_slim_itm cur = root; + for (int i = key_len - 1; i > -1; i--) { + byte b = key[i]; + if (root.Case_any() && (b > 64 && b < 91)) b += 32; + Btrie_slim_itm nxt = cur.Ary_find(b); + if (nxt == null) + nxt = cur.Ary_add(b, null); + if (i == 0) + nxt.Val_set(val); + cur = nxt; + } + count++; // FUTURE: do not increment if replacing value + return this; + } + public int Count() {return count;} private int count; + public void Del(byte[] key) { + int key_len = key.length; + Btrie_slim_itm cur = root; + for (int i = 0; i < key_len; i++) { + byte b = key[i]; + cur = cur.Ary_find(b); + if (cur == null) break; + cur.Ary_del(b); + } + count--; // FUTURE: do not decrement if not found + } + public void Clear() {root.Clear(); count = 0;} + public static Btrie_bwd_mgr cs_() {return new Btrie_bwd_mgr(false);} + public static Btrie_bwd_mgr ci_() {return new Btrie_bwd_mgr(true);} + public Btrie_bwd_mgr(boolean caseAny) { + root = new Btrie_slim_itm(Byte_.Zero, null, caseAny); + } private Btrie_slim_itm root; +} diff --git a/100_core/src/gplx/core/btries/Btrie_bwd_mgr_tst.java b/100_core/src/gplx/core/btries/Btrie_bwd_mgr_tst.java new file mode 100644 index 000000000..c017b4ff4 --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_bwd_mgr_tst.java @@ -0,0 +1,87 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Btrie_bwd_mgr_tst { + @Before public void init() {} private Btrie_bwd_mgr trie; + private void ini_setup1() { + trie = new Btrie_bwd_mgr(false); + run_Add("c" , 1); + run_Add("abc" , 123); + } + @Test public void Get_by() { + ini_setup1(); + tst_MatchAtCur("c" , 1); + tst_MatchAtCur("abc" , 123); + tst_MatchAtCur("bc" , 1); + tst_MatchAtCur("yzabc" , 123); + tst_MatchAtCur("ab" , null); + } + @Test public void Fetch_intl() { + trie = new Btrie_bwd_mgr(false); + run_Add("a�", 1); + tst_MatchAtCur("a�" , 1); + tst_MatchAtCur("�" , null); + } + @Test public void Eos() { + ini_setup1(); + tst_Match("ab", Byte_ascii.Ltr_c, 2, 123); + } + @Test public void Match_exact() { + ini_setup1(); + tst_MatchAtCurExact("c", 1); + tst_MatchAtCurExact("bc", null); + tst_MatchAtCurExact("abc", 123); + } + private void ini_setup2() { + trie = new Btrie_bwd_mgr(false); + run_Add("a" , 1); + run_Add("b" , 2); + } + @Test public void Match_2() { + ini_setup2(); + tst_MatchAtCur("a", 1); + tst_MatchAtCur("b", 2); + } + private void ini_setup_caseAny() { + trie = Btrie_bwd_mgr.ci_(); + run_Add("a" , 1); + run_Add("b" , 2); + } + @Test public void CaseAny() { + ini_setup_caseAny(); + tst_MatchAtCur("a", 1); + tst_MatchAtCur("A", 1); + } + private void run_Add(String k, int val) {trie.Add(Bry_.new_u8(k), val);} + private void tst_Match(String srcStr, byte b, int bgn_pos, int expd) { + byte[] src = Bry_.new_u8(srcStr); + Object actl = trie.Match(b, src, bgn_pos, -1); + Tfds.Eq(expd, actl); + } + private void tst_MatchAtCur(String srcStr, Object expd) { + byte[] src = Bry_.new_u8(srcStr); + Object actl = trie.Match(src[src.length - 1], src, src.length - 1, -1); + Tfds.Eq(expd, actl); + } + private void tst_MatchAtCurExact(String srcStr, Object expd) { + byte[] src = Bry_.new_u8(srcStr); + Object actl = trie.Match_exact(src, src.length - 1, -1); + Tfds.Eq(expd, actl); + } +} diff --git a/100_core/src/gplx/core/btries/Btrie_fast_mgr.java b/100_core/src/gplx/core/btries/Btrie_fast_mgr.java new file mode 100644 index 000000000..9dd0da5da --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_fast_mgr.java @@ -0,0 +1,194 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import gplx.core.primitives.*; +public class Btrie_fast_mgr { + private ByteTrieItm_fast root; + public boolean CaseAny() {return root.CaseAny();} public Btrie_fast_mgr CaseAny_(boolean v) {root.CaseAny_(v); return this;} + public int Match_pos() {return match_pos;} private int match_pos; + + public Object Match_at(Btrie_rv rv, byte[] src, int bgn_pos, int end_pos) {return Match_at_w_b0(rv, src[bgn_pos], src, bgn_pos, end_pos);} + public Object Match_at_w_b0(Btrie_rv rv, byte b, byte[] src, int bgn_pos, int src_end) { + Object rv_obj = null; + int rv_pos = bgn_pos; + ByteTrieItm_fast nxt = root.Ary_find(b); + if (nxt == null) { // nxt does not have b; return rv; + rv.Init(rv_pos, rv_obj); + return rv_obj; + } + int cur_pos = bgn_pos + 1; + ByteTrieItm_fast cur = root; + while (true) { + if (nxt.Ary_is_empty()) { // nxt is leaf; return nxt.Val() (which should be non-null) + rv_obj = nxt.Val(); + rv.Init(cur_pos, rv_obj); + return rv_obj; + } + Object nxt_val = nxt.Val(); + if (nxt_val != null) { // nxt is node; cache rv (in case of false match) + rv_pos = cur_pos; + rv_obj = nxt_val; + } + if (cur_pos == src_end) { // eos; exit + rv.Init(rv_pos, rv_obj); + return rv_obj; + } + b = src[cur_pos]; + cur = nxt; + nxt = cur.Ary_find(b); + if (nxt == null) { + rv.Init(rv_pos, rv_obj); + return rv_obj; + } + ++cur_pos; + } + } + + public Object Match_exact(byte[] src, int bgn_pos, int end_pos) { + Object rv = Match_bgn_w_byte(src[bgn_pos], src, bgn_pos, end_pos); + return rv == null ? null : match_pos - bgn_pos == end_pos - bgn_pos ? rv : null; + } + public Object Match_bgn(byte[] src, int bgn_pos, int end_pos) {return Match_bgn_w_byte(src[bgn_pos], src, bgn_pos, end_pos);} + public Object Match_bgn_w_byte(byte b, byte[] src, int bgn_pos, int src_len) { + match_pos = bgn_pos; + ByteTrieItm_fast nxt = root.Ary_find(b); if (nxt == null) return null; // nxt does not have b; return rv; + Object rv = null; int cur_pos = bgn_pos + 1; + ByteTrieItm_fast cur = root; + while (true) { + if (nxt.Ary_is_empty()) {match_pos = cur_pos; return nxt.Val();} // nxt is leaf; return nxt.Val() (which should be non-null) + Object nxt_val = nxt.Val(); + if (nxt_val != null) {match_pos = cur_pos; rv = nxt_val;} // nxt is node; cache rv (in case of false match) + if (cur_pos == src_len) return rv; // eos; exit + b = src[cur_pos]; + cur = nxt; + nxt = cur.Ary_find(b); if (nxt == null) return rv; + ++cur_pos; + } + } + public Btrie_fast_mgr Add_bry_byte(byte key, byte val) {return Add(new byte[] {key}, Byte_obj_val.new_(val));} + public Btrie_fast_mgr Add_bry_byte(byte[] key, byte val) {return Add(key, Byte_obj_val.new_(val));} + public Btrie_fast_mgr Add_str_byte(String key, byte val) {return Add(Bry_.new_u8(key), Byte_obj_val.new_(val));} + public Btrie_fast_mgr Add(byte key, Object val) {return Add(new byte[] {key}, val);} + public Btrie_fast_mgr Add(String key, Object val) {return Add(Bry_.new_u8(key), val);} + public Btrie_fast_mgr Add(byte[] key, Object val) { + if (val == null) throw Err_.new_wo_type("null objects cannot be registered", "key", String_.new_u8(key)); + int key_len = key.length; int key_end = key_len - 1; + ByteTrieItm_fast cur = root; + for (int i = 0; i < key_len; i++) { + byte b = key[i]; + ByteTrieItm_fast nxt = cur.Ary_find(b); + if (nxt == null) + nxt = cur.Ary_add(b, null); + if (i == key_end) + nxt.Val_set(val); + cur = nxt; + } + return this; + } + public Btrie_fast_mgr Add_stub(byte tid, String s) { + byte[] bry = Bry_.new_u8(s); + Btrie_itm_stub stub = new Btrie_itm_stub(tid, bry); + return Add(bry, stub); + } + public void Del(byte[] key) { + int key_len = key.length; + ByteTrieItm_fast cur = root; + for (int i = 0; i < key_len; i++) { + byte b = key[i]; + Object itm_obj = cur.Ary_find(b); + if (itm_obj == null) break; // b not found; no match; exit; + ByteTrieItm_fast itm = (ByteTrieItm_fast)itm_obj; + if (i == key_len - 1) { // last char + if (itm.Val() == null) break; // itm does not have val; EX: trie with "abc", and "ab" deleted + if (itm.Ary_is_empty()) + cur.Ary_del(b); + else + itm.Val_set(null); + } + else { // mid char; set itm as cur and continue + cur = itm; + } + } + } + public void Clear() {root.Clear();} + public byte[] Replace(Bry_bfr tmp_bfr, byte[] src, int bgn, int end) { + int pos = bgn; + boolean dirty = false; + while (pos < end) { + byte b = src[pos]; + Object o = this.Match_bgn_w_byte(b, src, pos, end); + if (o == null) { + if (dirty) + tmp_bfr.Add_byte(b); + pos++; + } + else { + if (!dirty) { + tmp_bfr.Add_mid(src, bgn, pos); + dirty = true; + } + tmp_bfr.Add((byte[])o); + pos = match_pos; + } + } + return dirty ? tmp_bfr.To_bry_and_clear() : src; + } + public static Btrie_fast_mgr cs() {return new Btrie_fast_mgr(Bool_.N);} + public static Btrie_fast_mgr ci_a7() {return new Btrie_fast_mgr(Bool_.Y);} + public static Btrie_fast_mgr new_(boolean case_any) {return new Btrie_fast_mgr(case_any);} + Btrie_fast_mgr(boolean case_any) { + root = new ByteTrieItm_fast(Byte_.Zero, null, case_any); + } +} +class ByteTrieItm_fast { + private ByteTrieItm_fast[] ary = new ByteTrieItm_fast[256]; + public byte Key_byte() {return key_byte;} private byte key_byte; + public Object Val() {return val;} public void Val_set(Object val) {this.val = val;} Object val; + public boolean Ary_is_empty() {return ary_is_empty;} private boolean ary_is_empty; + public boolean CaseAny() {return case_any;} public ByteTrieItm_fast CaseAny_(boolean v) {case_any = v; return this;} private boolean case_any; + public void Clear() { + val = null; + for (int i = 0; i < 256; i++) { + if (ary[i] != null) { + ary[i].Clear(); + ary[i] = null; + } + } + ary_len = 0; + ary_is_empty = true; + } + public ByteTrieItm_fast Ary_find(byte b) { + int key_byte = (case_any && (b > 64 && b < 91) ? b + 32 : b) & 0xff;// PATCH.JAVA:need to convert to unsigned byte + return ary[key_byte]; + } + public ByteTrieItm_fast Ary_add(byte b, Object val) { + int key_byte = (case_any && (b > 64 && b < 91) ? b + 32 : b) & 0xff;// PATCH.JAVA:need to convert to unsigned byte + ByteTrieItm_fast rv = new ByteTrieItm_fast(b, val, case_any); + ary[key_byte] = rv; + ++ary_len; + ary_is_empty = false; + return rv; + } + public void Ary_del(byte b) { + int key_byte = (case_any && (b > 64 && b < 91) ? b + 32 : b) & 0xff;// PATCH.JAVA:need to convert to unsigned byte + ary[key_byte] = null; + --ary_len; + ary_is_empty = ary_len == 0; + } int ary_len = 0; + public ByteTrieItm_fast(byte key_byte, Object val, boolean case_any) {this.key_byte = key_byte; this.val = val; this.case_any = case_any;} +} diff --git a/100_core/src/gplx/core/btries/Btrie_fast_mgr_tst.java b/100_core/src/gplx/core/btries/Btrie_fast_mgr_tst.java new file mode 100644 index 000000000..a39857240 --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_fast_mgr_tst.java @@ -0,0 +1,85 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Btrie_fast_mgr_tst { + private Btrie_fast_mgr_fxt fxt = new Btrie_fast_mgr_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Get_by() { + fxt.Test_matchAtCur("a" , 1); + fxt.Test_matchAtCur("abc" , 123); + fxt.Test_matchAtCur("ab" , 1); + fxt.Test_matchAtCur("abcde" , 123); + fxt.Test_matchAtCur(" a" , null); + } + @Test public void Bos() { + fxt.Test_match("bc", Byte_ascii.Ltr_a, -1, 123); + } + @Test public void Match_exact() { + fxt.Test_matchAtCurExact("a", 1); + fxt.Test_matchAtCurExact("ab", null); + fxt.Test_matchAtCurExact("abc", 123); + } + @Test public void Del_noop__no_match() { + fxt.Exec_del("d"); + fxt.Test_matchAtCurExact("a" , 1); + fxt.Test_matchAtCurExact("abc" , 123); + } + @Test public void Del_noop__partial_match() { + fxt.Exec_del("ab"); + fxt.Test_matchAtCurExact("a" , 1); + fxt.Test_matchAtCurExact("abc" , 123); + } + @Test public void Del_match__long() { + fxt.Exec_del("abc"); + fxt.Test_matchAtCurExact("a" , 1); + fxt.Test_matchAtCurExact("abc" , null); + } + @Test public void Del_match__short() { + fxt.Exec_del("a"); + fxt.Test_matchAtCurExact("a" , null); + fxt.Test_matchAtCurExact("abc" , 123); + } +} +class Btrie_fast_mgr_fxt { + private Btrie_fast_mgr trie; + public void Clear() { + trie = Btrie_fast_mgr.cs(); + Init_add( 1 , Byte_ascii.Ltr_a); + Init_add(123 , Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c); + } + public void Init_add(int val, byte... ary) {trie.Add(ary, val);} + public void Test_match(String src_str, byte b, int bgn_pos, int expd) { + byte[] src = Bry_.new_a7(src_str); + Object actl = trie.Match_bgn_w_byte(b, src, bgn_pos, src.length); + Tfds.Eq(expd, actl); + } + public void Test_matchAtCur(String src_str, Object expd) { + byte[] src = Bry_.new_a7(src_str); + Object actl = trie.Match_bgn(src, 0, src.length); + Tfds.Eq(expd, actl); + } + public void Test_matchAtCurExact(String src_str, Object expd) { + byte[] src = Bry_.new_a7(src_str); + Object actl = trie.Match_exact(src, 0, src.length); + Tfds.Eq(expd, actl); + } + public void Exec_del(String src_str) { + trie.Del(Bry_.new_u8(src_str)); + } +} diff --git a/100_core/src/gplx/core/btries/Btrie_itm_stub.java b/100_core/src/gplx/core/btries/Btrie_itm_stub.java new file mode 100644 index 000000000..54acfbda0 --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_itm_stub.java @@ -0,0 +1,23 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +public class Btrie_itm_stub { + public Btrie_itm_stub(byte tid, byte[] val) {this.tid = tid; this.val = val;} + public byte Tid() {return tid;} private byte tid; + public byte[] Val() {return val;} private byte[] val; +} diff --git a/100_core/src/gplx/core/btries/Btrie_mgr.java b/100_core/src/gplx/core/btries/Btrie_mgr.java new file mode 100644 index 000000000..f05d435ed --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_mgr.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +public interface Btrie_mgr { + int Match_pos(); + Object Match_at(Btrie_rv rv, byte[] src, int bgn_pos, int end_pos); + Object Match_bgn(byte[] src, int bgn_pos, int end_pos); + Btrie_mgr Add_obj(String key, Object val); + Btrie_mgr Add_obj(byte[] key, Object val); +} diff --git a/100_core/src/gplx/core/btries/Btrie_rv.java b/100_core/src/gplx/core/btries/Btrie_rv.java new file mode 100644 index 000000000..f92eb01cd --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_rv.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import gplx.core.threads.poolables.*; +public class Btrie_rv { + public Object Obj() {return obj;} private Object obj; + public int Pos() {return pos;} private int pos; + public Btrie_rv Init(int pos, Object obj) { + this.obj = obj; + this.pos = pos; + return this; + } +} diff --git a/100_core/src/gplx/core/btries/Btrie_slim_itm.java b/100_core/src/gplx/core/btries/Btrie_slim_itm.java new file mode 100644 index 000000000..61b487fbc --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_slim_itm.java @@ -0,0 +1,132 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +public class Btrie_slim_itm { + private Btrie_slim_itm[] ary = Btrie_slim_itm.Ary_empty; + public Btrie_slim_itm(byte key_byte, Object val, boolean case_any) {this.key_byte = key_byte; this.val = val; this.case_any = case_any;} + public byte Key_byte() {return key_byte;} private byte key_byte; + public Object Val() {return val;} public void Val_set(Object val) {this.val = val;} private Object val; + public boolean Case_any() {return case_any;} private boolean case_any; + public boolean Ary_is_empty() {return ary == Btrie_slim_itm.Ary_empty;} + public void Clear() { + val = null; + for (int i = 0; i < ary_len; i++) + ary[i].Clear(); + ary = Btrie_slim_itm.Ary_empty; + ary_len = ary_max = 0; + } + public Btrie_slim_itm Ary_find(byte b) { + int find_val = (case_any && (b > 64 && b < 91) ? b + 32 : b) & 0xff;// PATCH.JAVA:need to convert to unsigned byte + int key_val = 0; + switch (ary_len) { + case 0: return null; + case 1: + Btrie_slim_itm rv = ary[0]; + key_val = rv.Key_byte() & 0xff;// PATCH.JAVA:need to convert to unsigned byte; + key_val = (case_any && (key_val > 64 && key_val < 91) ? key_val + 32 : key_val); + return key_val == find_val ? rv : null; + default: + int adj = 1; + int prv_pos = 0; + int prv_len = ary_len; + int cur_len = 0; + int cur_idx = 0; + Btrie_slim_itm itm = null; + while (true) { + cur_len = prv_len / 2; + if (prv_len % 2 == 1) ++cur_len; + cur_idx = prv_pos + (cur_len * adj); + if (cur_idx < 0) cur_idx = 0; + else if (cur_idx >= ary_len) cur_idx = ary_len - 1; + itm = ary[cur_idx]; + key_val = itm.Key_byte() & 0xff; // PATCH.JAVA:need to convert to unsigned byte; + key_val = (case_any && (key_val > 64 && key_val < 91) ? key_val + 32 : key_val); + if (find_val < key_val) adj = -1; + else if (find_val > key_val) adj = 1; + else /*(find_val == cur_val)*/ return itm; + if (cur_len == 1) { + cur_idx += adj; + if (cur_idx < 0 || cur_idx >= ary_len) return null; + itm = ary[cur_idx]; + return (itm.Key_byte() & 0xff) == find_val ? itm : null; // PATCH.JAVA:need to convert to unsigned byte; + } + prv_len = cur_len; + prv_pos = cur_idx; + } + } + } + public Btrie_slim_itm Ary_add(byte b, Object val) { + int new_len = ary_len + 1; + if (new_len > ary_max) { + ary_max += 4; + ary = (Btrie_slim_itm[])Array_.Resize(ary, ary_max); + } + Btrie_slim_itm rv = new Btrie_slim_itm(b, val, case_any); + ary[ary_len] = rv; + ary_len = new_len; + synchronized (ByteHashItm_sorter.Instance) {// TS: DATE:2016-07-06 + ByteHashItm_sorter.Instance.Sort(ary, ary_len); + } + return rv; + } + public void Ary_del(byte b) { + boolean found = false; + for (int i = 0; i < ary_len; i++) { + if (found) { + if (i < ary_len - 1) + ary[i] = ary[i + 1]; + } + else { + if (b == ary[i].Key_byte()) found = true; + } + } + if (found) --ary_len; + } + public static final Btrie_slim_itm[] Ary_empty = new Btrie_slim_itm[0]; int ary_len = 0, ary_max = 0; +} +class ByteHashItm_sorter {// quicksort + Btrie_slim_itm[] ary; int ary_len; + public void Sort(Btrie_slim_itm[] ary, int ary_len) { + if (ary == null || ary_len < 2) return; + this.ary = ary; + this.ary_len = ary_len; + Sort_recurse(0, ary_len - 1); + } + private void Sort_recurse(int lo, int hi) { + int i = lo, j = hi; + int mid = ary[lo + (hi-lo)/2].Key_byte()& 0xFF; // get mid itm + while (i <= j) { // divide into two lists + while ((ary[i].Key_byte() & 0xFF) < mid) // if lhs.cur < mid, then get next from lhs + i++; + while ((ary[j].Key_byte() & 0xFF) > mid) // if rhs.cur > mid, then get next from rhs + j--; + + // lhs.cur > mid && rhs.cur < mid; switch lhs.cur and rhs.cur; increase i and j + if (i <= j) { + Btrie_slim_itm tmp = ary[i]; + ary[i] = ary[j]; + ary[j] = tmp; + i++; + j--; + } + } + if (lo < j) Sort_recurse(lo, j); + if (i < hi) Sort_recurse(i, hi); + } + public static final ByteHashItm_sorter Instance = new ByteHashItm_sorter(); ByteHashItm_sorter() {} +} diff --git a/100_core/src/gplx/core/btries/Btrie_slim_itm_tst.java b/100_core/src/gplx/core/btries/Btrie_slim_itm_tst.java new file mode 100644 index 000000000..26770864a --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_slim_itm_tst.java @@ -0,0 +1,49 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Btrie_slim_itm_tst { + private Btrie_slim_itm itm = new Btrie_slim_itm(Byte_.Zero, null, false); + @Before public void init() {itm.Clear();} + @Test public void Find_nil() { + tst_Find(Byte_ascii.Ltr_a, null); + } + @Test public void Add_one() { + run_Add(Byte_ascii.Ltr_a); + tst_Find(Byte_ascii.Ltr_a, "a"); + } + @Test public void Add_many() { + run_Add(Byte_ascii.Bang, Byte_ascii.Num_0, Byte_ascii.Ltr_a, Byte_ascii.Ltr_B); + tst_Find(Byte_ascii.Ltr_a, "a"); + } + @Test public void Del() { + run_Add(Byte_ascii.Bang, Byte_ascii.Num_0, Byte_ascii.Ltr_a, Byte_ascii.Ltr_B); + tst_Find(Byte_ascii.Ltr_a, "a"); + run_Del(Byte_ascii.Ltr_a); + tst_Find(Byte_ascii.Ltr_a, null); + tst_Find(Byte_ascii.Num_0, "0"); + tst_Find(Byte_ascii.Ltr_B, "B"); + } + private void tst_Find(byte b, String expd) { + Btrie_slim_itm actl_itm = itm.Ary_find(b); + Object actl = actl_itm == null ? null : actl_itm.Val(); + Tfds.Eq(expd, actl); + } + private void run_Add(byte... ary) {for (byte b : ary) itm.Ary_add(b, Char_.To_str((char)b));} + private void run_Del(byte... ary) {for (byte b : ary) itm.Ary_del(b);} +} diff --git a/100_core/src/gplx/core/btries/Btrie_slim_mgr.java b/100_core/src/gplx/core/btries/Btrie_slim_mgr.java new file mode 100644 index 000000000..f6a57241f --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_slim_mgr.java @@ -0,0 +1,206 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import gplx.core.primitives.*; import gplx.core.threads.poolables.*; +public class Btrie_slim_mgr implements Btrie_mgr { + Btrie_slim_mgr(boolean case_match) {root = new Btrie_slim_itm(Byte_.Zero, null, !case_match);} private Btrie_slim_itm root; + public int Count() {return count;} private int count; + public int Match_pos() {return match_pos;} private int match_pos; + + public Object Match_at(Btrie_rv rv, byte[] src, int bgn_pos, int end_pos) {return bgn_pos < end_pos ? Match_at_w_b0(rv, src[bgn_pos], src, bgn_pos, end_pos) : null;} // handle out of bounds gracefully; EX: Match_bgn("abc", 3, 3) should return null not fail + public Object Match_at_w_b0(Btrie_rv rv, byte b, byte[] src, int bgn_pos, int src_end) { + Object rv_obj = null; + int rv_pos = bgn_pos; + int cur_pos = bgn_pos; + Btrie_slim_itm cur = root; + while (true) { + Btrie_slim_itm nxt = cur.Ary_find(b); + if (nxt == null) { + rv.Init(rv_pos, rv_obj); // nxt does not have b; return rv_obj; + return rv_obj; + } + ++cur_pos; + if (nxt.Ary_is_empty()) { + rv_obj = nxt.Val(); + rv.Init(cur_pos, rv_obj); // nxt is leaf; return nxt.Val() (which should be non-null) + return rv_obj; + } + Object nxt_val = nxt.Val(); + if (nxt_val != null) {rv_pos = cur_pos; rv_obj = nxt_val;} // nxt is node; cache rv_obj (in case of false match) + if (cur_pos == src_end) { + rv.Init(rv_pos, rv_obj); // increment cur_pos and exit if src_end + return rv_obj; + } + b = src[cur_pos]; + cur = nxt; + } + } + public Object Match_exact(byte[] src) {return src == null ? null : Match_exact(src, 0, src.length);} + public Object Match_exact(byte[] src, int bgn_pos, int end_pos) { + if (bgn_pos == end_pos) return null; // NOTE:handle empty String; DATE:2016-04-21 + Object rv = Match_bgn_w_byte(src[bgn_pos], src, bgn_pos, end_pos); + return rv == null ? null : match_pos - bgn_pos == end_pos - bgn_pos ? rv : null; + } + public Object Match_bgn(byte[] src, int bgn_pos, int end_pos) {return bgn_pos < end_pos ? Match_bgn_w_byte(src[bgn_pos], src, bgn_pos, end_pos) : null;} // handle out of bounds gracefully; EX: Match_bgn("abc", 3, 3) should return null not fail + public Object Match_bgn_w_byte(byte b, byte[] src, int bgn_pos, int src_end) { + Object rv = null; int cur_pos = match_pos = bgn_pos; + Btrie_slim_itm cur = root; + while (true) { + Btrie_slim_itm nxt = cur.Ary_find(b); if (nxt == null) return rv; // nxt does not hav b; return rv; + ++cur_pos; + if (nxt.Ary_is_empty()) {match_pos = cur_pos; return nxt.Val();} // nxt is leaf; return nxt.Val() (which should be non-null) + Object nxt_val = nxt.Val(); + if (nxt_val != null) {match_pos = cur_pos; rv = nxt_val;} // nxt is node; cache rv (in case of false match) + if (cur_pos == src_end) return rv; // increment cur_pos and exit if src_end + b = src[cur_pos]; + cur = nxt; + } + } + public byte Match_byte_or(byte b, byte[] src, int bgn, int end, byte or) { + Object rv_obj = Match_bgn_w_byte(b, src, bgn, end); + return rv_obj == null ? or : ((Byte_obj_val)rv_obj).Val(); + } + public byte Match_byte_or(byte[] src, int bgn, int end, byte or) { + Object rv_obj = Match_bgn(src, bgn, end); + return rv_obj == null ? or : ((Byte_obj_val)rv_obj).Val(); + } + public byte Match_byte_or(Btrie_rv trv, byte b, byte[] src, int bgn, int end, byte or) { + Object rv_obj = Match_at_w_b0(trv, b, src, bgn, end); + return rv_obj == null ? or : ((Byte_obj_val)rv_obj).Val(); + } + public byte Match_byte_or(Btrie_rv trv, byte[] src, int bgn, int end, byte or) { + Object rv_obj = Match_at(trv, src, bgn, end); + return rv_obj == null ? or : ((Byte_obj_val)rv_obj).Val(); + } + public byte Match_byte_or(Btrie_rv trv, byte[] src, byte or) { + Object rv_obj = Match_at(trv, src, 0, src.length); + return rv_obj == null ? or : ((Byte_obj_val)rv_obj).Val(); + } + public Btrie_slim_mgr Add_bry_tid(byte[] bry, byte tid) {return (Btrie_slim_mgr)Add_obj(bry, Byte_obj_val.new_(tid));} + public Btrie_slim_mgr Add_bry_int(byte[] key, int val) {return (Btrie_slim_mgr)Add_obj(key, new Int_obj_val(val));} + public Btrie_slim_mgr Add_str_byte(String key, byte val) {return (Btrie_slim_mgr)Add_obj(Bry_.new_u8(key), Byte_obj_val.new_(val));} + public Btrie_slim_mgr Add_str_int(String key, int val) {return (Btrie_slim_mgr)Add_obj(Bry_.new_u8(key), new Int_obj_val(val));} + public Btrie_slim_mgr Add_bry(String key, String val) {return (Btrie_slim_mgr)Add_obj(Bry_.new_u8(key), Bry_.new_u8(val));} + public Btrie_slim_mgr Add_bry(String key, byte[] val) {return (Btrie_slim_mgr)Add_obj(Bry_.new_u8(key), val);} + public Btrie_slim_mgr Add_bry(byte[] v) {return (Btrie_slim_mgr)Add_obj(v, v);} + public Btrie_slim_mgr Add_str_str(String key, String val) {return (Btrie_slim_mgr)Add_obj(Bry_.new_u8(key), Bry_.new_u8(val));} + public Btrie_slim_mgr Add_bry_bry(byte[] key, byte[] val) {return (Btrie_slim_mgr)Add_obj(key, val);} + public Btrie_slim_mgr Add_bry_byte(byte b, byte val) {return (Btrie_slim_mgr)Add_obj(new byte[] {b}, Byte_obj_val.new_(val));} + public Btrie_slim_mgr Add_bry_byte(byte[] bry, byte val) {return (Btrie_slim_mgr)Add_obj(bry, Byte_obj_val.new_(val));} + public Btrie_slim_mgr Add_str_byte__many(byte val, String... ary) { + int ary_len = ary.length; + Byte_obj_val bval = Byte_obj_val.new_(val); + for (int i = 0; i < ary_len; i++) + Add_obj(Bry_.new_u8(ary[i]), bval); + return this; + } + public Btrie_slim_mgr Add_many_int(int val, String... ary) {return Add_many_int(val, Bry_.Ary(ary));} + public Btrie_slim_mgr Add_many_int(int val, byte[]... ary) { + int len = ary.length; + Int_obj_val obj = new Int_obj_val(val); + for (int i = 0; i < len; i++) + Add_obj(ary[i], obj); + return this; + } + public Btrie_slim_mgr Add_ary_byte(byte... ary) { + int len = ary.length; + for (int i = 0; i < len; ++i) { + byte b = ary[i]; + Byte_obj_val bval = Byte_obj_val.new_(b); + Add_obj(Bry_.New_by_byte(b), bval); + } + return this; + } + public Btrie_slim_mgr Add_replace_many(String trg_str, String... src_ary) {return Add_replace_many(Bry_.new_u8(trg_str), src_ary);} + public Btrie_slim_mgr Add_replace_many(byte[] trg_bry, String... src_ary) { + int len = src_ary.length; + for (int i = 0; i < len; i++) + Add_obj(Bry_.new_u8(src_ary[i]), trg_bry); + return this; + } + public Btrie_slim_mgr Add_stub(String key, byte val) {byte[] bry = Bry_.new_u8(key); return (Btrie_slim_mgr)Add_obj(bry, new Btrie_itm_stub(val, bry));} + public Btrie_slim_mgr Add_stubs(byte[][] ary) {return Add_stubs(ary, ary.length);} + public Btrie_slim_mgr Add_stubs(byte[][] ary, int ary_len) { + for (byte i = 0; i < ary_len; i++) { + byte[] bry = ary[i]; + Add_obj(bry, new Btrie_itm_stub(i, bry)); + } + return this; + } + public Btrie_mgr Add_obj(String key, Object val) {return Add_obj(Bry_.new_u8(key), val);} + public Btrie_mgr Add_obj(byte[] key, Object val) { + if (val == null) throw Err_.new_wo_type("null objects cannot be registered", "key", String_.new_u8(key)); + int key_len = key.length; int key_end = key_len - 1; + Btrie_slim_itm cur = root; + for (int i = 0; i < key_len; i++) { + byte b = key[i]; + if (root.Case_any() && (b > 64 && b < 91)) b += 32; + Btrie_slim_itm nxt = cur.Ary_find(b); + if (nxt == null) + nxt = cur.Ary_add(b, null); + if (i == key_end) + nxt.Val_set(val); + cur = nxt; + } + count++; // FUTURE: do not increment if replacing value + return this; + } + public void Del(byte[] key) { + int key_len = key.length; + Btrie_slim_itm cur = root; + for (int i = 0; i < key_len; i++) { + byte b = key[i]; + Btrie_slim_itm nxt = cur.Ary_find(b); + if (nxt == null) break; + Object nxt_val = nxt.Val(); + if (nxt_val == null) // cur is end of chain; remove entry; EX: Abc and at c + cur.Ary_del(b); + else // cur is mid of chain; null out entry + nxt.Val_set(null); + cur = nxt; + } + count--; // FUTURE: do not decrement if not found + } + public byte[] Replace(Bry_bfr tmp_bfr, byte[] src, int bgn, int end) { + int pos = bgn; + boolean dirty = false; + while (pos < end) { + byte b = src[pos]; + Object o = this.Match_bgn_w_byte(b, src, pos, end); + if (o == null) { + if (dirty) + tmp_bfr.Add_byte(b); + pos++; + } + else { + if (!dirty) { + tmp_bfr.Add_mid(src, bgn, pos); + dirty = true; + } + tmp_bfr.Add((byte[])o); + pos = match_pos; + } + } + return dirty ? tmp_bfr.To_bry_and_clear() : src; + } + public void Clear() {root.Clear(); count = 0;} + public static Btrie_slim_mgr cs() {return new Btrie_slim_mgr(Bool_.Y);} + public static Btrie_slim_mgr ci_a7() {return new Btrie_slim_mgr(Bool_.N);} + public static Btrie_slim_mgr ci_u8() {return new Btrie_slim_mgr(Bool_.N);} + public static Btrie_slim_mgr new_(boolean v) {return new Btrie_slim_mgr(v);} +} diff --git a/100_core/src/gplx/core/btries/Btrie_slim_mgr_tst.java b/100_core/src/gplx/core/btries/Btrie_slim_mgr_tst.java new file mode 100644 index 000000000..9ef1d08e4 --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_slim_mgr_tst.java @@ -0,0 +1,92 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Btrie_slim_mgr_tst { + @Before public void init() { + } private Btrie_slim_mgr trie; + private void ini_setup1() { + trie = Btrie_slim_mgr.cs(); + run_Add("a" , 1); + run_Add("abc" , 123); + } + @Test public void Get_by() { + ini_setup1(); + tst_MatchAtCur("a" , 1); + tst_MatchAtCur("abc" , 123); + tst_MatchAtCur("ab" , 1); + tst_MatchAtCur("abcde" , 123); + tst_MatchAtCur(" a" , null); + } + @Test public void Bos() { + ini_setup1(); + tst_Match("bc", Byte_ascii.Ltr_a, -1, 123); + } + @Test public void Match_exact() { + ini_setup1(); + tst_MatchAtCurExact("a", 1); + tst_MatchAtCurExact("ab", null); + tst_MatchAtCurExact("abc", 123); + } + private void ini_setup2() { + trie = Btrie_slim_mgr.cs(); + run_Add("a" , 1); + run_Add("b" , 2); + } + @Test public void Match_2() { + ini_setup2(); + tst_MatchAtCur("a", 1); + tst_MatchAtCur("b", 2); + } + private void ini_setup_caseAny() { + trie = Btrie_slim_mgr.ci_a7(); // NOTE:ci.ascii:test + run_Add("a" , 1); + run_Add("b" , 2); + } + @Test public void CaseAny() { + ini_setup_caseAny(); + tst_MatchAtCur("a", 1); + tst_MatchAtCur("A", 1); + } + @Test public void Del() { + ini_setup1(); + trie.Del(Bry_.new_a7("a")); // delete "a"; "abc" still remains; + tst_MatchAtCur("a" , null); + tst_MatchAtCur("abc" , 123); + + trie.Del(Bry_.new_a7("abc")); + tst_MatchAtCur("abc" , null); + } + + private void run_Add(String k, int val) {trie.Add_obj(Bry_.new_a7(k), val);} + private void tst_Match(String srcStr, byte b, int bgn_pos, int expd) { + byte[] src = Bry_.new_a7(srcStr); + Object actl = trie.Match_bgn_w_byte(b, src, bgn_pos, src.length); + Tfds.Eq(expd, actl); + } + private void tst_MatchAtCur(String srcStr, Object expd) { + byte[] src = Bry_.new_a7(srcStr); + Object actl = trie.Match_bgn_w_byte(src[0], src, 0, src.length); + Tfds.Eq(expd, actl); + } + private void tst_MatchAtCurExact(String srcStr, Object expd) { + byte[] src = Bry_.new_a7(srcStr); + Object actl = trie.Match_exact(src, 0, src.length); + Tfds.Eq(expd, actl); + } +} diff --git a/100_core/src/gplx/core/btries/Btrie_u8_itm.java b/100_core/src/gplx/core/btries/Btrie_u8_itm.java new file mode 100644 index 000000000..e7880d780 --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_u8_itm.java @@ -0,0 +1,68 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import gplx.core.intls.*; +class Btrie_u8_itm { + private Hash_adp_bry nxts; + private byte[] asymmetric_bry; + public Btrie_u8_itm(byte[] key, Object val) {this.key = key; this.val = val;} + public byte[] Key() {return key;} private byte[] key; + public Object Val() {return val;} public void Val_set(Object val) {this.val = val;} private Object val; + public boolean Nxts_is_empty() {return nxts == null;} + public void Clear() { + val = null; + nxts.Clear(); + nxts = null; + } + public Btrie_u8_itm Nxts_find(byte[] src, int c_bgn, int c_end, boolean called_by_match) { + if (nxts == null) return null; + Object rv_obj = nxts.Get_by_mid(src, c_bgn, c_end); + if (rv_obj == null) return null; + Btrie_u8_itm rv = (Btrie_u8_itm)rv_obj; + byte[] asymmetric_bry = rv.asymmetric_bry; + if (asymmetric_bry == null) // itm doesn't have asymmetric_bry; note that this is the case for most items + return rv; + else { // itm has asymmetric_bry; EX: "Ä°" was added to trie, must match "Ä°" and "i"; + if (called_by_match) { // called by mgr.Match + return + ( Bry_.Eq(src, c_bgn, c_end, rv.key) // key matches src; EX: "aÄ°" + || Bry_.Eq(src, c_bgn, c_end, rv.asymmetric_bry) // asymmetric_bry matches src; EX: "ai"; note that "aI" won't match + ) + ? rv : null; + } + else { // called by mgr.Add; this means that an asymmetric_itm was already added; happens when "Ä°" added first and then "I" added next + rv.asymmetric_bry = null; // always null out asymmetric_bry; note that this noops non-asymmetric itms, while making an asymmetric_itm case-insenstivie (matches Ä°,i,I); see tests + return rv; + } + } + } + public Btrie_u8_itm Nxts_add(Gfo_case_mgr case_mgr, byte[] key, Object val) { + Btrie_u8_itm rv = new Btrie_u8_itm(key, val); + if (nxts == null) nxts = Hash_adp_bry.ci_u8(case_mgr); + nxts.Add_bry_obj(key, rv); + Gfo_case_itm case_itm = case_mgr.Get_or_null(key[0], key, 0, key.length); // get case_item + if (case_itm != null) { // note that case_itm may be null; EX: "__TOC__" and "_" + byte[] asymmetric_bry = case_itm.Asymmetric_bry(); + if (asymmetric_bry != null) { // case_itm has asymmetry_bry; only itms in Xol_case_itm_ that are created with Tid_upper and Tid_lower will be non-null + rv.asymmetric_bry = asymmetric_bry; // set itm to asymmetric_bry; EX: for Ä°, asymmetric_bry = i +// nxts.Add_bry_obj(asymmetric_bry, rv); // add the asymmetric_bry to the hash; in above example, this allows "i" to match "Ä°" + } + } + return rv; + } +} diff --git a/100_core/src/gplx/core/btries/Btrie_u8_mgr.java b/100_core/src/gplx/core/btries/Btrie_u8_mgr.java new file mode 100644 index 000000000..7a7d6243c --- /dev/null +++ b/100_core/src/gplx/core/btries/Btrie_u8_mgr.java @@ -0,0 +1,99 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.btries; import gplx.*; import gplx.core.*; +import gplx.core.intls.*; +public class Btrie_u8_mgr implements Btrie_mgr { + private Btrie_u8_itm root; private Gfo_case_mgr case_mgr; + Btrie_u8_mgr(Gfo_case_mgr case_mgr) { + this.case_mgr = case_mgr; + this.root = new Btrie_u8_itm(Bry_.Empty, null); + } + public int Count() {return count;} private int count; + public Object Match_at(Btrie_rv rv, byte[] src, int bgn_pos, int end_pos) {return Match_at_w_b0(rv, src[bgn_pos], src, bgn_pos, end_pos);} + public Object Match_at_w_b0(Btrie_rv rv, byte b, byte[] src, int bgn_pos, int end_pos) { + Object rv_obj = null; + int rv_pos = bgn_pos; + int cur_pos = bgn_pos; + Btrie_u8_itm cur = root; + while (true) { + int c_len = Utf8_.Len_of_char_by_1st_byte(b); + int c_end = cur_pos + c_len; + Btrie_u8_itm nxt = cur.Nxts_find(src, cur_pos, c_end, true); + if (nxt == null) { + rv.Init(rv_pos, rv_obj); // nxts does not have key; return rv_obj; + return rv_obj; + } + cur_pos = c_end; + if (nxt.Nxts_is_empty()) { // nxt is leaf; return nxt.Val() (which should be non-null) + rv_obj = nxt.Val(); + rv.Init(cur_pos, rv_obj); + return rv_obj; + } + Object nxt_val = nxt.Val(); + if (nxt_val != null) {rv_pos = cur_pos; rv_obj = nxt_val;} // nxt is node; cache rv_obj (in case of false match) + if (cur_pos == end_pos) { // increment cur_pos and exit if end + rv.Init(rv_pos, rv_obj); + return rv_obj; + } + b = src[cur_pos]; + cur = nxt; + } + } + + public int Match_pos() {return match_pos;} private int match_pos; + public Object Match_bgn(byte[] src, int bgn_pos, int end_pos) {return Match_bgn_w_byte(src[bgn_pos], src, bgn_pos, end_pos);} + public Object Match_bgn_w_byte(byte b, byte[] src, int bgn_pos, int end_pos) { + Object rv = null; int cur_pos = match_pos = bgn_pos; + Btrie_u8_itm cur = root; + while (true) { + int c_len = Utf8_.Len_of_char_by_1st_byte(b); + int c_end = cur_pos + c_len; + Btrie_u8_itm nxt = cur.Nxts_find(src, cur_pos, c_end, true); if (nxt == null) return rv; // nxts does not have key; return rv; + cur_pos = c_end; + if (nxt.Nxts_is_empty()) {match_pos = cur_pos; return nxt.Val();} // nxt is leaf; return nxt.Val() (which should be non-null) + Object nxt_val = nxt.Val(); + if (nxt_val != null) {match_pos = cur_pos; rv = nxt_val;} // nxt is node; cache rv (in case of false match) + if (cur_pos == end_pos) return rv; // increment cur_pos and exit if end + b = src[cur_pos]; + cur = nxt; + } + } + public void Clear() {root.Clear(); count = 0;} + public Btrie_mgr Add_obj(String key, Object val) {return Add_obj(Bry_.new_u8(key), val);} + public Btrie_mgr Add_obj(byte[] key, Object val) { + if (val == null) throw Err_.new_wo_type("null objects cannot be registered", "key", String_.new_u8(key)); + int key_len = key.length; + Btrie_u8_itm cur = root; + int c_bgn = 0; + while (c_bgn < key_len) { + byte c = key[c_bgn]; + int c_len = Utf8_.Len_of_char_by_1st_byte(c); + int c_end = c_bgn + c_len; + Btrie_u8_itm nxt = cur.Nxts_find(key, c_bgn, c_end, false); + if (nxt == null) + nxt = cur.Nxts_add(case_mgr, Bry_.Mid(key, c_bgn, c_end), null); + c_bgn = c_end; + if (c_bgn == key_len) + nxt.Val_set(val); + cur = nxt; + } + ++count; + return this; + } + public static Btrie_u8_mgr new_(Gfo_case_mgr case_mgr) {return new Btrie_u8_mgr(case_mgr);} +} diff --git a/100_core/src/gplx/core/consoles/Console_adp.java b/100_core/src/gplx/core/consoles/Console_adp.java new file mode 100644 index 000000000..3fc260176 --- /dev/null +++ b/100_core/src/gplx/core/consoles/Console_adp.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.consoles; import gplx.*; import gplx.core.*; +public interface Console_adp { + boolean Enabled(); // optimization; allows Write to be skipped (since Write may Concat strings or generate arrays) + boolean Canceled_chk(); + int Chars_per_line_max(); void Chars_per_line_max_(int v); + void Write_str(String s); + void Write_fmt_w_nl(String fmt, Object... args); + void Write_tmp(String s); + char Read_key(String msg); + String Read_line(String msg); +} diff --git a/100_core/src/gplx/core/consoles/Console_adp_.java b/100_core/src/gplx/core/consoles/Console_adp_.java new file mode 100644 index 000000000..7fcae89cd --- /dev/null +++ b/100_core/src/gplx/core/consoles/Console_adp_.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.consoles; import gplx.*; import gplx.core.*; +public class Console_adp_ { + public static final Console_adp Noop = new Console_adp__noop(); + public static Console_adp__mem Dev() {return new Console_adp__mem();} +} +class Console_adp__noop implements Console_adp { + public boolean Enabled() {return false;} + public boolean Canceled_chk() {return false;} + public int Chars_per_line_max() {return 80;} public void Chars_per_line_max_(int v) {} + public void Write_str(String s) {} + public void Write_fmt_w_nl(String s, Object... args) {} + public void Write_tmp(String s) {} + public char Read_key(String msg) {return '\0';} + public String Read_line(String msg) {return "";} +} diff --git a/100_core/src/gplx/core/consoles/Console_adp__mem.java b/100_core/src/gplx/core/consoles/Console_adp__mem.java new file mode 100644 index 000000000..82343833c --- /dev/null +++ b/100_core/src/gplx/core/consoles/Console_adp__mem.java @@ -0,0 +1,50 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.consoles; import gplx.*; import gplx.core.*; +public class Console_adp__mem implements Console_adp { + private final List_adp written = List_adp_.New(); + private final Hash_adp ignored = Hash_adp_.New(); + public boolean Enabled() {return true;} + public boolean Canceled_chk() {return false;} + public int Chars_per_line_max() {return 80;} public void Chars_per_line_max_(int v) {} + public Console_adp__mem Ignore_add(String s) {ignored.Add_as_key_and_val(s); return this;} + public void Write_str(String s) {WriteString(s);} + public void Write_fmt_w_nl(String s, Object... args) {WriteString(String_.Format(s, args) + String_.CrLf);} + public void Write_tmp(String s) {WriteString(s);} + public String Read_line(String msg) {return "";} + public char Read_key(String msg) {return '\0';} + public Console_adp__mem CancelWhenTextWritten(String val) { + cancelVal = val; + return this; + } + void WriteString(String s) { + if (ignored.Has(s)) return; + written.Add(s); + if (cancelVal != null && String_.Has(s, cancelVal)) throw Err_.new_wo_type("canceled", "cancel_val", s); + } + String cancelVal; + + public List_adp Written() {return written;} + public void tst_WrittenStr(String... expd) { + String[] actl = new String[written.Count()]; + int actlLength = Array_.Len(actl); + for (int i = 0; i < actlLength; i++) + actl[i] = written.Get_at(i).toString(); + Tfds.Eq_ary(actl, expd); + } +} diff --git a/100_core/src/gplx/core/consoles/Console_adp__sys.java b/100_core/src/gplx/core/consoles/Console_adp__sys.java new file mode 100644 index 000000000..58fa69d4e --- /dev/null +++ b/100_core/src/gplx/core/consoles/Console_adp__sys.java @@ -0,0 +1,67 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.consoles; import gplx.*; import gplx.core.*; +import gplx.core.envs.*; +public class Console_adp__sys implements Console_adp { + private String tmp_txt; + public Console_adp__sys() { + this.backspace_by_bytes = Op_sys.Cur().Tid_is_lnx(); // bash shows UTF8 by default; backspace in bytes, else multi-byte characters don't show; DATE:2014-03-04 + } + public boolean Enabled() {return true;} + public boolean Canceled() {return canceled;} public void Canceled_set(boolean v) {canceled = v;} private boolean canceled = false; + public boolean Canceled_chk() {if (canceled) throw Err_.new_op_canceled(); return canceled;} + public int Chars_per_line_max() {return chars_per_line_max;} public void Chars_per_line_max_(int v) {chars_per_line_max = v;} int chars_per_line_max = 80; + public boolean Backspace_by_bytes() {return backspace_by_bytes;} public Console_adp__sys Backspace_by_bytes_(boolean v) {backspace_by_bytes = v; return this;} private boolean backspace_by_bytes; + public void Write_str(String s) {Clear_tmp(); Write_str_lang(s);} + public void Write_str_w_nl(String s) {Clear_tmp(); Write_str_w_nl_lang(s);} + public void Write_fmt_w_nl(String fmt, Object... args) {Clear_tmp(); Write_str_w_nl_lang(String_.Format(fmt, args));} + public char Read_key(String s) {Write_str(s); return Read_key_lang();} + public String Read_line(String s) {Write_str(s); return Read_line_lang();} + public void Write_tmp(String s) { + Clear_tmp(); + if (String_.Has(s, "\r")) s = String_.Replace(s, "\r", " "); + if (String_.Has(s, "\n")) s = String_.Replace(s, "\n", " "); + if (String_.Len(s) >= chars_per_line_max) s = String_.Mid(s, 0, chars_per_line_max - String_.Len("...") - 1) + "..."; // NOTE: >= and -1 needed b/c line needs to be 1 less than max; ex: default cmd is 80 width, but writing 80 chars will automatically create lineBreak + tmp_txt = s; + Write_str_lang(s); + } + private void Clear_tmp() { + if (tmp_txt == null) return; + if (Env_.Mode_debug()) {Write_str_lang(String_.CrLf); return;} + int count = backspace_by_bytes ? Bry_.new_u8(tmp_txt).length : String_.Len(tmp_txt); + String moveBack = String_.Repeat("\b", count); + this.Write_str_lang(moveBack); // move cursor back to beginning of line + this.Write_str_lang(String_.Repeat(" ", count)); // overwrite tmp_txt with space + this.Write_str_lang(moveBack); // move cursor back to beginning of line (so next Write will start at beginning) + tmp_txt = null; + } + private void Write_str_lang(String s) {System.out.print(s);} + private void Write_str_w_nl_lang(String s) {System.out.println(s);} + private String Read_line_lang() {return System.console() == null ? "" : System.console().readLine();} + private char Read_key_lang() { + String text = Read_line_lang(); + return String_.Len(text) == 0 ? '\0' : String_.CharAt(text, 0); + } + public void Write_str_w_nl_utf8(String s) { + java.io.PrintStream ps; + try {ps = new java.io.PrintStream(System.out, true, "UTF-8");} + catch (java.io.UnsupportedEncodingException e) {throw Err_.new_wo_type("unsupported exception");} + ps.println(s); + } + public static final Console_adp__sys Instance = new Console_adp__sys(); +} diff --git a/100_core/src/gplx/core/criterias/Criteria.java b/100_core/src/gplx/core/criterias/Criteria.java new file mode 100644 index 000000000..30e11db16 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public interface Criteria extends To_str_able { + byte Tid(); + boolean Matches(Object obj); + void Val_from_args(Hash_adp args); + void Val_as_obj_(Object obj); +} diff --git a/100_core/src/gplx/core/criterias/Criteria_.java b/100_core/src/gplx/core/criterias/Criteria_.java new file mode 100644 index 000000000..67b44d0b0 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_.java @@ -0,0 +1,53 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +import gplx.core.texts.*; /*RegxPatn_cls_like*/ +public class Criteria_ { + public static final Criteria All = new Criteria_const(true); + public static final Criteria None = new Criteria_const(false); + public static Criteria Not(Criteria arg) {return new Criteria_not(arg);} + public static Criteria And(Criteria lhs, Criteria rhs) {return new Criteria_and(lhs, rhs);} + public static Criteria And_many(Criteria... ary) { + int len = Array_.Len(ary); if (len == 0) throw Err_.new_wo_type("cannot AND 0 criterias;"); + Criteria rv = ary[0]; + for (int i = 1; i < len; i++) + rv = And(rv, ary[i]); + return rv; + } + public static Criteria Or(Criteria lhs, Criteria rhs) {return new Criteria_or(lhs, rhs);} + public static Criteria Or_many(Criteria... ary) { + int len = Array_.Len(ary); if (len == 0) throw Err_.new_wo_type("cannot OR 0 criterias;"); + Criteria rv = ary[0]; + for (int i = 1; i < len; i++) + rv = Or(rv, ary[i]); + return rv; + } + public static Criteria eq_(Object arg) {return new Criteria_eq(false, arg);} + public static Criteria eqn_(Object arg) {return new Criteria_eq(true, arg);} + public static Criteria in_(Object... array) {return new Criteria_in(false, array);} + public static Criteria inn_(Object... array) {return new Criteria_in(true, array);} + public static Criteria lt_(Comparable val) {return new Criteria_comp(CompareAble_.Less, val);} + public static Criteria lte_(Comparable val) {return new Criteria_comp(CompareAble_.LessOrSame, val);} + public static Criteria mt_(Comparable val) {return new Criteria_comp(CompareAble_.More, val);} + public static Criteria mte_(Comparable val) {return new Criteria_comp(CompareAble_.MoreOrSame, val);} + public static Criteria between_(Comparable lhs, Comparable rhs) {return new Criteria_between(false, lhs, rhs);} + public static Criteria between_(boolean negated, Comparable lhs, Comparable rhs) {return new Criteria_between(negated, lhs, rhs);} + public static Criteria like_(String pattern) {return new Criteria_like(false, RegxPatn_cls_like_.parse(pattern, RegxPatn_cls_like.EscapeDefault));} + public static Criteria liken_(String pattern) {return new Criteria_like(true, RegxPatn_cls_like_.parse(pattern, RegxPatn_cls_like.EscapeDefault));} + public static final byte Tid_custom = 0, Tid_const = 1, Tid_not = 2, Tid_and = 3, Tid_or = 4, Tid_eq = 5, Tid_between = 6, Tid_in = 7, Tid_like = 8, Tid_comp = 9, Tid_wrapper = 10, Tid_iomatch = 11, Tid_db_obj_ary = 12; +} diff --git a/100_core/src/gplx/core/criterias/Criteria_between.java b/100_core/src/gplx/core/criterias/Criteria_between.java new file mode 100644 index 000000000..699f56940 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_between.java @@ -0,0 +1,40 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public class Criteria_between implements Criteria { + public Criteria_between(boolean neg, Comparable lo, Comparable hi) {this.neg = neg; this.lo = lo; this.hi = hi;} + public byte Tid() {return Criteria_.Tid_between;} + public boolean Neg() {return neg;} private final boolean neg; + public Comparable Lo() {return lo;} private Comparable lo; public void Lo_(Comparable v) {this.lo = v;} + public Comparable Hi() {return hi;} private Comparable hi; public void Hi_(Comparable v) {this.hi = v;} + public void Val_from_args(Hash_adp args) {throw Err_.new_unimplemented();} + public void Val_as_obj_(Object v) { + Object[] ary = (Object[])v; + lo = (Comparable)ary[0]; + hi = (Comparable)ary[1]; + } + public boolean Matches(Object comp_obj) { + Comparable comp = CompareAble_.as_(comp_obj); + int lo_rslt = CompareAble_.CompareComparables(lo, comp); + int hi_rslt = CompareAble_.CompareComparables(hi, comp); + boolean rv = (lo_rslt * hi_rslt) != 1; + return neg ? !rv : rv; + } + + public String To_str() {return String_.Concat_any("BETWEEN ", lo, " AND ", hi);} +} diff --git a/100_core/src/gplx/core/criterias/Criteria_bool_base.java b/100_core/src/gplx/core/criterias/Criteria_bool_base.java new file mode 100644 index 000000000..132467219 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_bool_base.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public abstract class Criteria_bool_base implements Criteria { + @gplx.Internal protected void Ctor(String op_literal, Criteria lhs, Criteria rhs) {this.op_literal = op_literal; this.lhs = lhs; this.rhs = rhs;} + public abstract byte Tid(); + public abstract boolean Matches(Object curVal); + public void Val_from_args(Hash_adp args) {lhs.Val_from_args(args); rhs.Val_from_args(args);} + public void Val_as_obj_(Object v) {throw Err_.new_unimplemented();} + public String To_str() {return String_.Concat(lhs.To_str(), " ", this.op_literal, " ", rhs.To_str());} + public String Op_literal() {return op_literal;} private String op_literal; + public Criteria Lhs() {return lhs;} private Criteria lhs; + public Criteria Rhs() {return rhs;} private Criteria rhs; + public static Criteria_bool_base as_(Object obj) {return obj instanceof Criteria_bool_base ? (Criteria_bool_base)obj : null;} +} +class Criteria_and extends Criteria_bool_base { + public Criteria_and(Criteria lhs, Criteria rhs) {this.Ctor("AND", lhs, rhs);} + @Override public byte Tid() {return Criteria_.Tid_and;} + @Override public boolean Matches(Object curVal) {return this.Lhs().Matches(curVal) && this.Rhs().Matches(curVal);} +} +class Criteria_or extends Criteria_bool_base { + public Criteria_or(Criteria lhs, Criteria rhs) {this.Ctor("OR", lhs, rhs);} + @Override public byte Tid() {return Criteria_.Tid_or;} + @Override public boolean Matches(Object curVal) {return this.Lhs().Matches(curVal) || this.Rhs().Matches(curVal);} +} +class Criteria_const implements Criteria { + public Criteria_const(boolean val) {this.val = val;} + public byte Tid() {return Criteria_.Tid_const;} + public boolean Matches(Object comp) {return val;} private final boolean val; + public void Val_from_args(Hash_adp args) {;} + public void Val_as_obj_(Object v) {throw Err_.new_unimplemented();} + public String To_str() {return String_.Concat(" IS ", Bool_.To_str_lower(val));} +} diff --git a/100_core/src/gplx/core/criterias/Criteria_comp.java b/100_core/src/gplx/core/criterias/Criteria_comp.java new file mode 100644 index 000000000..a1f03e242 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_comp.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public class Criteria_comp implements Criteria { + @gplx.Internal protected Criteria_comp(int comp_mode, Comparable val) {this.comp_mode = comp_mode; this.val = val;} + public byte Tid() {return Criteria_.Tid_comp;} + public Comparable Val() {return val;} private Comparable val; + public void Val_from_args(Hash_adp args) {throw Err_.new_unimplemented();} + public void Val_as_obj_(Object v) {val = (Comparable)v;} + public int Comp_mode() {return comp_mode;} private final int comp_mode; + public boolean Matches(Object comp_obj) { + return CompareAble_.Is(comp_mode, CompareAble_.as_(comp_obj), val); + } + + public String To_str() { + String comp_sym = comp_mode < CompareAble_.Same ? "<" : ">"; + String eq_sym = comp_mode % 2 == CompareAble_.Same ? "=" : ""; + return String_.Concat_any(comp_sym, eq_sym, " ", val); + } +} diff --git a/100_core/src/gplx/core/criterias/Criteria_eq.java b/100_core/src/gplx/core/criterias/Criteria_eq.java new file mode 100644 index 000000000..5e70583bf --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_eq.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public class Criteria_eq implements Criteria { + @gplx.Internal protected Criteria_eq(boolean neg, Object val) {this.neg = neg; this.val = val;} + public byte Tid() {return Criteria_.Tid_eq;} + public boolean Neg() {return neg;} private final boolean neg; + public Object Val() {return val;} private Object val; + public void Val_as_obj_(Object v) {this.val = v;} + public void Val_from_args(Hash_adp args) {throw Err_.new_unimplemented();} + public boolean Matches(Object comp) { + Class val_type = Type_adp_.ClassOf_obj(val); + if (!Type_adp_.Eq_typeSafe(comp, val_type)) throw Err_.new_type_mismatch(val_type, comp); + boolean rv = Object_.Eq(val, comp); + return neg ? !rv : rv; + } + public String To_str() {return String_.Concat_any("= ", val);} + public static Criteria_eq as_(Object obj) {return obj instanceof Criteria_eq ? (Criteria_eq)obj : null;} +} diff --git a/100_core/src/gplx/core/criterias/Criteria_fld.java b/100_core/src/gplx/core/criterias/Criteria_fld.java new file mode 100644 index 000000000..2ea7fe71e --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_fld.java @@ -0,0 +1,71 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public class Criteria_fld implements Criteria { + Criteria_fld(String pre, String key, Criteria crt) {this.pre = pre; this.key = key; this.crt = crt;} + public byte Tid() {return Criteria_.Tid_wrapper;} + public String Pre() {return pre;} private final String pre; + public String Key() {return key;} private final String key; + public Criteria Crt() {return crt;} private final Criteria crt; + public String Pre_w_key() {return pre == Pre_null ? key : String_.Concat(pre, ".", key);} + public void Val_as_obj_(Object v) {throw Err_.new_unimplemented();} + public void Val_from_args(Hash_adp args) { + List_adp list = (List_adp)args.Get_by(key); if (list == null) throw Err_.new_wo_type("criteria.fld key not found", "key", key); + Object o = Fill_val(key, crt.Tid(), list); + crt.Val_as_obj_(o); + } + public boolean Matches(Object invkObj) { + Gfo_invk invk = (Gfo_invk)invkObj; + if (key == Criteria_fld.Key_null) return crt.Matches(invkObj); + Object comp = Gfo_invk_.Invk_by_key(invk, this.Pre_w_key()); + return crt.Matches(comp); + } + public String To_str() {return String_.Concat(key, " ", crt.To_str());} + + public static final String Key_null = null, Pre_null = null; + public static Criteria_fld as_(Object obj) {return obj instanceof Criteria_fld ? (Criteria_fld)obj : null;} + public static Criteria_fld new_(String pre, String key, Criteria crt) {return new Criteria_fld(pre, key, crt);} + public static Criteria_fld new_(String key, Criteria crt) {return new Criteria_fld(Pre_null, key, crt);} + public static Object Fill_val(String key, byte tid, List_adp list) { + int len = list.Count(); + switch (tid) { + case Criteria_.Tid_eq: + case Criteria_.Tid_comp: + case Criteria_.Tid_like: + case Criteria_.Tid_iomatch: + if (len != 1) throw Err_.new_wo_type("list.len should be 1", "key", key, "tid", tid, "len", len); + return list.Get_at(0); + case Criteria_.Tid_between: + if (len != 2) throw Err_.new_wo_type("list.len should be 2", "key", key, "tid", tid, "len", len); + return new Object[] {list.Get_at(0), list.Get_at(1)}; + case Criteria_.Tid_in: + if (len == 0) throw Err_.new_wo_type("list.len should be > 0", "key", key, "tid", tid, "len", len); + return list.To_obj_ary(); + case Criteria_.Tid_const: + case Criteria_.Tid_not: + case Criteria_.Tid_and: + case Criteria_.Tid_or: + if (len != 0) throw Err_.new_wo_type("list.len should be 0", "key", key, "tid", tid, "len", len); + return key; // no values to fill in; return back key + case Criteria_.Tid_wrapper: // not recursive + case Criteria_.Tid_db_obj_ary: // unsupported + case Criteria_.Tid_custom: + default: throw Err_.new_unhandled(tid); + } + } +} diff --git a/100_core/src/gplx/core/criterias/Criteria_in.java b/100_core/src/gplx/core/criterias/Criteria_in.java new file mode 100644 index 000000000..55c70c5e8 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_in.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public class Criteria_in implements Criteria { + public Criteria_in(boolean neg, Object[] ary) {this.neg = neg; Ary_(ary);} + public byte Tid() {return Criteria_.Tid_in;} + public boolean Neg() {return neg;} private final boolean neg; + public Object[] Ary() {return ary;} private Object[] ary; + public int Ary_len() {return ary_len;} private int ary_len; + public Class Itm_type() {return itm_type;} private Class itm_type; + private void Ary_(Object[] v) { + this.ary = v; + this.ary_len = ary.length; + this.itm_type = ary_len == 0 ? Object.class : Type_adp_.ClassOf_obj(ary[0]); + } + public void Val_as_obj_(Object v) {Ary_((Object[])v);} + public void Val_from_args(Hash_adp args) {throw Err_.new_unimplemented();} + public boolean Matches(Object comp) { + if (ary_len == 0) return false; // empty array never matches + if (!Type_adp_.Eq_typeSafe(comp, itm_type)) throw Err_.new_type_mismatch(itm_type, comp); + boolean rv = false; + for (int i = 0; i < ary_len; ++i) { + Object val = ary[i]; + if (Object_.Eq(val, comp)) { + rv = true; + break; + } + } + return neg ? !rv : rv; + } + + public String To_str() {return String_.Concat_any("IN ", String_.Concat_any(ary));} +} diff --git a/100_core/src/gplx/core/criterias/Criteria_ioItm_tst.java b/100_core/src/gplx/core/criterias/Criteria_ioItm_tst.java new file mode 100644 index 000000000..9eb145b84 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_ioItm_tst.java @@ -0,0 +1,50 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +import org.junit.*; +import gplx.core.ios.*; +public class Criteria_ioItm_tst { + IoItmFil fil; Criteria crt; IoItm_fxt fx = IoItm_fxt.new_(); + @Test public void IoType() { + crt = crt_(IoItm_base_.Prop_Type, Criteria_.eq_(IoItmFil.Type_Fil)); + tst_Match(true, crt, fx.fil_wnt_("C:\\fil.txt")); + tst_Match(false, crt, fx.dir_wnt_("C:\\dir")); + } + @Test public void Ext() { + crt = crt_(IoItm_base_.Prop_Ext, Criteria_.eq_(".txt")); + tst_Match(true, crt, fx.fil_wnt_("C:\\fil.txt")); + tst_Match(false, crt, fx.fil_wnt_("C:\\fil.xml"), fx.fil_wnt_("C:\\fil.txt1"), fx.fil_wnt_("C:\\fil1.txt.xml"), fx.dir_wnt_("C:\\.txt")); + } + @Test public void Modified() { + fil = fx.fil_wnt_("C:\\fil.txt"); + crt = crt_(IoItmFil_.Prop_Modified, Criteria_.mte_(DateAdp_.parse_gplx("2001-01-01"))); + tst_Match(true, crt, fil.ModifiedTime_(DateAdp_.parse_gplx("2001-01-02")), fil.ModifiedTime_(DateAdp_.parse_gplx("2001-01-01"))); + tst_Match(false, crt, fil.ModifiedTime_(DateAdp_.parse_gplx("2000-12-31"))); + } + @Test public void IoMatch() { + Criteria crt = Criteria_ioMatch.parse(true, "*.txt", false); + CriteriaFxt fx_crt = new CriteriaFxt(); + fx_crt.tst_Matches(crt, Io_url_.new_any_("file.txt")); + fx_crt.tst_MatchesNot(crt, Io_url_.new_any_("file.xml")); + } + Criteria crt_(String fld, Criteria crt) {return Criteria_fld.new_(fld, crt);} + void tst_Match(boolean expt, Criteria fieldCrt, IoItm_base... ary) { + for (IoItm_base itm : ary) + Tfds.Eq(expt, fieldCrt.Matches(itm)); + } +} diff --git a/100_core/src/gplx/core/criterias/Criteria_ioMatch.java b/100_core/src/gplx/core/criterias/Criteria_ioMatch.java new file mode 100644 index 000000000..13adafddc --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_ioMatch.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +import gplx.core.texts.*; +public class Criteria_ioMatch implements Criteria { // EX: url IOMATCH '*.xml|*.txt' + public Criteria_ioMatch(boolean match, RegxPatn_cls_ioMatch pattern) {this.match = match; this.pattern = pattern;} + public byte Tid() {return Criteria_.Tid_iomatch;} + public boolean Neg() {return !match;} private final boolean match; + public void Val_from_args(Hash_adp args) {throw Err_.new_unimplemented();} + public void Val_as_obj_(Object v) {this.pattern = (RegxPatn_cls_ioMatch)v;} + public RegxPatn_cls_ioMatch Pattern() {return pattern;} private RegxPatn_cls_ioMatch pattern; + public boolean Matches(Object compObj) { + Io_url comp = (Io_url)compObj; + boolean rv = pattern.Matches(comp.XtoCaseNormalized()); + return match ? rv : !rv; + } + public String To_str() {return String_.Concat_any("IOMATCH ", pattern);} + + public static final String TokenName = "IOMATCH"; + public static Criteria_ioMatch as_(Object obj) {return obj instanceof Criteria_ioMatch ? (Criteria_ioMatch)obj : null;} + public static Criteria_ioMatch parse(boolean match, String raw, boolean caseSensitive) {return new Criteria_ioMatch(match, RegxPatn_cls_ioMatch_.parse(raw, caseSensitive));} +} diff --git a/100_core/src/gplx/core/criterias/Criteria_like.java b/100_core/src/gplx/core/criterias/Criteria_like.java new file mode 100644 index 000000000..b19f4d050 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_like.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +import gplx.core.texts.*; /*RegxPatn_cls_like*/ +public class Criteria_like implements Criteria { + @gplx.Internal protected Criteria_like(boolean neg, RegxPatn_cls_like pattern) {this.neg = neg; this.pattern = pattern;} + public byte Tid() {return Criteria_.Tid_like;} + public boolean Neg() {return neg;} private final boolean neg; + public RegxPatn_cls_like Pattern() {return pattern;} private RegxPatn_cls_like pattern; + public void Val_from_args(Hash_adp args) {throw Err_.new_unimplemented();} + public void Val_as_obj_(Object v) {this.pattern = RegxPatn_cls_like_.parse((String)v, RegxPatn_cls_like.EscapeDefault);} + public boolean Matches(Object compObj) { + String comp = String_.as_(compObj); if (comp == null) throw Err_.new_type_mismatch(String.class, compObj); + boolean rv = pattern.Matches(comp); + return neg ? !rv : rv; + } + + public String To_str() {return String_.Concat_any("LIKE ", pattern);} +} \ No newline at end of file diff --git a/100_core/src/gplx/core/criterias/Criteria_not.java b/100_core/src/gplx/core/criterias/Criteria_not.java new file mode 100644 index 000000000..b4c67aac1 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_not.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +public class Criteria_not implements Criteria { + public Criteria_not(Criteria v) {this.criteria = v;} + public byte Tid() {return Criteria_.Tid_not;} + public boolean Matches(Object obj) {return !criteria.Matches(obj);} + public void Val_from_args(Hash_adp args) {criteria.Val_from_args(args);} + public void Val_as_obj_(Object v) {criteria.Val_as_obj_(v);} + public String To_str() {return String_.Concat_any(" NOT ", criteria.To_str());} + public Criteria Crt() {return criteria;} private final Criteria criteria; +} diff --git a/100_core/src/gplx/core/criterias/Criteria_tst.java b/100_core/src/gplx/core/criterias/Criteria_tst.java new file mode 100644 index 000000000..693387198 --- /dev/null +++ b/100_core/src/gplx/core/criterias/Criteria_tst.java @@ -0,0 +1,92 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.criterias; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Criteria_tst { + @Test public void Equal() { + Criteria crt = Criteria_.eq_(true); + fx.tst_Matches(crt, true); + fx.tst_MatchesNot(crt, false); + fx.tst_MatchesFail(crt, "true"); + + fx.tst_Matches(Criteria_.eq_(1), 1); + fx.tst_Matches(Criteria_.eq_("equal"), "equal"); + fx.tst_Matches(Criteria_.eq_(date), date); + } + @Test public void Not() { + Criteria crt = Criteria_.eqn_(true); + fx.tst_Matches(crt, false); + fx.tst_MatchesNot(crt, true); + fx.tst_MatchesFail(crt, "false"); + + fx.tst_Matches(Criteria_.eqn_(1), -1); + fx.tst_Matches(Criteria_.eqn_("equal"), "not equal"); + fx.tst_Matches(Criteria_.eqn_(date), date.Add_minute(1)); + } + @Test public void MoreThan() { + Criteria crt = Criteria_.mt_(0); + fx.tst_Matches(crt, 1, 2); + fx.tst_MatchesNot(crt, 0, -1); + fx.tst_MatchesFail(crt, "1"); + + fx.tst_Matches(Criteria_.mt_(0), 1); + fx.tst_Matches(Criteria_.mt_("a"), "b"); + fx.tst_Matches(Criteria_.mt_(date), date.Add_minute(1)); + fx.tst_Matches(Criteria_.mt_(false), true); // MISC: thus truth is greater than falsehood + } + @Test public void MoreThanEq() { + Criteria crt = Criteria_.mte_(0); + fx.tst_Matches(crt, 0); + } + @Test public void Less() { + Criteria crt = Criteria_.lt_(0); + fx.tst_Matches(crt, -1, -2); + fx.tst_MatchesNot(crt, 0, 1); + fx.tst_MatchesFail(crt, "-1"); + } + @Test public void LessEq() { + Criteria crt = Criteria_.lte_(0); + fx.tst_Matches(crt, 0); + } + @Test public void Between() { + Criteria crt = Criteria_.between_(-1, 1); + fx.tst_Matches(crt, 0, 1, -1); + fx.tst_MatchesNot(crt, -2, 2); + fx.tst_MatchesFail(crt, "0"); + + fx.tst_Matches(Criteria_.between_(1, -1), 0); // reverse range + fx.tst_Matches(Criteria_.between_("a", "c"), "b"); + } + @Test public void In() { + Criteria crt = Criteria_.in_(0, 1, 2); + fx.tst_Matches(crt, 0, 1, 2); + fx.tst_MatchesNot(crt, 3, -1); + fx.tst_MatchesFail(crt, "0"); + } + CriteriaFxt fx = new CriteriaFxt(); + DateAdp date = DateAdp_.parse_gplx("2001-01-01"); +} +class CriteriaFxt { + public void tst_Matches(Criteria crt, Object... ary) {for (Object val : ary) Tfds.Eq(true, crt.Matches(val));} + public void tst_MatchesNot(Criteria crt, Object... ary) {for (Object val : ary) Tfds.Eq(false, crt.Matches(val));} + public void tst_MatchesFail(Criteria crt, Object val) { + try {crt.Matches(val);} + catch(Exception exc) {Err_.Noop(exc); return;} + Tfds.Fail_expdError(); + } +} diff --git a/100_core/src/gplx/core/encoders/B85_fp_.java b/100_core/src/gplx/core/encoders/B85_fp_.java new file mode 100644 index 000000000..fc0d9cd17 --- /dev/null +++ b/100_core/src/gplx/core/encoders/B85_fp_.java @@ -0,0 +1,56 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.encoders; import gplx.*; import gplx.core.*; +public class B85_fp_ { + public static byte[] To_bry(double v) { + String str = Double_.To_str(v); + byte[] bry = Bry_.new_a7(str); int len = bry.length; + int num_len = len; boolean neg = false; + int bgn = 0; int dot = -1; + if (bry[0] == Byte_ascii.Dash) {neg = true; bgn = 1; --num_len;} + boolean skip_zeros = true; + for (int i = bgn; i < len; ++i) { + byte b = bry[i]; + switch (b) { + case Byte_ascii.Num_0: + if (skip_zeros) + --num_len; + break; + case Byte_ascii.Dot: + skip_zeros = false; + dot = i; + --num_len; + break; + default: + skip_zeros = false; + break; + } + } + int pow = 0; + if (dot != -1) { + pow = len - (dot + 1); + for (int i = 0; i < pow; ++i) + v *= 10; + } + int num = (int)v; + byte[] rv = new byte[num_len + 1]; + rv[0] = (byte)(Byte_ascii.Dot + pow + (neg ? 45 : 0)); + Base85_.Set_bry(num, rv, 1, 1); + return rv; + } +} diff --git a/100_core/src/gplx/core/encoders/B85_fp__tst.java b/100_core/src/gplx/core/encoders/B85_fp__tst.java new file mode 100644 index 000000000..8ed7e0ef4 --- /dev/null +++ b/100_core/src/gplx/core/encoders/B85_fp__tst.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.encoders; import gplx.*; import gplx.core.*; +import org.junit.*; +public class B85_fp__tst { + private final B85_fp__fxt fxt = new B85_fp__fxt(); + @Test public void Double_to_str() { + fxt.Test__to_str(.1d, "/\""); + } +} +class B85_fp__fxt { + public void Test__to_str(double val, String expd) { + byte[] actl = B85_fp_.To_bry(val); + Tfds.Eq_str(expd, String_.new_a7(actl)); + } +} diff --git a/100_core/src/gplx/core/encoders/Base85_.java b/100_core/src/gplx/core/encoders/Base85_.java new file mode 100644 index 000000000..aa4d4519c --- /dev/null +++ b/100_core/src/gplx/core/encoders/Base85_.java @@ -0,0 +1,66 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.encoders; import gplx.*; import gplx.core.*; +public class Base85_ { + public static String To_str(int val, int min_len) {return String_.new_u8(Set_bry(val, null, 0, min_len));} + public static byte[] To_bry(int val, int min_len) {return Set_bry(val, null, 0, min_len);} + public static byte[] Set_bry(int val, byte[] ary, int ary_pos, int min_len) { + int val_len = Bry_len(val); + int ary_len = val_len, pad_len = 0; + boolean pad = ary_len < min_len; + if (pad) { + pad_len = min_len - ary_len; + ary_len = min_len; + } + if (ary == null) ary = new byte[ary_len]; + if (pad) { + for (int i = 0; i < pad_len; i++) // fill ary with pad_len + ary[i + ary_pos] = A7_offset; + } + for (int i = ary_len - pad_len; i > 0; i--) { + int div = Pow85[i - 1]; + byte tmp = (byte)(val / div); + ary[ary_pos + ary_len - i] = (byte)(tmp + A7_offset); + val -= tmp * div; + } + return ary; + } + public static int To_int_by_str(String s) { + byte[] ary = Bry_.new_u8(s); + return To_int_by_bry(ary, 0, ary.length - 1); + } + public static int To_int_by_bry(byte[] ary, int bgn, int end) { + int rv = 0, factor = 1; + for (int i = end; i >= bgn; i--) { + rv += (ary[i] - A7_offset) * factor; + factor *= Radix; + } + return rv; + } + public static int Bry_len(int v) { + if (v == 0) return 1; + for (int i = Pow85_last; i > -1; i--) + if (v >= Pow85[i]) return i + 1; + throw Err_.new_wo_type("neg number not allowed", "v", v); + } + public static final int Len_int = 5; + private static final int Pow85_last = 4, Radix = 85; + public static final byte A7_offset = 33; + public static final int Pow85_0 = 1, Pow85_1 = 85, Pow85_2 = 7225, Pow85_3 = 614125, Pow85_4 = 52200625; + public static int[] Pow85 = new int[]{Pow85_0, Pow85_1, Pow85_2, Pow85_3, Pow85_4}; // NOTE: ary constructed to match index to exponent; Pow85[1] = 85^1 +} diff --git a/100_core/src/gplx/core/encoders/Base85__tst.java b/100_core/src/gplx/core/encoders/Base85__tst.java new file mode 100644 index 000000000..f3268532e --- /dev/null +++ b/100_core/src/gplx/core/encoders/Base85__tst.java @@ -0,0 +1,61 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.encoders; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Base85__tst { + private final Base85__fxt fxt = new Base85__fxt(); + @Test public void Log() { + fxt.Test_log( 0, 1); + fxt.Test_log( 84, 1); + fxt.Test_log( 85, 2); + fxt.Test_log( 7224, 2); + fxt.Test_log( 7225, 3); + fxt.Test_log( 614124, 3); + fxt.Test_log( 614125, 4); + fxt.Test_log( 52200624, 4); + fxt.Test_log( 52200625, 5); + fxt.Test_log(Int_.Max_value, 5); + } + @Test public void To_str() { + fxt.Test_to_str( 0, "!"); + fxt.Test_to_str( 84, "u"); + fxt.Test_to_str( 85, "\"!"); + fxt.Test_to_str( 7224, "uu"); + fxt.Test_to_str( 7225, "\"!!"); + fxt.Test_to_str( 614124, "uuu"); + fxt.Test_to_str( 614125, "\"!!!"); + fxt.Test_to_str( 52200624, "uuuu"); + fxt.Test_to_str( 52200625, "\"!!!!"); + } + @Test public void XtoStrAry() { + byte[] ary = new byte[9]; + fxt.Exec_to_str(ary, 0, 2); // !!# + fxt.Exec_to_str(ary, 3, 173); // !#$ + fxt.Exec_to_str(ary, 6, 14709); // #$% + Tfds.Eq("!!#!#$#$%", String_.new_u8(ary)); + } +} +class Base85__fxt { + public void Test_log(int val, int expd) {Tfds.Eq(expd, Base85_.Bry_len(val));} + public void Test_to_str(int val, String expd) { + String actl = Base85_.To_str(val, 0); + Tfds.Eq(expd, actl); + Tfds.Eq(val, Base85_.To_int_by_str(expd)); + } + public void Exec_to_str(byte[] ary, int aryPos, int val) {Base85_.Set_bry(val, ary, aryPos, 3);} +} diff --git a/100_core/src/gplx/core/encoders/Gfo_hzip_int_.java b/100_core/src/gplx/core/encoders/Gfo_hzip_int_.java new file mode 100644 index 000000000..c1c8e7278 --- /dev/null +++ b/100_core/src/gplx/core/encoders/Gfo_hzip_int_.java @@ -0,0 +1,63 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.encoders; import gplx.*; import gplx.core.*; +import gplx.core.primitives.*; +public class Gfo_hzip_int_ { + public static final int Neg_1_adj = 1; + public static void Encode(int reqd, Bry_bfr bfr, int val) { + int bfr_len = bfr.Len(); + int len_in_base85 = Base85_.Bry_len(val); + boolean abrv = val < Base85_.Pow85[reqd]; + int adj = abrv ? 0 : 1; + int actl_len = len_in_base85 + adj; + if (actl_len < reqd) actl_len = reqd; + bfr.Add_byte_repeat(Byte_ascii.Bang, actl_len); // fill with 0s; this asserts that there underlying array will be large enough for following write + byte[] bfr_bry = bfr.Bfr(); // NOTE: set bry reference here b/c Add_byte_repeat may create a new one + Base85_.Set_bry(val, bfr_bry, bfr_len + adj, reqd); // calc base85 val for val; EX: 7224 -> "uu" + if (!abrv) { + byte base85_byte = Base85_len__2; + switch (len_in_base85) { + case 3: base85_byte = Base85_len__3; break; + case 4: base85_byte = Base85_len__4; break; + case 5: base85_byte = Base85_len__5; break; + } + bfr_bry[bfr_len] = base85_byte; + } + } + public static int Decode(int reqd, byte[] src, int src_len, int src_bgn, Int_obj_ref pos_ref) { + byte b0 = src[src_bgn]; + int base85_bgn = src_bgn + 1; + int len_in_base85 = 1; // default to 1 + switch (b0) { + case Base85_len__2: len_in_base85 = 2; break; + case Base85_len__3: len_in_base85 = 3; break; + case Base85_len__4: len_in_base85 = 4; break; + case Base85_len__5: len_in_base85 = 5; break; + default: --base85_bgn; break; + } + if (len_in_base85 < reqd) len_in_base85 = reqd; + int base85_end = base85_bgn + len_in_base85; + pos_ref.Val_(base85_end); + return Base85_.To_int_by_bry(src, base85_bgn, base85_end - 1); + } + private static final byte + Base85_len__2 = Byte_ascii.Curly_bgn + , Base85_len__3 = Byte_ascii.Pipe + , Base85_len__4 = Byte_ascii.Curly_end + , Base85_len__5 = Byte_ascii.Tilde; +} diff --git a/100_core/src/gplx/core/encoders/Hex_utl_.java b/100_core/src/gplx/core/encoders/Hex_utl_.java new file mode 100644 index 000000000..1a9a0232d --- /dev/null +++ b/100_core/src/gplx/core/encoders/Hex_utl_.java @@ -0,0 +1,143 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.encoders; import gplx.*; import gplx.core.*; +public class Hex_utl_ { + public static int Parse_or(byte[] src, int or) {return Parse_or(src, 0, src.length, or);} + public static int Parse_or(byte[] src, int bgn, int end, int or) { + int rv = 0; int factor = 1; + byte b = Byte_.Max_value_127; + for (int i = end - 1; i >= bgn; i--) { + switch (src[i]) { + case Byte_ascii.Num_0: b = 0; break; case Byte_ascii.Num_1: b = 1; break; case Byte_ascii.Num_2: b = 2; break; case Byte_ascii.Num_3: b = 3; break; case Byte_ascii.Num_4: b = 4; break; + case Byte_ascii.Num_5: b = 5; break; case Byte_ascii.Num_6: b = 6; break; case Byte_ascii.Num_7: b = 7; break; case Byte_ascii.Num_8: b = 8; break; case Byte_ascii.Num_9: b = 9; break; + case Byte_ascii.Ltr_A: b = 10; break; case Byte_ascii.Ltr_B: b = 11; break; case Byte_ascii.Ltr_C: b = 12; break; case Byte_ascii.Ltr_D: b = 13; break; case Byte_ascii.Ltr_E: b = 14; break; case Byte_ascii.Ltr_F: b = 15; break; + case Byte_ascii.Ltr_a: b = 10; break; case Byte_ascii.Ltr_b: b = 11; break; case Byte_ascii.Ltr_c: b = 12; break; case Byte_ascii.Ltr_d: b = 13; break; case Byte_ascii.Ltr_e: b = 14; break; case Byte_ascii.Ltr_f: b = 15; break; + default: b = Byte_.Max_value_127; break; + } + if (b == Byte_.Max_value_127) return or; + rv += b * factor; + factor *= 16; + } + return rv; + } + public static int Parse(String src) { + int rv = Parse_or(src, -1); if (rv == -1) throw Err_.new_parse("HexDec", "src"); + return rv; + } + public static int Parse_or(String src, int or) { + int rv = 0; int digit = 0, factor = 1, len = String_.Len(src); + for (int i = len - 1; i > -1; --i) { + digit = To_int(String_.CharAt(src, i)); + if (digit == -1) return or; + rv += digit * factor; + factor *= 16; + } + return rv; + } + public static byte[] Encode_bry(byte[] src) { + int src_len = src.length; + byte[] trg = new byte[src_len * 2]; + Encode_bry(src, trg); + return trg; + } + public static void Encode_bry(byte[] src, byte[] trg) { + int src_len = src.length, trg_len = trg.length; + if (trg_len != src_len * 2) throw Err_.new_("hex", "trg.len must be src.len * 2", "src_len", src_len, "trg_len", trg_len); + int trg_idx = -1; + for (int src_idx = 0; src_idx < src_len; ++src_idx) { + byte src_byte = src[src_idx]; + trg[++trg_idx] = To_byte_lcase(0xf & src_byte >>> 4); + trg[++trg_idx] = To_byte_lcase(0xf & src_byte); + } + } + public static void Encode_bfr(Bry_bfr bfr, byte[] src) { + int src_len = src.length; + for (int src_idx = 0; src_idx < src_len; ++src_idx) { + byte src_byte = src[src_idx]; + bfr.Add_byte(To_byte_lcase(0xf & src_byte >>> 4)); + bfr.Add_byte(To_byte_lcase(0xf & src_byte)); + } + } + public static String To_str(int val, int pad) { + char[] ary = new char[8]; int idx = 8; // 8 is max len of hexString; (2^4 * 8); EX: int.MaxValue = 7FFFFFFF + do { + int byt = val % 16; + ary[--idx] = To_char(byt); + val /= 16; + } while (val > 0); + while (8 - idx < pad) // pad left with zeros + ary[--idx] = '0'; + return String_.new_charAry_(ary, idx, 8-idx); + } + public static void Write(byte[] bry, int bgn, int end, int val) { + for (int i = end - 1; i > bgn - 1; i--) { + int b = val % 16; + bry[i] = To_byte(b); + val /= 16; + if (val == 0) break; + } + } + public static boolean Is_hex_many(byte... ary) { + for (byte itm : ary) { + switch (itm) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: case Byte_ascii.Ltr_F: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: case Byte_ascii.Ltr_f: + break; + default: + return false; + } + } + return true; + } + private static int To_int(char c) { + switch (c) { + case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; + case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; + case 'A': return 10; case 'B': return 11; case 'C': return 12; case 'D': return 13; case 'E': return 14; case 'F': return 15; + case 'a': return 10; case 'b': return 11; case 'c': return 12; case 'd': return 13; case 'e': return 14; case 'f': return 15; + default: return -1; + } + } + private static char To_char(int val) { + switch (val) { + case 0: return '0'; case 1: return '1'; case 2: return '2'; case 3: return '3'; case 4: return '4'; + case 5: return '5'; case 6: return '6'; case 7: return '7'; case 8: return '8'; case 9: return '9'; + case 10: return 'A'; case 11: return 'B'; case 12: return 'C'; case 13: return 'D'; case 14: return 'E'; case 15: return 'F'; + default: throw Err_.new_parse("hexstring", Int_.To_str(val)); + } + } + private static byte To_byte(int v) { + switch (v) { + case 0: return Byte_ascii.Num_0; case 1: return Byte_ascii.Num_1; case 2: return Byte_ascii.Num_2; case 3: return Byte_ascii.Num_3; case 4: return Byte_ascii.Num_4; + case 5: return Byte_ascii.Num_5; case 6: return Byte_ascii.Num_6; case 7: return Byte_ascii.Num_7; case 8: return Byte_ascii.Num_8; case 9: return Byte_ascii.Num_9; + case 10: return Byte_ascii.Ltr_A; case 11: return Byte_ascii.Ltr_B; case 12: return Byte_ascii.Ltr_C; case 13: return Byte_ascii.Ltr_D; case 14: return Byte_ascii.Ltr_E; case 15: return Byte_ascii.Ltr_F; + default: throw Err_.new_parse("hexstring", Int_.To_str(v)); + } + } + private static byte To_byte_lcase(int v) { + switch (v) { + case 0: return Byte_ascii.Num_0; case 1: return Byte_ascii.Num_1; case 2: return Byte_ascii.Num_2; case 3: return Byte_ascii.Num_3; + case 4: return Byte_ascii.Num_4; case 5: return Byte_ascii.Num_5; case 6: return Byte_ascii.Num_6; case 7: return Byte_ascii.Num_7; + case 8: return Byte_ascii.Num_8; case 9: return Byte_ascii.Num_9; case 10: return Byte_ascii.Ltr_a; case 11: return Byte_ascii.Ltr_b; + case 12: return Byte_ascii.Ltr_c; case 13: return Byte_ascii.Ltr_d; case 14: return Byte_ascii.Ltr_e; case 15: return Byte_ascii.Ltr_f; + default: throw Err_.new_parse("hexstring", Int_.To_str(v)); + } + } +} diff --git a/100_core/src/gplx/core/encoders/Hex_utl__tst.java b/100_core/src/gplx/core/encoders/Hex_utl__tst.java new file mode 100644 index 000000000..9579d71da --- /dev/null +++ b/100_core/src/gplx/core/encoders/Hex_utl__tst.java @@ -0,0 +1,70 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.encoders; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Hex_utl__tst { + private final Hex_utl__fxt fxt = new Hex_utl__fxt(); + @Test public void To_int() { + fxt.Test__to_int("0" , 0); + fxt.Test__to_int("F" , 15); + fxt.Test__to_int("0F" , 15); + fxt.Test__to_int("10" , 16); + fxt.Test__to_int("20" , 32); + fxt.Test__to_int("FF" , 255); + fxt.Test__to_int("100" , 256); + fxt.Test__to_int("0a" , 10); + fxt.Test__to_int("7FFFFFFF" , Int_.Max_value); + fxt.Test__to_int_bry("100" , 256); + } + @Test public void To_str() { + fxt.Test__to_str(0 , "0"); + fxt.Test__to_str(15 , "F"); + fxt.Test__to_str(16 , "10"); + fxt.Test__to_str(32 , "20"); + fxt.Test__to_str(255 , "FF"); + fxt.Test__to_str(Int_.Max_value, "7FFFFFFF"); + + fxt.Test__to_str(15, 2 , "0F"); + fxt.Test__to_str(15, 3 , "00F"); + } + @Test public void Write() { + fxt.Test__write("[00000000]", 1, 9, 15, "[0000000F]"); + fxt.Test__write("[00000000]", 1, 9, 255, "[000000FF]"); + } +} +class Hex_utl__fxt { + public void Test__write(String s, int bgn, int end, int val, String expd) { + byte[] bry = Bry_.new_a7(s); + Hex_utl_.Write(bry, bgn, end, val); + Tfds.Eq(expd, String_.new_a7(bry)); + } + public void Test__to_int(String raw, int expd) { + int actl = Hex_utl_.Parse(raw); + Tfds.Eq(expd, actl); + } + public void Test__to_int_bry(String raw, int expd) {Tfds.Eq(expd, Hex_utl_.Parse_or(Bry_.new_a7(raw), -1));} + public void Test__to_str(int val, String expd) {Test__to_str(val, 0, expd);} + public void Test__to_str(int val, int pad, String expd) { + String actl = Hex_utl_.To_str(val, pad); + Tfds.Eq(expd, actl); + } +// public void Test__encode_bry(int val, int pad, String expd) { +// String actl = Hex_utl_.To_str(val, pad); +// Tfds.Eq(expd, actl); +// } +} diff --git a/100_core/src/gplx/core/envs/Env_.java b/100_core/src/gplx/core/envs/Env_.java new file mode 100644 index 000000000..9e43cbc3c --- /dev/null +++ b/100_core/src/gplx/core/envs/Env_.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +public class Env_ { + public static void Init(String[] args, String appNameAndExt, Class type) { + mode_testing = false; + mode_debug = String_.In("GPLX_DEBUG_MODE_ENABLED", args); + appArgs = args; + appUrl = Jar_adp_.Url_type(type).OwnerDir().GenSubFil(appNameAndExt); + } + public static void Init_swt(String[] args, Class type) { // DATE:2014-06-23 + mode_testing = false; + mode_debug = String_.In("GPLX_DEBUG_MODE_ENABLED", args); + appArgs = args; + appUrl = Jar_adp_.Url_type(type); + } + public static void Init_drd() { + mode_testing = mode_debug = false; + } + public static void Init_testing() {mode_testing = true;} + public static void Init_testing_n_() {mode_testing = false;} + public static boolean Mode_testing() {return mode_testing;} private static boolean mode_testing = true; + public static boolean Mode_debug() {return mode_debug;} private static boolean mode_debug = false; + public static String[] AppArgs() {return appArgs;} static String[] appArgs; + public static Io_url AppUrl() { + if (mode_testing) return Io_url_.mem_fil_("mem/testing.jar"); + if (appUrl == Io_url_.Empty) throw Err_.new_wo_type("Env_.Init was not called"); + return appUrl; + } static Io_url appUrl = Io_url_.Empty; + + public static final String LocalHost = "127.0.0.1"; + public static String NewLine_lang() {return mode_testing ? "\n" : "\n";} +} diff --git a/100_core/src/gplx/core/envs/Jar_adp_.java b/100_core/src/gplx/core/envs/Jar_adp_.java new file mode 100644 index 000000000..5edeac6ac --- /dev/null +++ b/100_core/src/gplx/core/envs/Jar_adp_.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +public class Jar_adp_ { + public static DateAdp ModifiedTime_type(Class type) {if (type == null) throw Err_.new_null(); + Io_url url = Url_type(type); + return Io_mgr.Instance.QueryFil(url).ModifiedTime(); + } + public static Io_url Url_type(Class type) {if (type == null) throw Err_.new_null(); + String codeBase = type.getProtectionDomain().getCodeSource().getLocation().getPath(); + if (Op_sys.Cur().Tid_is_wnt()) + codeBase = String_.Mid(codeBase, 1); // codebase always starts with /; remove for wnt + codeBase = String_.Replace(codeBase, "/", Op_sys.Cur().Fsys_dir_spr_str()); // java always returns DirSpr as /; change to Env_.DirSpr to handle windows + try {codeBase = java.net.URLDecoder.decode(codeBase, "UTF-8");} + catch (java.io.UnsupportedEncodingException e) {Err_.Noop(e);} + return Io_url_.new_fil_(codeBase); + } +} diff --git a/100_core/src/gplx/core/envs/Op_sys.java b/100_core/src/gplx/core/envs/Op_sys.java new file mode 100644 index 000000000..a253f6e40 --- /dev/null +++ b/100_core/src/gplx/core/envs/Op_sys.java @@ -0,0 +1,96 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +public class Op_sys { + Op_sys(byte tid, byte sub_tid, String os_name, byte bitness, String nl_str, byte fsys_dir_spr_byte, boolean fsys_case_match) { + this.tid = tid; this.sub_tid = sub_tid; this.os_name = os_name; this.bitness = bitness; this.nl_str = nl_str; this.fsys_dir_spr_byte = fsys_dir_spr_byte; this.fsys_dir_spr_str = Char_.To_str((char)fsys_dir_spr_byte); this.fsys_case_match = fsys_case_match; + } + public byte Tid() {return tid;} private final byte tid; + public byte Sub_tid() {return sub_tid;} private final byte sub_tid; + public String Os_name() {return os_name;} private String os_name; + public byte Bitness() {return bitness;} private final byte bitness; + public String Bitness_str() {return (bitness == Bitness_32 ? "32" : "64");} + public String Nl_str() {return nl_str;} private final String nl_str; + public String Fsys_dir_spr_str() {return fsys_dir_spr_str;} private final String fsys_dir_spr_str; + public byte Fsys_dir_spr_byte() {return fsys_dir_spr_byte;} private final byte fsys_dir_spr_byte; + public String Fsys_http_frag_to_url_str(String raw) {return fsys_dir_spr_byte == Byte_ascii.Slash ? raw : String_.Replace(raw, Lnx.Fsys_dir_spr_str(), fsys_dir_spr_str);} + public boolean Fsys_case_match() {return fsys_case_match;} private final boolean fsys_case_match; + public String Fsys_case_match_str(String s) {return String_.CaseNormalize(fsys_case_match, s);} + public boolean Tid_is_wnt() {return tid == Tid_wnt;} + public boolean Tid_is_lnx() {return tid == Tid_lnx;} + public boolean Tid_is_osx() {return tid == Tid_osx;} + public boolean Tid_is_drd() {return tid == Tid_drd;} + public String To_str() {return os_name + Bitness_str();} + + public static final byte Tid_nil = 0, Tid_wnt = 1, Tid_lnx = 2, Tid_osx = 3, Tid_drd = 4, Tid_arm = 5; + public static final byte Sub_tid_unknown = 0, Sub_tid_win_xp = 1, Sub_tid_win_7 = 2, Sub_tid_win_8 = 3; + public static final byte Bitness_32 = 1, Bitness_64 = 2; + public static final char Nl_char_lnx = '\n'; + public static final byte Dir_spr__lnx = Byte_ascii.Slash, Dir_spr__wnt = Byte_ascii.Backslash; + public static final Op_sys Lnx = new_unx_flavor_(Tid_lnx, "linux", Bitness_32); + public static final Op_sys Osx = new_unx_flavor_(Tid_osx, "macosx", Bitness_32); + public static final Op_sys Drd = new_unx_flavor_(Tid_drd, "android", Bitness_32); + public static final Op_sys Wnt = new_wnt_(Sub_tid_unknown, Bitness_32); + public static Op_sys Cur() {return cur_op_sys;} static Op_sys cur_op_sys = new_auto_identify_(); + public static String Fsys_path_to_lnx(String v) { + return cur_op_sys.Tid_is_wnt() ? String_.Replace(v, Wnt.fsys_dir_spr_str, Lnx.fsys_dir_spr_str) : v; + } + public static String Fsys_path_to_wnt(String v) { + return cur_op_sys.Tid_is_wnt() ? String_.Replace(v, Lnx.fsys_dir_spr_str, Wnt.fsys_dir_spr_str) : v; + } + private static Op_sys new_wnt_(byte bitness, byte sub_tid) {return new Op_sys(Tid_wnt , sub_tid , "windows", bitness, "\r\n", Dir_spr__wnt , Bool_.N);} + private static Op_sys new_unx_flavor_(byte tid, String os_name, byte bitness) {return new Op_sys(tid , Sub_tid_unknown , os_name , bitness, "\n" , Dir_spr__lnx , Bool_.Y);} + public static void Cur_(int tid) { + switch (tid) { + case Tid_wnt: cur_op_sys = Wnt; break; + case Tid_lnx: cur_op_sys = Lnx; break; + case Tid_osx: cur_op_sys = Osx; break; + case Tid_drd: cur_op_sys = Drd; break; + default: throw Err_.new_unhandled_default(tid); + } + } + static final String GRP_KEY = "gplx.op_sys"; +// public static Op_sys Cur_() {cur_op_sys = new_auto_identify_(); return cur_op_sys;} + static Op_sys new_auto_identify_() { + String os_name = ""; + try { + String bitness_str = System.getProperty("sun.arch.data.model"); if (bitness_str == null) return Drd; + bitness_str = bitness_str.toLowerCase(); + byte bitness_byte = Bitness_32; + if (String_.Eq(bitness_str, "32")) bitness_byte = Bitness_32; + else if (String_.Eq(bitness_str, "64")) bitness_byte = Bitness_64; + else throw Err_.new_wo_type("unknown bitness; expecting 32 or 64; System.getProperty(\"bit.level\")", "val", bitness_str); + + os_name = System.getProperty("os.name").toLowerCase(); + String os_arch = System.getProperty("os.arch").toLowerCase(); + if (String_.Eq(os_arch, "arm")) return new_unx_flavor_(Tid_arm, os_name, bitness_byte); // EX:arm; DATE:2016-09-23;"arm" and "32" + if (String_.Has_at_bgn(os_name, "win")) { + String os_version = System.getProperty("os.version").toLowerCase();// "Windows 7".equals(osName) && "6.1".equals(osVersion); + byte sub_tid = Sub_tid_unknown; + if (String_.Eq(os_name, "windows xp") && String_.Eq(os_version, "5.1")) sub_tid = Sub_tid_win_xp; + else if (String_.Eq(os_name, "windows 7") && String_.Eq(os_version, "6.1")) sub_tid = Sub_tid_win_7; + else if (String_.Eq(os_name, "windows 8")) sub_tid = Sub_tid_win_8; + return new_wnt_(bitness_byte, sub_tid); + } + else if (String_.Eq(os_name, "linux")) return new_unx_flavor_(Tid_lnx, os_name, bitness_byte); + else if (String_.Has_at_bgn(os_name, "mac")) return new_unx_flavor_(Tid_osx, os_name, bitness_byte); // EX:Mac OS X + else throw Err_.new_wo_type("unknown os_name; expecting windows, linux, mac; System.getProperty(\"os.name\")", "val", os_name); + } catch (Exception exc) {Drd.os_name = os_name; return Drd;} + } + public static void Cur_is_drd_() {cur_op_sys = Drd;} + } diff --git a/100_core/src/gplx/core/envs/Op_sys_.java b/100_core/src/gplx/core/envs/Op_sys_.java new file mode 100644 index 000000000..ce6876a6e --- /dev/null +++ b/100_core/src/gplx/core/envs/Op_sys_.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +public class Op_sys_ { + public static boolean Wnt_invalid_char(byte b) { + switch (b) { + case Byte_ascii.Slash: + case Byte_ascii.Backslash: + case Byte_ascii.Lt: + case Byte_ascii.Gt: + case Byte_ascii.Colon: + case Byte_ascii.Pipe: + case Byte_ascii.Question: + case Byte_ascii.Star: + case Byte_ascii.Quote: return true; + default: return false; + } + } +} diff --git a/100_core/src/gplx/core/envs/Process_adp.java b/100_core/src/gplx/core/envs/Process_adp.java new file mode 100644 index 000000000..24c840d2e --- /dev/null +++ b/100_core/src/gplx/core/envs/Process_adp.java @@ -0,0 +1,379 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +import gplx.Bool_; +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Err_; +import gplx.Gfo_invk; +import gplx.Gfo_invk_cmd; +import gplx.Gfo_invk_; +import gplx.GfoMsg; +import gplx.Gfo_usr_dlg; +import gplx.Gfo_usr_dlg_; +import gplx.GfsCtx; +import gplx.Io_url; +import gplx.Io_url_; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.Rls_able; +import gplx.String_; +import gplx.Tfds; +import gplx.Virtual; +import gplx.core.threads.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import javax.management.RuntimeErrorException; +import gplx.core.brys.fmtrs.*; import gplx.core.strings.*; +import gplx.langs.gfs.*; +public class Process_adp implements Gfo_invk, Rls_able { + public boolean Enabled() {return enabled;} public Process_adp Enabled_(boolean v) {enabled = v; return this;} private boolean enabled = true; + public byte Exe_exists() {return exe_exists;} public Process_adp Exe_exists_(byte v) {exe_exists = v; return this;} private byte exe_exists = Bool_.__byte; + public Io_url Exe_url() {return exe_url;} public Process_adp Exe_url_(Io_url val) {exe_url = val; exe_exists = Bool_.__byte; return this;} Io_url exe_url; + public String Args_str() {return args_str;} public Process_adp Args_str_(String val) {args_str = val; return this;} private String args_str = ""; + public Bry_fmtr Args_fmtr() {return args_fmtr;} Bry_fmtr args_fmtr = Bry_fmtr.new_(""); + public boolean Args__include_quotes() {return args__include_quotes;} public void Args__include_quotes_(boolean v) {args__include_quotes = v;} private boolean args__include_quotes; + public byte Run_mode() {return run_mode;} public Process_adp Run_mode_(byte v) {run_mode = v; return this;} private byte run_mode = Run_mode_sync_block; + public static final byte Run_mode_async = 0, Run_mode_sync_block = 1, Run_mode_sync_timeout = 2; + public int Exit_code() {return exit_code;} int exit_code; + public boolean Exit_code_pass() {return exit_code == Exit_pass;} + public String Rslt_out() {return rslt_out;} private String rslt_out; + public Io_url Working_dir() {return working_dir;} public Process_adp Working_dir_(Io_url v) {working_dir = v; return this;} Io_url working_dir; + public Process_adp Cmd_args(String cmd, String args) {this.Exe_url_(Io_url_.new_fil_(cmd)); this.args_fmtr.Fmt_(args); return this;} + public Process_adp WhenBgn_add(Gfo_invk_cmd cmd) {whenBgnList.Add(cmd); return this;} + public Process_adp WhenBgn_del(Gfo_invk_cmd cmd) {whenBgnList.Del(cmd); return this;} + public int Thread_timeout() {return thread_timeout;} public Process_adp Thread_timeout_seconds_(int v) {thread_timeout = v * 1000; return this;} int thread_timeout = 0; + public int Thread_interval() {return thread_interval;} public Process_adp Thread_interval_(int v) {thread_interval = v; return this;} int thread_interval = 20; + public String Thread_kill_name() {return thread_kill_name;} public Process_adp Thread_kill_name_(String v) {thread_kill_name = v; return this;} private String thread_kill_name = ""; + public Io_url Tmp_dir() {return tmp_dir;} @gplx.Virtual public Process_adp Tmp_dir_(Io_url v) {tmp_dir = v; return this;} Io_url tmp_dir; + private Process_adp WhenBgn_run() {return Invk_cmds(whenBgnList);} List_adp whenBgnList = List_adp_.New(); + public Process_adp WhenEnd_add(Gfo_invk_cmd cmd) {whenEndList.Add(cmd); return this;} + public Process_adp WhenEnd_del(Gfo_invk_cmd cmd) {whenEndList.Del(cmd); return this;} + public Gfo_usr_dlg Prog_dlg() {return prog_dlg;} public Process_adp Prog_dlg_(Gfo_usr_dlg v) {prog_dlg = v; return this;} Gfo_usr_dlg prog_dlg; + public String Prog_fmt() {return prog_fmt;} public Process_adp Prog_fmt_(String v) {prog_fmt = v; return this;} private String prog_fmt = ""; // NOTE: set to "", else cmds that do not set prog_fmt will fail on fmtr.Fmt(null) + private Gfo_invk owner; + private Process_adp WhenEnd_run() {return Invk_cmds(whenEndList);} List_adp whenEndList = List_adp_.New(); + private Process_adp Invk_cmds(List_adp list) { + for (Object o : list) + ((Gfo_invk_cmd)o).Exec(); + return this; + } + public Process_adp Run(Object... args) { + if (String_.Len_eq_0(exe_url.Raw())) return this; // noop if exe_url is ""; + if (!args_fmtr.Fmt_null()) { + Bry_bfr tmp_bfr = Bry_bfr_.New(); + args_fmtr.Bld_bfr_many(tmp_bfr, args); + args_str = tmp_bfr.To_str_and_clear(); + } + prog_dlg.Log_many(GRP_KEY, "run", "running process: ~{0} ~{1}", exe_url.Raw(), args_str); + exit_code = Exit_init; + switch (run_mode) { + case Run_mode_async: return Run_async(); + case Run_mode_sync_timeout: return Run_wait(); + case Run_mode_sync_block: return Run_wait_sync(); + default: throw Err_.new_unhandled(run_mode); + } + } + public String[] Xto_process_bldr_args(String... args) { + String args_str = args_fmtr.Bld_str_many(args); + return To_process_bldr_args_utl(exe_url, args_str, false); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return enabled; + else if (ctx.Match(k, Invk_enabled_)) enabled = m.ReadBool("v"); + else if (ctx.Match(k, Invk_cmd)) return exe_url.Raw(); + else if (ctx.Match(k, Invk_cmd_)) this.Exe_url_(Bry_fmtr_eval_mgr_.Eval_url(cmd_url_eval, m.ReadBry("cmd"))); + else if (ctx.Match(k, Invk_args)) return String_.new_u8(args_fmtr.Fmt()); + else if (ctx.Match(k, Invk_args_)) args_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_cmd_args_)) {this.Exe_url_(Bry_fmtr_eval_mgr_.Eval_url(cmd_url_eval, m.ReadBry("cmd"))); args_fmtr.Fmt_(m.ReadBry("args"));} + else if (ctx.Match(k, Invk_mode_)) run_mode = m.ReadByte("v"); + else if (ctx.Match(k, Invk_timeout_)) thread_timeout = m.ReadInt("v"); + else if (ctx.Match(k, Invk_tmp_dir_)) tmp_dir = m.ReadIoUrl("v"); + else if (ctx.Match(k, Invk_owner)) return owner; + else return Gfo_invk_.Rv_unhandled; + return this; + } + static final String Invk_cmd = "cmd", Invk_cmd_ = "cmd_", Invk_args = "args", Invk_args_ = "args_", Invk_cmd_args_ = "cmd_args_", Invk_enabled = "enabled", Invk_enabled_ = "enabled_", Invk_mode_ = "mode_", Invk_timeout_ = "timeout_", Invk_tmp_dir_ = "tmp_dir_", Invk_owner = "owner"; + Bry_fmtr_eval_mgr cmd_url_eval; + public static Process_adp ini_(Gfo_invk owner, Gfo_usr_dlg usr_dlg, Process_adp process, Bry_fmtr_eval_mgr cmd_url_eval, byte run_mode, int timeout, String cmd_url_fmt, String args_fmt, String... args_keys) { + process.Run_mode_(run_mode).Thread_timeout_seconds_(timeout); + process.cmd_url_eval = cmd_url_eval; + Io_url cmd_url = Bry_fmtr_eval_mgr_.Eval_url(cmd_url_eval, Bry_.new_u8(cmd_url_fmt)); + process.Exe_url_(cmd_url).Tmp_dir_(cmd_url.OwnerDir()); + process.Args_fmtr().Fmt_(args_fmt).Keys_(args_keys); + process.owner = owner; + process.Prog_dlg_(usr_dlg); + return process; // return process for chaining + } + public static String Escape_ampersands_if_process_is_cmd(boolean os_is_wnt, String exe_url, String exe_args) { + return ( os_is_wnt + && String_.Eq(exe_url, "cmd")) + ? String_.Replace(exe_args, "&", "^&") // escape ampersands + : exe_args + ; + } + private Bry_fmtr notify_fmtr = Bry_fmtr.new_("", "process_exe_name", "process_exe_args", "process_seconds"); Bry_bfr notify_bfr = Bry_bfr_.Reset(255); + public Process UnderProcess() {return process;} Process process; + public void Rls() {if (process != null) process.destroy();} + public Process_adp Run_wait_sync() { + if (Env_.Mode_testing()) return Test_runs_add(); + Process_bgn(); + Process_start(); + Process_run_and_end(); + return this; + } + public Process_adp Run_start() { + if (Env_.Mode_testing()) return Test_runs_add(); + Process_bgn(); + Process_start(); + return this; + } + public Process_adp Run_async() { + if (Env_.Mode_testing()) return Test_runs_add(); + Process_bgn(); + Thread_ProcessAdp_async thread = new Thread_ProcessAdp_async(this); + thread.start(); + return this; + } + public Process_adp Run_wait() { + if (Env_.Mode_testing()) return Test_runs_add(); + int notify_interval = 100; int notify_checkpoint = notify_interval; + int elapsed = 0; + try { + Process_bgn(); + Thread_ProcessAdp_sync thread = new Thread_ProcessAdp_sync(this); + thread.start(); + // thread_timeout = 15000; + boolean thread_run = false; + notify_fmtr.Fmt_(prog_fmt); + while (thread.isAlive()) { + thread_run = true; + long prv = System_.Ticks(); + Thread_adp_.Sleep(thread_interval); +// try {thread.join(thread_interval);} +// catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread interrupted at join");} + long cur = System_.Ticks(); + int dif = (int)(cur - prv); + elapsed += dif; + if (prog_dlg != null) { + if (elapsed > notify_checkpoint) { + elapsed = notify_checkpoint; + notify_checkpoint += notify_interval; + notify_fmtr.Bld_bfr_many(notify_bfr, exe_url.NameAndExt(), args_str, elapsed / 1000); + prog_dlg.Prog_none(GRP_KEY, "notify.prog", notify_bfr.To_str_and_clear()); + } + } + if (thread_timeout == 0) break; + if (elapsed > thread_timeout) { + thread.interrupt(); + thread.Cancel(); + try {thread.join();} + catch (InterruptedException e) {throw Err_.new_exc(e, "core", "thread interrupted at timeout");} + break; + } + } + if (!thread_run) { + try {thread.join();} + catch (InterruptedException e) {throw Err_.new_exc(e, "core", "thread interrupted at join 2");} + } + } catch (Exception exc) { + Tfds.Write(Err_.Message_gplx_full(exc)); + } + if (elapsed != notify_checkpoint) { + notify_fmtr.Bld_bfr_many(notify_bfr, exe_url.NameAndExt(), args_str, elapsed / 1000); + if (prog_dlg != null) prog_dlg.Prog_none(GRP_KEY, "notify.prog", notify_bfr.To_str_and_clear()); + } + return this; + } + public synchronized void Process_post(String result) { + exit_code = process.exitValue(); + rslt_out = result; + WhenEnd_run(); + process.destroy(); + } + String Kill() { + if (thread_kill_name == String_.Empty) return ""; +// Runtime rt = Runtime.getRuntime(); + String kill_exe = "", kill_args = ""; + if (Op_sys.Cur().Tid_is_wnt()) { + kill_exe = "taskkill"; + kill_args = "/F /IM "; + } + else { + kill_exe = "kill"; + kill_args = "-9 "; + } + kill_args += thread_kill_name; + Process_adp kill_process = new Process_adp().Exe_url_(Io_url_.new_fil_(kill_exe)).Args_str_(kill_args).Thread_kill_name_(""); + boolean pass = kill_process.Run_wait().Exit_code_pass(); + return "killed|" + kill_exe + "|" + kill_args + "|" + pass + "|" + exe_url.Raw() + "|" + args_str; + } + synchronized void Process_bgn() { + exit_code = Exit_init; + rslt_out = ""; + WhenBgn_run(); + pb = new ProcessBuilder(To_process_bldr_args_utl(exe_url, args_str, args__include_quotes)); + pb.redirectErrorStream(true); // NOTE: need to redirectErrorStream or rdr.readLine() will hang; see inkscape and Ostfriesland Verkehr-de.svg + if (working_dir != null) + pb.directory(new File(working_dir.Xto_api())); + else if (!exe_url.OwnerDir().EqNull()) // only set workingDir if ownerDir is not null; NOTE: workingDir necessary for AdvMame; probably not a bad thing to do + pb.directory(new File(exe_url.OwnerDir().Xto_api())); + } ProcessBuilder pb; + protected Process Process_start() { + try {process = pb.start();} + catch (IOException e) { + java.util.List command_list = pb.command(); + String[] command_ary = new String[command_list.size()]; + command_ary = command_list.toArray(command_ary); + throw Err_.new_exc(e, "core", "process start failed", "args", String_.Concat_with_str(" ", command_ary)); + } + return process; + } + void Process_run_and_end() { + String_bldr sb = String_bldr_.new_(); + BufferedReader rdr = new BufferedReader(new InputStreamReader(process.getInputStream())); + try { + String line = ""; + while ((line = rdr.readLine()) != null) + sb.Add_str_w_crlf(line); + process.waitFor(); + } + catch (InterruptedException e) {throw Err_.new_exc(e, "core", "thread interrupted at wait_for", "exe_url", exe_url.Xto_api(), "exeArgs", args_str);} + catch (IOException e) {throw Err_.new_exc(e, "core", "io error", "exe_url", exe_url.Xto_api(), "exeArgs", args_str);} + exit_code = process.exitValue(); + WhenEnd_run(); + process.destroy(); + rslt_out = sb.To_str_and_clear(); + } + public void Process_term() { + try { + process.getInputStream().close(); + process.getErrorStream().close(); + } catch (IOException e) {} + process.destroy(); + } + public static void run_wait_(Io_url url) { + Process_adp process = new Process_adp().Exe_url_(url); + process.Run_start(); + process.Process_run_and_end(); + return; + } + public static final List_adp Test_runs = List_adp_.New(); + private Process_adp Test_runs_add() {Test_runs.Add(exe_url.Raw() + " " + args_str); exit_code = Exit_pass; return this;} + public static int run_wait_arg_(Io_url url, String arg) { + Process_adp process = new Process_adp(); + process.Exe_url_(url).Args_str_(arg).Run_wait(); + return process.Exit_code(); + } + private static final String GRP_KEY = "gplx.process"; + public static final int Exit_pass = 0, Exit_init = -1; + public static String[] To_process_bldr_args_utl(Io_url exe_url, String args_str, boolean include_quotes) { + List_adp list = List_adp_.New(); + list.Add(exe_url.Xto_api()); + String_bldr sb = String_bldr_.new_(); + int len = String_.Len(args_str); + boolean in_quotes = false; + for (int i = 0; i < len; i++) { + char c = String_.CharAt(args_str, i); + if (c == ' ' && !in_quotes) { // space encountered; assume arg done + list.Add(sb.To_str()); + sb.Clear(); + } + else if (c == '"') { // NOTE: ProcessBuilder seems to have issues with quotes; do not call sb.Add() + in_quotes = !in_quotes; + if (include_quotes) + sb.Add(c); + } + else + sb.Add(c); + } + if (sb.Has_some()) list.Add(sb.To_str()); + return list.To_str_ary(); + } +} +class Thread_ProcessAdp_async extends Thread { + public Thread_ProcessAdp_async(Process_adp process_adp) {this.process_adp = process_adp;} Process_adp process_adp; + public boolean Done() {return done;} boolean done = false; + public void Cancel() {process_adp.UnderProcess().destroy();} + public void run() { + process_adp.Run_wait(); + } +} +class Thread_ProcessAdp_sync extends Thread { + public Thread_ProcessAdp_sync(Process_adp process_adp) {this.process_adp = process_adp;} private final Process_adp process_adp; + public boolean Done() {return done;} private boolean done = false; + public void Cancel() { + process_adp.UnderProcess().destroy(); + } + public synchronized void run() { + done = false; + try { + Process process = process_adp.Process_start(); + StreamGobbler input_gobbler = new StreamGobbler("input", process.getInputStream()); + StreamGobbler error_gobbler = new StreamGobbler("error", process.getErrorStream()); + input_gobbler.start(); + error_gobbler.start(); + try {process.waitFor();} + catch (InterruptedException e) { + this.Cancel(); + String kill_rslt = process_adp.Kill(); + process_adp.Process_post(kill_rslt); + done = false; + return; + } + while (input_gobbler.isAlive()) { + try {input_gobbler.join(50);} + catch (InterruptedException e) {throw Err_.new_exc(e, "core", "thread interrupted at input gobbler");} + } + while (error_gobbler.isAlive()) { + try {error_gobbler.join(50);} + catch (InterruptedException e) {throw Err_.new_exc(e, "core", "thread interrupted at error gobbler");} + } + String result = input_gobbler.Rslt() + "\n" + error_gobbler.Rslt(); + process_adp.Process_post(result); + } catch (Exception e) { // NOTE: warn; do not throw, else multiple errors if timidity not available; PAGE:fr.u:Pentatoniques_altérées/Gammes_avec_deux_notes_altérées DATE:2015-05-08 + Gfo_usr_dlg_.Instance.Warn_many("", "", "process.sync failed; cmd=~{0} args=~{1}", process_adp.Exe_url().Raw(), process_adp.Args_str()); + } + finally {done = true;} + } +} +class StreamGobbler extends Thread { + private final String name; private final InputStream stream; + public StreamGobbler (String name, InputStream stream) {this.name = name; this.stream = stream;} + public String Rslt() {return rslt;} private String rslt; + public void run () { + try { + String_bldr sb = String_bldr_.new_(); + InputStreamReader isr = new InputStreamReader(stream); + BufferedReader br = new BufferedReader(isr); + while (true) { + String s = br.readLine(); + if (s == null) break; + sb.Add(s); + } + stream.close(); + rslt = sb.To_str_and_clear(); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "failed reading stream", "name", name);} + } +} diff --git a/100_core/src/gplx/core/envs/Process_adp_tst.java b/100_core/src/gplx/core/envs/Process_adp_tst.java new file mode 100644 index 000000000..07193ad2d --- /dev/null +++ b/100_core/src/gplx/core/envs/Process_adp_tst.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Process_adp_tst { + private Process_adp_fxt fxt = new Process_adp_fxt(); + @Test public void Escape_ampersands_if_process_is_cmd() { + fxt.Test_Escape_ampersands_if_process_is_cmd(Bool_.Y, "cmd" , "/c \"http://a.org?b=c&d=e\"", "/c \"http://a.org?b=c^&d=e\""); + fxt.Test_Escape_ampersands_if_process_is_cmd(Bool_.Y, "cmd1", "/c \"http://a.org?b=c&d=e\"", "/c \"http://a.org?b=c&d=e\""); + fxt.Test_Escape_ampersands_if_process_is_cmd(Bool_.N, "cmd" , "/c \"http://a.org?b=c&d=e\"", "/c \"http://a.org?b=c&d=e\""); + } +} +class Process_adp_fxt { + public void Test_Escape_ampersands_if_process_is_cmd(boolean os_is_wnt, String exe_url, String exe_args, String expd) { + Tfds.Eq(expd, Process_adp.Escape_ampersands_if_process_is_cmd(os_is_wnt, exe_url, exe_args)); + } +} diff --git a/100_core/src/gplx/core/envs/Runtime_.java b/100_core/src/gplx/core/envs/Runtime_.java new file mode 100644 index 000000000..34d397c12 --- /dev/null +++ b/100_core/src/gplx/core/envs/Runtime_.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +public class Runtime_ { + // *** Hardware-related + public static int Cpu_count() {return Runtime.getRuntime().availableProcessors();} + public static long Memory_max() {return Runtime.getRuntime().maxMemory();} + public static long Memory_total() {return Runtime.getRuntime().totalMemory();} + public static long Memory_free() {return Runtime.getRuntime().freeMemory();} +} \ No newline at end of file diff --git a/100_core/src/gplx/core/envs/System_.java b/100_core/src/gplx/core/envs/System_.java new file mode 100644 index 000000000..f27b246df --- /dev/null +++ b/100_core/src/gplx/core/envs/System_.java @@ -0,0 +1,63 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.envs; import gplx.*; import gplx.core.*; +public class System_ { + // *** ticks + public static long Ticks() {return Ticks__test_val >= 0 ? Ticks__test_val : System.currentTimeMillis();} + public static int Ticks__elapsed_in_sec (long time_bgn) {return (int)(Ticks() - time_bgn) / 1000;} + public static int Ticks__elapsed_in_frac (long time_bgn) {return (int)(Ticks() - time_bgn);} + public static void Ticks__test_set(long v) {Ticks__test_val = v;} + public static void Ticks__test_add(long v) {Ticks__test_val += v;} + public static void Ticks__test_off() {Ticks__test_val = -1;} + private static long Ticks__test_val = -1; // in milliseconds + + // *** misc methods: Exit / GC + public static void Exit() {Exit_by_code(0);} + public static void Exit_by_code(int code) {System.exit(code);} + public static void Garbage_collect() {if (Env_.Mode_testing()) return; System.gc();} + + // *** java properties: getProperty; "-D" properties + public static String Prop__user_language() {return Prop__get(Prop_key__user_language);} + public static String Prop__user_name() {return Prop__get(Prop_key__user_name);} + public static String Prop__java_version() {return Prop__get(Prop_key__java_version);} + public static String Prop__java_home() {return Prop__get(Prop_key__java_home);} + private static String Prop__get(String key) { + return System.getProperty(key); + } + private static final String + Prop_key__user_language = "user.language" + , Prop_key__user_name = "user.name" + , Prop_key__java_version = "java.version" + , Prop_key__java_home = "java.version" + ; + + // *** environment variables: getenv + public static String Env__machine_name() { + String rv = ""; + rv = Env__get(Env_key__computername); if (String_.Len_gt_0(rv)) return rv; + rv = Env__get(Env_key__hostname); if (String_.Len_gt_0(rv)) return rv; + return "UNKNOWN_MACHINE_NAME"; + } + private static String Env__get(String key) { + return System.getenv(key); + } + private static final String + Env_key__computername = "COMPUTERNAME" + , Env_key__hostname = "HOSTNAME" + ; +} diff --git a/100_core/src/gplx/core/errs/Err_msg.java b/100_core/src/gplx/core/errs/Err_msg.java new file mode 100644 index 000000000..62aa38b08 --- /dev/null +++ b/100_core/src/gplx/core/errs/Err_msg.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.errs; import gplx.*; import gplx.core.*; +public class Err_msg { + private final String msg; private Object[] args; + public Err_msg(String type, String msg, Object[] args) { + this.type = type; + this.msg = msg; + this.args = args; + } + public String Type() {return type;} private final String type; + public void Args_add(Object[] add) { + this.args = (Object[])Array_.Resize_add(args, add); + } + public String To_str() {return To_str_w_type(type, msg, args);} + public String To_str_wo_type() {return To_str(msg, args);} + public String To_str_wo_args() {return To_str(msg);} + public static String To_str(String msg, Object... args) {return To_str_w_type(null, msg, args);} + public static String To_str_w_type(String type, String msg, Object... args) { + String rv = String_.Len_eq_0(type) ? "" : "<" + type + "> "; + rv += msg; + int len = args.length; + if (len > 0) { + rv += ":"; + for (int i = 0; i < len; i += 2) { + Object key = args[i]; + Object val = i + 1 < len ? args[i + 1] : "MISSING_VAL"; + rv += " " + Object_.Xto_str_strict_or_null_mark(key) + "=" + Object_.Xto_str_strict_or_null_mark(val); + } + } + return rv; + } +} diff --git a/100_core/src/gplx/core/gfo_ndes/GfoFld.java b/100_core/src/gplx/core/gfo_ndes/GfoFld.java new file mode 100644 index 000000000..36b84567f --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoFld.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.type_xtns.*; +public class GfoFld { + public String Key() {return key;} private String key; + public ClassXtn Type() {return type;} ClassXtn type; + public static final GfoFld Null = new_(String_.Null_mark, ObjectClassXtn.Instance); + public static GfoFld new_(String key, ClassXtn c) { + GfoFld rv = new GfoFld(); + rv.key = key; rv.type = c; + return rv; + } +} diff --git a/100_core/src/gplx/core/gfo_ndes/GfoFldList.java b/100_core/src/gplx/core/gfo_ndes/GfoFldList.java new file mode 100644 index 000000000..2ee4773ea --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoFldList.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.type_xtns.*; +public interface GfoFldList { + int Count(); + boolean Has(String key); + int Idx_of(String key); + GfoFld Get_at(int i); + GfoFld FetchOrNull(String key); + GfoFldList Add(String key, ClassXtn c); + String To_str(); +} diff --git a/100_core/src/gplx/core/gfo_ndes/GfoFldList_.java b/100_core/src/gplx/core/gfo_ndes/GfoFldList_.java new file mode 100644 index 000000000..c396d0fc9 --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoFldList_.java @@ -0,0 +1,63 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; import gplx.core.type_xtns.*; +public class GfoFldList_ { + public static final GfoFldList Null = new GfoFldList_null(); + public static GfoFldList new_() {return new GfoFldList_base();} + public static GfoFldList str_(String... names) { + GfoFldList rv = new GfoFldList_base(); + for (String name : names) + rv.Add(name, StringClassXtn.Instance); + return rv; + } +} +class GfoFldList_base implements GfoFldList { + public int Count() {return hash.Count();} + public boolean Has(String key) {return hash.Has(key);} + public int Idx_of(String key) { + Object rv = idxs.Get_by(key); + return rv == null ? List_adp_.Not_found : Int_.cast(rv); + } + public GfoFld Get_at(int i) {return (GfoFld)hash.Get_at(i);} + public GfoFld FetchOrNull(String key) {return (GfoFld)hash.Get_by(key);} + public GfoFldList Add(String key, ClassXtn c) { + GfoFld fld = GfoFld.new_(key, c); + hash.Add(key, fld); + idxs.Add(key, idxs.Count()); + return this; + } + public String To_str() { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < hash.Count(); i++) { + GfoFld fld = this.Get_at(i); + sb.Add(fld.Key()).Add("|"); + } + return sb.To_str(); + } + Ordered_hash hash = Ordered_hash_.New(); Hash_adp idxs = Hash_adp_.New(); // PERF: idxs used for Idx_of; need to recalc if Del ever added +} +class GfoFldList_null implements GfoFldList { + public int Count() {return 0;} + public boolean Has(String key) {return false;} + public int Idx_of(String key) {return List_adp_.Not_found;} + public GfoFld Get_at(int i) {return GfoFld.Null;} + public GfoFld FetchOrNull(String key) {return null;} + public GfoFldList Add(String key, ClassXtn typx) {return this;} + public String To_str() {return "<>";} +} \ No newline at end of file diff --git a/100_core/src/gplx/core/gfo_ndes/GfoNde.java b/100_core/src/gplx/core/gfo_ndes/GfoNde.java new file mode 100644 index 000000000..7561405a4 --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoNde.java @@ -0,0 +1,72 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; import gplx.core.stores.*; +public class GfoNde implements Gfo_invk { + public GfoFldList Flds() {return flds;} GfoFldList flds; + public Hash_adp EnvVars() {return envVars;} Hash_adp envVars = Hash_adp_.New(); + public String Name() {return name;} public GfoNde Name_(String v) {name = v; return this;} private String name; + public Object ReadAt(int i) {ChkIdx(i); return ary[i];} + public void WriteAt(int i, Object val) {ChkIdx(i); ary[i] = val;} + public Object Read(String key) {int i = IndexOfOrFail(key); return ary[i];} + public void Write(String key, Object val) {int i = IndexOfOrFail(key); ary[i] = val;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return Read(k);} + + public GfoNdeList Subs() {return subs;} GfoNdeList subs = GfoNdeList_.new_(); + public GfoFldList SubFlds() {return subFlds;} GfoFldList subFlds = GfoFldList_.new_(); + public void XtoStr_wtr(DataWtr wtr) {XtoStr_wtr(this, wtr);}// TEST + public String To_str() { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < aryLen; i++) { + String key = i >= flds.Count() ? "<< NULL " + i + " >>" : flds.Get_at(i).Key(); + String val = i >= aryLen ? "<< NULL " + i + " >>" : Object_.Xto_str_strict_or_null_mark(ary[i]); + sb.Add(key).Add("=").Add(val); + } + return sb.To_str(); + } + int IndexOfOrFail(String key) { + int i = flds.Idx_of(key); + if ((i < 0 || i >= aryLen)) throw Err_.new_wo_type("field name not found", "name", key, "index", i, "count", this.Flds().Count()); + return i; + } + boolean ChkIdx(int i) {if (i < 0 || i >= aryLen) throw Err_.new_missing_idx(i, aryLen); return true;} + Object[] ary; int type; int aryLen; + @gplx.Internal protected GfoNde(int type, String name, GfoFldList flds, Object[] ary, GfoFldList subFlds, GfoNde[] subAry) { + this.type = type; this.name = name; this.flds = flds; this.ary = ary; aryLen = Array_.Len(ary); this.subFlds = subFlds; + for (GfoNde sub : subAry) + subs.Add(sub); + } + static void XtoStr_wtr(GfoNde nde, DataWtr wtr) { + if (nde.type == GfoNde_.Type_Leaf) { + wtr.WriteLeafBgn("flds"); + for (int i = 0; i < nde.ary.length; i++) + wtr.WriteData(nde.Flds().Get_at(i).Key(), nde.ReadAt(i)); + wtr.WriteLeafEnd(); + } + else { + if (nde.type == GfoNde_.Type_Node) // never write node info for root + wtr.WriteTableBgn(nde.Name(), nde.SubFlds()); + for (int i = 0; i < nde.Subs().Count(); i++) { + GfoNde sub = nde.Subs().FetchAt_asGfoNde(i); + XtoStr_wtr(sub, wtr); + } + if (nde.type == GfoNde_.Type_Node) + wtr.WriteNodeEnd(); + } + } +} diff --git a/100_core/src/gplx/core/gfo_ndes/GfoNdeFxt.java b/100_core/src/gplx/core/gfo_ndes/GfoNdeFxt.java new file mode 100644 index 000000000..56418fc57 --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoNdeFxt.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.type_xtns.*; +public class GfoNdeFxt { + public GfoNde root_(GfoNde... subs) {return GfoNde_.root_(subs);} + public GfoNde tbl_(String name, GfoNde... rows) {return GfoNde_.tbl_(name, GfoFldList_.Null, rows);} + public GfoNde nde_(String name, GfoFldList flds, GfoNde... subs) {return GfoNde_.tbl_(name, flds, subs);} + public GfoNde row_(GfoFldList flds, Object... vals) {return GfoNde_.vals_(flds, vals);} + public GfoNde row_vals_(Object... vals) {return GfoNde_.vals_(GfoFldList_by_count_(vals.length), vals);} + public GfoNde csv_dat_(GfoNde... rows) {return GfoNde_.tbl_("", GfoFldList_.Null, rows);} + public GfoNde csv_hdr_(GfoFldList flds, GfoNde... rows) {return GfoNde_.tbl_("", flds, rows);} + public static GfoNdeFxt new_() {return new GfoNdeFxt();} + + static GfoFldList GfoFldList_by_count_(int count) { + GfoFldList rv = GfoFldList_.new_(); + for (int i = 0; i < count; i++) + rv.Add("fld" + Int_.To_str(i), StringClassXtn.Instance); + return rv; + } +} diff --git a/100_core/src/gplx/core/gfo_ndes/GfoNdeList.java b/100_core/src/gplx/core/gfo_ndes/GfoNdeList.java new file mode 100644 index 000000000..85c4c5038 --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoNdeList.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.lists.*; /*ComparerAble*/ +public interface GfoNdeList { + int Count(); + GfoNde FetchAt_asGfoNde(int index); + void Add(GfoNde rcd); + void Del(GfoNde rcd); + void Clear(); + void Sort_by(ComparerAble comparer); +} diff --git a/100_core/src/gplx/core/gfo_ndes/GfoNdeList_.java b/100_core/src/gplx/core/gfo_ndes/GfoNdeList_.java new file mode 100644 index 000000000..d2ebe7639 --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoNdeList_.java @@ -0,0 +1,40 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.lists.*; /*ComparerAble*/ +public class GfoNdeList_ { + public static final GfoNdeList Null = new GfoNdeList_null(); + public static GfoNdeList new_() {return new GfoNdeList_base();} +} +class GfoNdeList_base implements GfoNdeList { + public int Count() {return list.Count();} + public GfoNde FetchAt_asGfoNde(int i) {return (GfoNde)list.Get_at(i);} + public void Add(GfoNde rcd) {list.Add(rcd);} + public void Del(GfoNde rcd) {list.Del(rcd);} + public void Clear() {list.Clear();} + public void Sort_by(ComparerAble comparer) {list.Sort_by(comparer);} + List_adp list = List_adp_.New(); +} +class GfoNdeList_null implements GfoNdeList { + public int Count() {return 0;} + public GfoNde FetchAt_asGfoNde(int index) {return null;} + public void Add(GfoNde rcd) {} + public void Del(GfoNde rcd) {} + public void Clear() {} + public void Sort_by(ComparerAble comparer) {} +} diff --git a/100_core/src/gplx/core/gfo_ndes/GfoNde_.java b/100_core/src/gplx/core/gfo_ndes/GfoNde_.java new file mode 100644 index 000000000..3b376a9b9 --- /dev/null +++ b/100_core/src/gplx/core/gfo_ndes/GfoNde_.java @@ -0,0 +1,47 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_ndes; import gplx.*; import gplx.core.*; +import gplx.core.type_xtns.*; import gplx.core.stores.*; +public class GfoNde_ { + public static final GfoNde[] Ary_empty = new GfoNde[0]; + public static GfoNde[] ary_(GfoNde... ary) {return ary;} + public static GfoNde as_(Object obj) {return obj instanceof GfoNde ? (GfoNde)obj : null;} + public static GfoNde root_(GfoNde... subs) {return new GfoNde(GfoNde_.Type_Root, "RootName", GfoFldList_.Null, Object_.Ary_empty, GfoFldList_.Null, subs);} + public static GfoNde tbl_(String name, GfoFldList flds, GfoNde... rows) {return new GfoNde(GfoNde_.Type_Node, name, flds, Object_.Ary_empty, flds, rows);} + public static GfoNde vals_(GfoFldList flds, Object[] ary) {return new GfoNde(GfoNde_.Type_Leaf, "row", flds, ary, GfoFldList_.Null, Ary_empty);} + public static GfoNde vals_params_(GfoFldList flds, Object... ary) {return new GfoNde(GfoNde_.Type_Leaf, "row", flds, ary, GfoFldList_.Null, Ary_empty);} + public static GfoNde nde_(String name, Object[] ary, GfoNde... subs) {return new GfoNde(GfoNde_.Type_Node, name, GfoFldList_.Null, ary, GfoFldList_.Null, subs);} + public static GfoNde rdr_(DataRdr rdr) { + try { + List_adp rows = List_adp_.New(); + GfoFldList flds = GfoFldList_.new_(); + int fldLen = rdr.FieldCount(); + for (int i = 0; i < fldLen; i++) + flds.Add(rdr.KeyAt(i), ObjectClassXtn.Instance); + while (rdr.MoveNextPeer()) { + Object[] valAry = new Object[fldLen]; + for (int i = 0; i < fldLen; i++) + valAry[i] = rdr.ReadAt(i); + rows.Add(GfoNde_.vals_(flds, valAry)); + } + return GfoNde_.tbl_("", flds, (GfoNde[])rows.To_ary(GfoNde.class)); + } + finally {rdr.Rls();} + } + @gplx.Internal protected static final int Type_Leaf = 1, Type_Node = 2, Type_Root = 3; +} diff --git a/100_core/src/gplx/core/gfo_regys/GfoMsgParser.java b/100_core/src/gplx/core/gfo_regys/GfoMsgParser.java new file mode 100644 index 000000000..5e940cf5d --- /dev/null +++ b/100_core/src/gplx/core/gfo_regys/GfoMsgParser.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_regys; import gplx.*; import gplx.core.*; +public interface GfoMsgParser { + GfoMsg ParseToMsg(String s); +} diff --git a/100_core/src/gplx/core/gfo_regys/GfoRegy.java b/100_core/src/gplx/core/gfo_regys/GfoRegy.java new file mode 100644 index 000000000..32cf48076 --- /dev/null +++ b/100_core/src/gplx/core/gfo_regys/GfoRegy.java @@ -0,0 +1,95 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_regys; import gplx.*; import gplx.core.*; +import gplx.langs.gfs.*; import gplx.core.type_xtns.*; import gplx.core.interfaces.*; +public class GfoRegy implements Gfo_invk { + public int Count() {return hash.Count();} + public Hash_adp Parsers() {return parsers;} Hash_adp parsers = Hash_adp_.New(); + public GfoRegyItm FetchOrNull(String key) {return (GfoRegyItm)hash.Get_by(key);} + public Object FetchValOrFail(String key) { + GfoRegyItm rv = (GfoRegyItm)hash.Get_by(key); if (rv == null) throw Err_.new_wo_type("regy does not have key", "key", key); + return rv.Val(); + } + public Object FetchValOrNull(String key) {return FetchValOr(key, null);} + public Object FetchValOr(String key, Object or) { + GfoRegyItm itm = FetchOrNull(key); + return itm == null ? or : itm.Val(); + } + public void Del(String key) {hash.Del(key);} + public void RegObj(String key, Object val) {RegItm(key, val, GfoRegyItm.ValType_Obj, Io_url_.Empty);} + public void RegDir(Io_url dirUrl, String match, boolean recur, String chopBgn, String chopEnd) { + Io_url[] filUrls = Io_mgr.Instance.QueryDir_args(dirUrl).FilPath_(match).Recur_(recur).ExecAsUrlAry(); + if (filUrls.length == 0 && !Io_mgr.Instance.ExistsDir(dirUrl)) {UsrDlg_.Instance.Stop(UsrMsg.new_("dirUrl does not exist").Add("dirUrl", dirUrl.Xto_api())); return;} + for (Io_url filUrl : filUrls) { + String key = filUrl.NameAndExt(); + int pos = String_.Find_none; + if (String_.EqNot(chopBgn, "")) { + pos = String_.FindFwd(key, chopBgn); + if (pos == String_.Len(key) - 1) + throw Err_.new_wo_type(Err_ChopBgn, "key", key, "chopBgn", chopBgn); + else if (pos != String_.Find_none) + key = String_.Mid(key, pos + 1); + } + if (String_.EqNot(chopEnd, "")) { + pos = String_.FindBwd(key, chopEnd); + if (pos == 0) + throw Err_.new_wo_type(Err_ChopEnd, "key", key, "chopEnd", chopEnd); + else if (pos != String_.Find_none) + key = String_.MidByLen(key, 0, pos); + } + if (hash.Has(key)) throw Err_.new_wo_type(Err_Dupe, "key", key, "filUrl", filUrl); + RegItm(key, null, GfoRegyItm.ValType_Url, filUrl); + } + } + public void RegObjByType(String key, String val, String type) { + Object o = val; + if (String_.EqNot(type, StringClassXtn.Key_const)) { + ParseAble parser = (ParseAble)parsers.Get_by(type); + if (parser == null) throw Err_.new_wo_type("could not find parser", "type", type, "key", key, "val", val); + o = parser.ParseAsObj(val); + } + RegItm(key, o, GfoRegyItm.ValType_Obj, Io_url_.Empty); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_RegDir)) { + Io_url dir = m.ReadIoUrl("dir"); + String match = m.ReadStrOr("match", "*.*"); + boolean recur = m.ReadBoolOr("recur", false); + String chopBgn = m.ReadStrOr("chopBgn", ""); + String chopEnd = m.ReadStrOr("chopEnd", "."); + if (ctx.Deny()) return this; + RegDir(dir, match, recur, chopBgn, chopEnd); + } + else if (ctx.Match(k, Invk_RegObj)) { + String key = m.ReadStr("key"); + String val = m.ReadStr("val"); + String type = m.ReadStrOr("type", StringClassXtn.Key_const); + if (ctx.Deny()) return this; + RegObjByType(key, val, type); + } + else return Gfo_invk_.Rv_unhandled; + return this; + } public static final String Invk_RegDir = "RegDir", Invk_RegObj = "RegObj"; + void RegItm(String key, Object val, int valType, Io_url url) { + hash.Add_if_dupe_use_nth(key, new GfoRegyItm(key, val, valType, url)); + } + Hash_adp hash = Hash_adp_.New(); + public static final String Err_ChopBgn = "chopBgn results in null key", Err_ChopEnd = "chopEnd results in null key", Err_Dupe = "key already registered"; + public static final GfoRegy Instance = new GfoRegy(); GfoRegy() {} + @gplx.Internal protected static GfoRegy new_() {return new GfoRegy();} +} diff --git a/100_core/src/gplx/core/gfo_regys/GfoRegyItm.java b/100_core/src/gplx/core/gfo_regys/GfoRegyItm.java new file mode 100644 index 000000000..aeb235994 --- /dev/null +++ b/100_core/src/gplx/core/gfo_regys/GfoRegyItm.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_regys; import gplx.*; import gplx.core.*; +public class GfoRegyItm { + public String Key() {return key;} private String key; + public Object Val() {return val;} Object val; + public Io_url Url() {return url;} Io_url url; + public int ValType() {return valType;} int valType; + public GfoRegyItm(String key, Object val, int valType, Io_url url) {this.key = key; this.val = val; this.valType = valType; this.url = url;} + + public static final int + ValType_Obj = 1 + , ValType_Url = 2 + , ValType_B64 = 3 + ; +} diff --git a/100_core/src/gplx/core/gfo_regys/GfoRegy_RegDir_tst.java b/100_core/src/gplx/core/gfo_regys/GfoRegy_RegDir_tst.java new file mode 100644 index 000000000..155c9e914 --- /dev/null +++ b/100_core/src/gplx/core/gfo_regys/GfoRegy_RegDir_tst.java @@ -0,0 +1,61 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_regys; import gplx.*; import gplx.core.*; +import org.junit.*; +public class GfoRegy_RegDir_tst { + @Before public void setup() { + regy = GfoRegy.new_(); + Io_mgr.Instance.InitEngine_mem(); + root = Io_url_.mem_dir_("mem/root"); + } GfoRegy regy; Io_url root; + @Test public void Basic() { + ini_fil("101_tsta.txt"); + ini_fil("102_tstb.txt"); + ini_fil("103_tstc.png"); + ini_fil("dir1", "104_tstd.txt"); + regy.RegDir(root, "*.txt", false, "_", "."); + tst_Count(2); + tst_Exists("tsta"); + tst_Exists("tstb"); + } + @Test public void Err_dupe() { + ini_fil("101_tsta.txt"); + ini_fil("102_tsta.txt"); + try {regy.RegDir(root, "*.txt", false, "_", ".");} + catch (Exception e) {Tfds.Err_has(e, GfoRegy.Err_Dupe); return;} + Tfds.Fail_expdError(); + } + @Test public void Err_chopBgn() { + ini_fil("123_"); + try {regy.RegDir(root, "*", false, "_", ".");} + catch (Exception e) {Tfds.Err_has(e, GfoRegy.Err_ChopBgn); return;} + Tfds.Fail_expdError(); + } + @Test public void Err_chopEnd() { + ini_fil(".txt"); + try {regy.RegDir(root, "*.txt", false, "_", ".");} + catch (Exception e) {Tfds.Err_has(e, GfoRegy.Err_ChopEnd); return;} + Tfds.Fail_expdError(); + } + void tst_Count(int expd) {Tfds.Eq(expd, regy.Count());} + void tst_Exists(String expd) { + GfoRegyItm itm = regy.FetchOrNull(expd); + Tfds.Eq_nullNot(itm); + } + void ini_fil(String... nest) {Io_mgr.Instance.SaveFilStr(root.GenSubFil_nest(nest), "");} +} diff --git a/100_core/src/gplx/core/gfo_regys/GfoRegy_basic_tst.java b/100_core/src/gplx/core/gfo_regys/GfoRegy_basic_tst.java new file mode 100644 index 000000000..5dd32d923 --- /dev/null +++ b/100_core/src/gplx/core/gfo_regys/GfoRegy_basic_tst.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.gfo_regys; import gplx.*; import gplx.core.*; +import org.junit.*; +public class GfoRegy_basic_tst { + @Before public void setup() { + regy = GfoRegy.new_(); + } GfoRegy regy; + @Test public void RegObjByType() { + regy.Parsers().Add("Io_url", Io_url_.Parser); + Io_url expd = Io_url_.new_any_("C:\\fil.txt"); + regy.RegObjByType("test", expd.Xto_api(), "Io_url"); + Io_url actl = (Io_url)regy.FetchValOr("test", Io_url_.Empty); + Tfds.Eq(expd.Xto_api(), actl.Xto_api()); + } +} diff --git a/100_core/src/gplx/core/interfaces/InjectAble.java b/100_core/src/gplx/core/interfaces/InjectAble.java new file mode 100644 index 000000000..492648d9c --- /dev/null +++ b/100_core/src/gplx/core/interfaces/InjectAble.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.interfaces; import gplx.*; import gplx.core.*; +public interface InjectAble { + void Inject(Object owner); +} diff --git a/100_core/src/gplx/core/interfaces/ParseAble.java b/100_core/src/gplx/core/interfaces/ParseAble.java new file mode 100644 index 000000000..ea4fe23c5 --- /dev/null +++ b/100_core/src/gplx/core/interfaces/ParseAble.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.interfaces; import gplx.*; import gplx.core.*; +public interface ParseAble { + Object ParseAsObj(String raw); +} diff --git a/100_core/src/gplx/core/interfaces/SrlAble.java b/100_core/src/gplx/core/interfaces/SrlAble.java new file mode 100644 index 000000000..2f9077fcb --- /dev/null +++ b/100_core/src/gplx/core/interfaces/SrlAble.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.interfaces; import gplx.*; import gplx.core.*; +public interface SrlAble { + Object Srl(GfoMsg owner); +} diff --git a/100_core/src/gplx/core/interfaces/SrlAble_.java b/100_core/src/gplx/core/interfaces/SrlAble_.java new file mode 100644 index 000000000..48be1449c --- /dev/null +++ b/100_core/src/gplx/core/interfaces/SrlAble_.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.interfaces; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; +public class SrlAble_ { + public static SrlAble as_(Object obj) {return obj instanceof SrlAble ? (SrlAble)obj : null;} + public static String To_str(GfoMsg owner) { + String_bldr sb = String_bldr_.new_(); + To_str(owner, sb, 0, false); + return sb.To_str(); + } + public static String To_str(Object o) { + SrlAble s = SrlAble_.as_(o); if (s == null) return Object_.Xto_str_strict_or_null_mark(o); + GfoMsg m = GfoMsg_.new_parse_("root"); + s.Srl(m); + return To_str(m); + } + static void To_str(GfoMsg owner, String_bldr sb, int depth, boolean indentOn) { + String indent = String_.Repeat(" ", depth * 4); + if (indentOn) sb.Add(indent); + sb.Add(owner.Key()).Add(":"); + for (int i = 0; i < owner.Args_count(); i++) { + if (i != 0) sb.Add(" "); + Keyval kv = owner.Args_getAt(i); + sb.Add(kv.Key()).Add("=").Add("'").Add(Object_.Xto_str_strict_or_null_mark(kv.Val())).Add("'"); + } + int subsCount = owner.Subs_count(); + if (subsCount == 0) { + sb.Add(";"); + return; + } + else if (subsCount == 1) { + sb.Add("{"); + To_str(owner.Subs_getAt(0), sb, depth + 1, false); + sb.Add("}"); + return; + } + else { + sb.Add("{"); + if (subsCount > 1) sb.Add_char_crlf(); + for (int i = 0; i < subsCount; i++) { + GfoMsg sub = owner.Subs_getAt(i); + To_str(sub, sb, depth + 1, true); + sb.Add_char_crlf(); + } + sb.Add(indent); + sb.Add("}"); + } + } +} diff --git a/100_core/src/gplx/core/interfaces/SrlAble__tst.java b/100_core/src/gplx/core/interfaces/SrlAble__tst.java new file mode 100644 index 000000000..ebd5546de --- /dev/null +++ b/100_core/src/gplx/core/interfaces/SrlAble__tst.java @@ -0,0 +1,66 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.interfaces; import gplx.*; import gplx.core.*; +import org.junit.*; +public class SrlAble__tst { + @Test public void Basic() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1) + , "itm:key='a' val='1';" + ); + + } + @Test public void Depth1_1() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1).Subs_ + ( GfoMsg_.new_cast_("itm").Add("key", "aa").Add("val", 11) + ) + , String_.Concat_lines_crlf_skipLast + ( "itm:key='a' val='1'{itm:key='aa' val='11';}" + ) + ); + } + @Test public void Depth1_2() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1).Subs_ + ( GfoMsg_.new_cast_("itm").Add("key", "aa").Add("val", 11) + , GfoMsg_.new_cast_("itm").Add("key", "ab").Add("val", 12) + ) + , String_.Concat_lines_crlf_skipLast + ( "itm:key='a' val='1'{" + , " itm:key='aa' val='11';" + , " itm:key='ab' val='12';" + , "}" + ) + ); + } + @Test public void Depth1_1_2() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1).Subs_ + ( GfoMsg_.new_cast_("itm").Add("key", "aa").Add("val", 11).Subs_( + GfoMsg_.new_cast_("itm").Add("key", "aab").Add("val", 112) + ) + ) + , String_.Concat_lines_crlf_skipLast + ( "itm:key='a' val='1'{itm:key='aa' val='11'{itm:key='aab' val='112';}}" + ) + ); + } + void tst_Srl_(GfoMsg m, String expd) {Tfds.Eq(expd, SrlAble_.To_str(m));} +} +//class SrlAble__tst diff --git a/100_core/src/gplx/core/intls/Gfo_case_itm.java b/100_core/src/gplx/core/intls/Gfo_case_itm.java new file mode 100644 index 000000000..d4ce4846a --- /dev/null +++ b/100_core/src/gplx/core/intls/Gfo_case_itm.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.intls; import gplx.*; import gplx.core.*; +public interface Gfo_case_itm { + int Hashcode_lo(); + int Len_lo(); + byte[] Asymmetric_bry(); + int Utf8_id_lo(); // lower-case byte or byte[] as single utf8 int +} diff --git a/100_core/src/gplx/core/intls/Gfo_case_mgr.java b/100_core/src/gplx/core/intls/Gfo_case_mgr.java new file mode 100644 index 000000000..000f1b8c1 --- /dev/null +++ b/100_core/src/gplx/core/intls/Gfo_case_mgr.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.intls; import gplx.*; import gplx.core.*; +public interface Gfo_case_mgr { + byte Tid(); + Gfo_case_itm Get_or_null(byte bgn_byte, byte[] src, int bgn, int end); +} diff --git a/100_core/src/gplx/core/intls/Gfo_case_mgr_.java b/100_core/src/gplx/core/intls/Gfo_case_mgr_.java new file mode 100644 index 000000000..ee64006f6 --- /dev/null +++ b/100_core/src/gplx/core/intls/Gfo_case_mgr_.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.intls; import gplx.*; import gplx.core.*; +public class Gfo_case_mgr_ { + public static final byte Tid_a7 = 0, Tid_u8 = 1, Tid_custom = 2; +} diff --git a/100_core/src/gplx/core/intls/Utf16_.java b/100_core/src/gplx/core/intls/Utf16_.java new file mode 100644 index 000000000..76425c7a4 --- /dev/null +++ b/100_core/src/gplx/core/intls/Utf16_.java @@ -0,0 +1,137 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.intls; import gplx.*; import gplx.core.*; +import gplx.core.primitives.*; +public class Utf16_ { + public static int Surrogate_merge(int hi, int lo) { // REF: http://perldoc.perl.org/Encode/Unicode.html + return 0x10000 + (hi - 0xD800) * 0x400 + (lo - 0xDC00); + } + public static void Surrogate_split(int v, Int_obj_ref hi, Int_obj_ref lo) { + hi.Val_((v - 0x10000) / 0x400 + 0xD800); + lo.Val_((v - 0x10000) % 0x400 + 0xDC00); + } + public static int Decode_to_int(byte[] ary, int pos) { + byte b0 = ary[pos]; + if ((b0 & 0x80) == 0) { + return b0; + } + else if ((b0 & 0xE0) == 0xC0) { + return ( b0 & 0x1f) << 6 + | ( ary[pos + 1] & 0x3f) + ; + } + else if ((b0 & 0xF0) == 0xE0) { + return ( b0 & 0x0f) << 12 + | ((ary[pos + 1] & 0x3f) << 6) + | ( ary[pos + 2] & 0x3f) + ; + } + else if ((b0 & 0xF8) == 0xF0) { + return ( b0 & 0x07) << 18 + | ((ary[pos + 1] & 0x3f) << 12) + | ((ary[pos + 2] & 0x3f) << 6) + | ( ary[pos + 3] & 0x3f) + ; + } + else throw Err_.new_wo_type("invalid utf8 byte", "byte", b0); + } + public static byte[] Encode_hex_to_bry(String raw) {return Encode_hex_to_bry(Bry_.new_a7(raw));} + public static byte[] Encode_hex_to_bry(byte[] raw) { + if (raw == null) return null; + int int_val = gplx.core.encoders.Hex_utl_.Parse_or(raw, Int_.Min_value); + return int_val == Int_.Min_value ? null : Encode_int_to_bry(int_val); + } + public static byte[] Encode_int_to_bry(int c) { + int bry_len = Len_by_int(c); + byte[] bry = new byte[bry_len]; + Encode_int(c, bry, 0); + return bry; + } + public static int Encode_char(int c, char[] c_ary, int c_pos, byte[] b_ary, int b_pos) { + if ((c > -1) + && (c < 128)) { + b_ary[ b_pos] = (byte)c; + return 1; + } + else if (c < 2048) { + b_ary[ b_pos] = (byte)(0xC0 | (c >> 6)); + b_ary[++b_pos] = (byte)(0x80 | (c & 0x3F)); + return 1; + } + else if((c > 55295) // 0xD800 + && (c < 56320)) { // 0xDFFF + if (c_pos >= c_ary.length) throw Err_.new_wo_type("incomplete surrogate pair at end of String", "char", c); + char nxt_char = c_ary[c_pos + 1]; + int v = Surrogate_merge(c, nxt_char); + b_ary[b_pos] = (byte)(0xF0 | (v >> 18)); + b_ary[++b_pos] = (byte)(0x80 | (v >> 12) & 0x3F); + b_ary[++b_pos] = (byte)(0x80 | (v >> 6) & 0x3F); + b_ary[++b_pos] = (byte)(0x80 | (v & 0x3F)); + return 2; + } + else { + b_ary[b_pos] = (byte)(0xE0 | (c >> 12)); + b_ary[++b_pos] = (byte)(0x80 | (c >> 6) & 0x3F); + b_ary[++b_pos] = (byte)(0x80 | (c & 0x3F)); + return 1; + } + } + public static int Encode_int(int c, byte[] src, int pos) { + if ((c > -1) + && (c < 128)) { + src[ pos] = (byte)c; + return 1; + } + else if (c < 2048) { + src[ pos] = (byte)(0xC0 | (c >> 6)); + src[++pos] = (byte)(0x80 | (c & 0x3F)); + return 2; + } + else if (c < 65536) { + src[pos] = (byte)(0xE0 | (c >> 12)); + src[++pos] = (byte)(0x80 | (c >> 6) & 0x3F); + src[++pos] = (byte)(0x80 | (c & 0x3F)); + return 3; + } + else if (c < 2097152) { + src[pos] = (byte)(0xF0 | (c >> 18)); + src[++pos] = (byte)(0x80 | (c >> 12) & 0x3F); + src[++pos] = (byte)(0x80 | (c >> 6) & 0x3F); + src[++pos] = (byte)(0x80 | (c & 0x3F)); + return 4; + } + else throw Err_.new_wo_type("UTF-16 int must be between 0 and 2097152", "char", c); + } + private static int Len_by_int(int c) { + if ((c > -1) + && (c < 128)) return 1; // 1 << 7 + else if (c < 2048) return 2; // 1 << 11 + else if (c < 65536) return 3; // 1 << 16 + else if (c < 2097152) return 4; + else throw Err_.new_wo_type("UTF-16 int must be between 0 and 2097152", "char", c); + } + public static int Len_by_char(int c) { + if ((c > -1) + && (c < 128)) return 1; // 1 << 7 + else if (c < 2048) return 2; // 1 << 11 + else if((c > 55295) // 0xD800 + && (c < 56320)) return 4; // 0xDFFF + else if (c < 65536) return 3; // 1 << 16 + else throw Err_.new_wo_type("UTF-16 int must be between 0 and 65536", "char", c); + } +} diff --git a/100_core/src/gplx/core/intls/Utf16__tst.java b/100_core/src/gplx/core/intls/Utf16__tst.java new file mode 100644 index 000000000..ee467cfeb --- /dev/null +++ b/100_core/src/gplx/core/intls/Utf16__tst.java @@ -0,0 +1,59 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.intls; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.core.primitives.*; +public class Utf16__tst { + private Utf16__fxt fxt = new Utf16__fxt(); + @Test public void Encode_decode() { +// fxt.Test_encode_decode(162, 194, 162); // cent +// fxt.Test_encode_decode(8364, 226, 130, 172); // euro + fxt.Test_encode_decode(150370, 240, 164, 173, 162); // example from [[UTF-8]]; should be encoded as two bytes + } + @Test public void Encode_as_bry_by_hex() { + fxt.Test_Encode_hex_to_bry("00", 0); + fxt.Test_Encode_hex_to_bry("41", 65); + fxt.Test_Encode_hex_to_bry("0041", 65); + fxt.Test_Encode_hex_to_bry("00C0", 195, 128); + } + @Test public void Surrogate() { + fxt.Test_surrogate(0x64321, 0xD950, 0xDF21); // example from w:UTF-16 + fxt.Test_surrogate(66643, 55297, 56403); // example from d:Boomerang + } +} +class Utf16__fxt { + private Int_obj_ref hi_ref = Int_obj_ref.New_neg1(), lo_ref = Int_obj_ref.New_neg1(); + public void Test_encode_decode(int expd_c_int, int... expd_int) { + byte[] expd = Bry_.New_by_ints(expd_int); + byte[] bfr = new byte[10]; + int bfr_len = Utf16_.Encode_int(expd_c_int, bfr, 0); + byte[] actl = Bry_.Mid_by_len(bfr, 0, bfr_len); + Tfds.Eq_ary(expd, actl); + int actl_c_int = Utf16_.Decode_to_int(bfr, 0); + Tfds.Eq(expd_c_int, actl_c_int); + } + public void Test_surrogate(int v, int hi, int lo) { + Tfds.Eq(v, Utf16_.Surrogate_merge((char)hi, (char)lo)); + Utf16_.Surrogate_split(v, hi_ref, lo_ref); + Tfds.Eq(hi, hi_ref.Val()); + Tfds.Eq(lo, lo_ref.Val()); + } + public void Test_Encode_hex_to_bry(String raw, int... expd) { + byte[] actl = Utf16_.Encode_hex_to_bry(raw); + Tfds.Eq_ary(Byte_.Ary_by_ints(expd), actl); + } +} diff --git a/100_core/src/gplx/core/intls/Utf8_.java b/100_core/src/gplx/core/intls/Utf8_.java new file mode 100644 index 000000000..5947a859a --- /dev/null +++ b/100_core/src/gplx/core/intls/Utf8_.java @@ -0,0 +1,117 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.intls; import gplx.*; import gplx.core.*; +public class Utf8_ { + public static int Len_of_bry(byte[] ary) { + if (ary == null) return 0; + int rv = 0; + int pos = 0, len = ary.length; + while (pos < len) { + int char_len = Len_of_char_by_1st_byte(ary[pos]); + ++rv; + pos += char_len; + } + return rv; + } + public static int Len_of_char_by_1st_byte(byte b) {// SEE:w:UTF-8 + int i = b & 0xff; // PATCH.JAVA:need to convert to unsigned byte + switch (i) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: + case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: + case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63: + case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79: + case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87: case 88: case 89: case 90: case 91: case 92: case 93: case 94: case 95: + case 96: case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 109: case 110: case 111: + case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: + case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: + case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: + case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: + case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: + return 1; + case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: + case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: + return 2; + case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: + return 3; + case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: + return 4; + default: throw Err_.new_wo_type("invalid initial utf8 byte", "byte", b); + } + } + public static byte[] Get_char_at_pos_as_bry(byte[] bry, int pos) { + int len = Len_of_char_by_1st_byte(bry[pos]); + return Bry_.Mid(bry, pos, pos + len); + } + public static byte[] Increment_char_at_last_pos(byte[] bry) { // EX: abc -> abd; complexity is for multi-byte chars + int bry_len = bry.length; if (bry_len == 0) return bry; + int pos = bry_len - 1; + while (true) { // loop bwds + int cur_char_pos0 = Get_pos0_of_char_bwd(bry, pos); // get byte0 of char + int cur_char_len = (pos - cur_char_pos0) + 1; // calc len of char + int nxt_char = Codepoint_max; + if (cur_char_len == 1) { // len=1; just change 1 byte + nxt_char = Increment_char(bry[cur_char_pos0]); // get next char + if (nxt_char < 128) { // single-byte char; just change pos + bry = Bry_.Copy(bry); // always return new bry; never reuse existing + bry[cur_char_pos0] = (byte)nxt_char; + return bry; + } + } + int cur_char = Utf16_.Decode_to_int(bry, cur_char_pos0); + nxt_char = Increment_char(cur_char); + if (nxt_char != Int_.Min_value) { + byte[] nxt_char_as_bry = Utf16_.Encode_int_to_bry(nxt_char); + bry = Bry_.Add(Bry_.Mid(bry, 0, cur_char_pos0), nxt_char_as_bry); + return bry; + } + pos = cur_char_pos0 - 1; + if (pos < 0) return null; + } + } + public static int Get_pos0_of_char_bwd(byte[] bry, int pos) { // find pos0 of char while moving bwd through bry; see test + int stop = pos - 4; // UTF8 char has max of 4 bytes + if (stop < 0) stop = 0; // if at pos 0 - 3, stop at 0 + for (int i = pos - 1; i >= stop; i--) { // start at pos - 1, and move bwd; NOTE: pos - 1 to skip pos, b/c pos will never definitively yield any char_len info + byte b = bry[i]; + int char_len = Len_of_char_by_1st_byte(b); + switch (char_len) { // if char_len is multi-byte and pos is at correct multi-byte pos (pos - i = # of bytes - 1), then pos0 found; EX: � = {226,130,172}; 172 is skipped; 130 has len of 1 -> continue; 226 has len of 3 and is found at correct pos for 3 byte char -> return + case 2: if (pos - i == 1) return i; break; + case 3: if (pos - i == 2) return i; break; + case 4: if (pos - i == 3) return i; break; + } + } + return pos; // no mult-byte char found; return pos + } + @gplx.Internal protected static int Increment_char(int cur) { + while (cur++ < Codepoint_max) { + if (cur == Codepoint_surrogate_bgn) cur = Codepoint_surrogate_end + 1; // skip over surrogate range + if (!Codepoint_valid(cur)) continue; + return cur; + } + return Int_.Min_value; + } + private static boolean Codepoint_valid(int v) { + return Character.isDefined(v); + } + public static final int + Codepoint_max = 0x10FFFF //see http://unicode.org/glossary/ + , Codepoint_surrogate_bgn = 0xD800 + , Codepoint_surrogate_end = 0xDFFF + ; +} diff --git a/100_core/src/gplx/core/intls/Utf8__tst.java b/100_core/src/gplx/core/intls/Utf8__tst.java new file mode 100644 index 000000000..7319b07e9 --- /dev/null +++ b/100_core/src/gplx/core/intls/Utf8__tst.java @@ -0,0 +1,69 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.intls; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Utf8__tst { + private Utf8__fxt fxt = new Utf8__fxt(); + @Test public void Get_pos0_of_char_bwd() { + fxt.Test_Get_pos0_of_char_bwd("abcd", 3); // len=1; (note that bry.len = 4) + fxt.Test_Get_pos0_of_char_bwd("a", 0); // len=1; short-String + fxt.Test_Get_pos0_of_char_bwd("abc¢", 3); // len=2; (note that bry.len = 5) + fxt.Test_Get_pos0_of_char_bwd("abc€", 3); // len=3; (note that bry.len = 6) + fxt.Test_Get_pos0_of_char_bwd("abc" + String_.new_u8(Byte_.Ary_by_ints(240, 164, 173, 162)), 3); // len=4; (note that bry.len = 7) + } + @Test public void Increment_char_at_last_pos() { + fxt.Test_Increment_char_at_last_pos("a", "b"); + fxt.Test_Increment_char_at_last_pos("abc", "abd"); + fxt.Test_Increment_char_at_last_pos("É", "Ê"); // len=2 + fxt.Test_Increment_char_at_last_pos("€", "â‚­"); // len=3 + } +// @Test public void Increment_char_at_last_pos_exhaustive_check() { // check all values; commented for perf +// Bry_bfr bfr = Bry_bfr_.New(); +// int bgn = 32; +// while (true) { +// byte[] bgn_bry = Utf16_.Encode_int_to_bry(bgn); +// int end = Utf8_.Increment_char(bgn); +// if (end == Utf8_.Codepoint_max) break; +//// if (bgn > 1024 * 1024) break; +// byte[] end_by_codepoint_next = Utf16_.Encode_int_to_bry(end); +// byte[] end_by_increment_char = Utf8_.Increment_char_at_last_pos(bgn_bry); +// if (!Bry_.Eq(end_by_codepoint_next, end_by_increment_char)) { +// Tfds.Write(bgn); +// } +//// bfr .Add_int_variable(bgn).Add_byte(Byte_ascii.Tab) +//// .Add(bgn_bry).Add_byte(Byte_ascii.Tab) +//// .Add(end_by_codepoint_next).Add_byte(Byte_ascii.Tab) +//// .Add(end_by_increment_char).Add_byte(Byte_ascii.Tab) +//// .Add_byte_nl() +//// ; +// bgn = end; +// bgn_bry = end_by_codepoint_next; +// } +// Tfds.WriteText(bfr.To_str_and_clear()); +// } +} +class Utf8__fxt { + public void Test_Get_pos0_of_char_bwd(String str, int expd) { + byte[] bry = Bry_.new_u8(str); + int pos = bry.length - 1; // always start from last char + Tfds.Eq(expd, Utf8_.Get_pos0_of_char_bwd(bry, pos)); + } + public void Test_Increment_char_at_last_pos(String str, String expd) { + Tfds.Eq(expd, String_.new_u8(Utf8_.Increment_char_at_last_pos(Bry_.new_u8(str)))); + } +} diff --git a/100_core/src/gplx/core/ios/IoEngine.java b/100_core/src/gplx/core/ios/IoEngine.java new file mode 100644 index 000000000..2bdc41d3e --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine.java @@ -0,0 +1,156 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.consoles.*; import gplx.core.criterias.*; +import gplx.core.ios.streams.*; +public interface IoEngine { + String Key(); + boolean ExistsFil_api(Io_url url); + void SaveFilText_api(IoEngine_xrg_saveFilStr args); + String LoadFilStr(IoEngine_xrg_loadFilStr args); + void DeleteFil_api(IoEngine_xrg_deleteFil args); + void CopyFil(IoEngine_xrg_xferFil args); + void MoveFil(IoEngine_xrg_xferFil args); + IoItmFil QueryFil(Io_url url); + void UpdateFilAttrib(Io_url url, IoItmAttrib atr); // will fail if file does not exists + void UpdateFilModifiedTime(Io_url url, DateAdp modified); + IoStream OpenStreamRead(Io_url url); + IoStream OpenStreamWrite(IoEngine_xrg_openWrite args); + void XferFil(IoEngine_xrg_xferFil args); + void RecycleFil(IoEngine_xrg_recycleFil xrg); + boolean Truncate_fil(Io_url url, long size); + + boolean ExistsDir(Io_url url); + void CreateDir(Io_url url); // creates all folder levels (EX: C:\a\b\c\ will create C:\a\ and C:\a\b\). will not fail if called on already existing folders. + void DeleteDir(Io_url url); + void MoveDir(Io_url src, Io_url trg); // will fail if trg exists + void CopyDir(Io_url src, Io_url trg); + IoItmDir QueryDir(Io_url url); + + void DeleteDirDeep(IoEngine_xrg_deleteDir args); + void MoveDirDeep(IoEngine_xrg_xferDir args); // will fail if trg exists + IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args); + void XferDir(IoEngine_xrg_xferDir args); + boolean DownloadFil(IoEngine_xrg_downloadFil xrg); + Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg); +} +class IoEngineUtl { + public int BufferLength() {return bufferLength;} public void BufferLength_set(int v) {bufferLength = v;} int bufferLength = 4096; // 0x1000 + public void DeleteRecycleGplx(IoEngine engine, IoEngine_xrg_recycleFil xrg) { + Io_url recycleUrl = xrg.RecycleUrl(); + if (recycleUrl.Type_fil()) + engine.MoveFil(IoEngine_xrg_xferFil.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true)); + else + engine.MoveDirDeep(IoEngine_xrg_xferDir.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true)); + } + public void DeleteDirDeep(IoEngine engine, Io_url dirUrl, IoEngine_xrg_deleteDir args) { + Console_adp usrDlg = args.UsrDlg(); + IoItmDir dir = engine.QueryDir(dirUrl); if (!dir.Exists()) return; + for (Object subDirObj : dir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + if (!args.SubDirScanCrt().Matches(subDir)) continue; + if (args.Recur()) DeleteDirDeep(engine, subDir.Url(), args); + } + for (Object subFilObj : dir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + if (!args.MatchCrt().Matches(subFil)) continue; + Io_url subFilUrl = subFil.Url(); + try {engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(subFilUrl).ReadOnlyFails_(args.ReadOnlyFails()));} + catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));} + } + // all subs deleted; now delete dir + if (!args.MatchCrt().Matches(dir)) return; + try {engine.DeleteDir(dir.Url());} + catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));} + } + public void XferDir(IoEngine srcEngine, Io_url src, IoEngine trgEngine, Io_url trg, IoEngine_xrg_xferDir args) { + trgEngine.CreateDir(trg); + IoItmDir srcDir = QueryDirDeep(srcEngine, IoEngine_xrg_queryDir.new_(src).Recur_(false)); + for (Object subSrcObj : srcDir.SubDirs()) { + IoItmDir subSrc = (IoItmDir)subSrcObj; + if (!args.SubDirScanCrt().Matches(subSrc)) continue; + if (!args.MatchCrt().Matches(subSrc)) continue; + Io_url subTrg = trg.GenSubDir_nest(subSrc.Url().NameOnly()); //EX: C:\abc\def\ -> C:\123\ + def\ + if (args.Recur()) XferDir(srcEngine, subSrc.Url(), trgEngine, subTrg, args); + } + IoItmList srcFils = IoItmList.list_(src.Info().CaseSensitive()); + for (Object srcFilObj : srcDir.SubFils()) { + IoItmFil srcFil = (IoItmFil)srcFilObj; + if (args.MatchCrt().Matches(srcFil)) srcFils.Add(srcFil); + } + for (Object srcFilObj : srcFils) { + IoItmFil srcFil = (IoItmFil)srcFilObj; + Io_url srcFilPath = srcFil.Url(); + Io_url trgFilPath = trg.GenSubFil(srcFilPath.NameAndExt()); //EX: C:\abc\fil.txt -> C:\123\ + fil.txt + IoEngine_xrg_xferFil xferArgs = args.Type_move() ? IoEngine_xrg_xferFil.move_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite()) : IoEngine_xrg_xferFil.copy_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite()); + XferFil(srcEngine, xferArgs); + } + if (args.Type_move()) srcEngine.DeleteDirDeep(IoEngine_xrg_deleteDir.new_(src).Recur_(args.Recur()).ReadOnlyFails_(args.ReadOnlyFails()));// this.DeleteDirDeep(srcEngine, src, IoEngine_xrg_deleteItm.new_(src).Recur_(args.Recur()).ReadOnlyIgnored_(args.ReadOnlyIgnored())); + } + public void XferFil(IoEngine srcEngine, IoEngine_xrg_xferFil args) { + Io_url src = args.Src(), trg = args.Trg(); + if (String_.Eq(srcEngine.Key(), trg.Info().EngineKey())) { + if (args.Type_move()) + srcEngine.MoveFil(args); + else + srcEngine.CopyFil(args); + } + else { + TransferStream(src, trg); + if (args.Type_move()) srcEngine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(src)); + } + } + public IoItmDir QueryDirDeep(IoEngine engine, IoEngine_xrg_queryDir args) { + IoItmDir rv = IoItmDir_.top_(args.Url()); + rv.Exists_set(QueryDirDeepCore(rv, args.Url(), engine, args.Recur(), args.SubDirScanCrt(), args.DirCrt(), args.FilCrt(), args.UsrDlg(), args.DirInclude())); + return rv; + } + static boolean QueryDirDeepCore(IoItmDir ownerDir, Io_url url, IoEngine engine, boolean recur, Criteria subDirScanCrt, Criteria dirCrt, Criteria filCrt, Console_adp usrDlg, boolean dirInclude) { + if (usrDlg.Canceled_chk()) return false; + if (usrDlg.Enabled()) usrDlg.Write_tmp(String_.Concat("scan: ", url.Raw())); + IoItmDir scanDir = engine.QueryDir(url); + for (Object subDirObj : scanDir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + if (!subDirScanCrt.Matches(subDir)) continue; + if (dirCrt.Matches(subDir)) { + ownerDir.SubDirs().Add(subDir); // NOTE: always add subDir; do not use dirCrt here, else its subFils will be added to non-existent subDir + } + if (recur) + QueryDirDeepCore(subDir, subDir.Url(), engine, recur, subDirScanCrt, dirCrt, filCrt, usrDlg, dirInclude); + } + for (Object subFilObj : scanDir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + if (filCrt.Matches(subFil)) ownerDir.SubFils().Add(subFil); + } + return scanDir.Exists(); + } + void TransferStream(Io_url src, Io_url trg) { + IoStream srcStream = null; + IoStream trgStream = null; + try { + srcStream = IoEnginePool.Instance.Get_by(src.Info().EngineKey()).OpenStreamRead(src); + trgStream = IoEngine_xrg_openWrite.new_(trg).Exec(); + srcStream.Transfer(trgStream, bufferLength); + } + finally { + if (srcStream != null) srcStream.Rls(); + if (trgStream != null) trgStream.Rls(); + } + } + public static IoEngineUtl new_() {return new IoEngineUtl();} IoEngineUtl() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngineFxt.java b/100_core/src/gplx/core/ios/IoEngineFxt.java new file mode 100644 index 000000000..0d3cc4776 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngineFxt.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoEngineFxt { + IoEngine EngineOf(Io_url url) {return IoEnginePool.Instance.Get_by(url.Info().EngineKey());} + public void tst_ExistsPaths(boolean expd, Io_url... ary) { + for (Io_url fil : ary) { + if (fil.Type_dir()) + Tfds.Eq(expd, EngineOf(fil).ExistsDir(fil), "ExistsDir failed; dir={0}", fil); + else + Tfds.Eq(expd, EngineOf(fil).ExistsFil_api(fil), "ExistsFil failed; fil={0}", fil); + } + } + public void tst_LoadFilStr(Io_url fil, String expd) {Tfds.Eq(expd, EngineOf(fil).LoadFilStr(IoEngine_xrg_loadFilStr.new_(fil)));} + public void run_SaveFilText(Io_url fil, String expd) {EngineOf(fil).SaveFilText_api(IoEngine_xrg_saveFilStr.new_(fil, expd));} + public void run_UpdateFilModifiedTime(Io_url fil, DateAdp modifiedTime) {EngineOf(fil).UpdateFilModifiedTime(fil, modifiedTime);} + public void tst_QueryFilReadOnly(Io_url fil, boolean expd) {Tfds.Eq(expd, EngineOf(fil).QueryFil(fil).ReadOnly());} + public IoEngineFxt tst_QueryFil_size(Io_url fil, long expd) {Tfds.Eq(expd, EngineOf(fil).QueryFil(fil).Size()); return this;} + public IoEngineFxt tst_QueryFil_modifiedTime(Io_url fil, DateAdp expd) {Tfds.Eq_date(expd, EngineOf(fil).QueryFil(fil).ModifiedTime()); return this;} + public IoItmDir tst_ScanDir(Io_url dir, Io_url... expd) { + IoItmDir dirItem = EngineOf(dir).QueryDir(dir); + Io_url[] actl = new Io_url[dirItem.SubDirs().Count() + dirItem.SubFils().Count()]; + for (int i = 0; i < dirItem.SubDirs().Count(); i++) { + IoItmDir subDir = IoItmDir_.as_(dirItem.SubDirs().Get_at(i)); + actl[i] = subDir.Url(); + } + for (int i = 0; i < dirItem.SubFils().Count(); i++) { + IoItmFil subFil = IoItmFil_.as_(dirItem.SubFils().Get_at(i)); + actl[i + dirItem.SubDirs().Count()] = subFil.Url(); + } + Tfds.Eq_ary_str(expd, actl); + return dirItem; + } + public static IoEngineFxt new_() { + IoEngineFxt rv = new IoEngineFxt(); + return rv; + } + public IoEngineFxt() {} +} diff --git a/100_core/src/gplx/core/ios/IoEnginePool.java b/100_core/src/gplx/core/ios/IoEnginePool.java new file mode 100644 index 000000000..9055cea51 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEnginePool.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoEnginePool { + private final Hash_adp hash = Hash_adp_.New(); + public void Add_if_dupe_use_nth(IoEngine engine) { + hash.Del(engine.Key()); + hash.Add(engine.Key(), engine); + } + public IoEngine Get_by(String key) { + IoEngine rv = (IoEngine)hash.Get_by(key); + return rv == null ? IoEngine_.Mem : rv; // rv == null when url is null or empty; return Mem which should be a noop; DATE:2013-06-04 + } + public static final IoEnginePool Instance = new IoEnginePool(); + IoEnginePool() { + this.Add_if_dupe_use_nth(IoEngine_.Sys); + this.Add_if_dupe_use_nth(IoEngine_.Mem); + } +} \ No newline at end of file diff --git a/100_core/src/gplx/core/ios/IoEngine_.java b/100_core/src/gplx/core/ios/IoEngine_.java new file mode 100644 index 000000000..8d9ab3aeb --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoEngine_ { + public static final String SysKey = "sys"; + public static final String MemKey = "mem"; + + public static final IoEngine Sys = IoEngine_system.new_(); + public static final IoEngine_memory Mem = IoEngine_memory.new_(MemKey); + public static IoEngine Mem_init_() { + Mem.Clear(); + return Mem; + } + public static IoEngine mem_new_(String key) {return IoEngine_memory.new_(key);} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_base.java b/100_core/src/gplx/core/ios/IoEngine_base.java new file mode 100644 index 000000000..b8f0816d9 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_base.java @@ -0,0 +1,59 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.ios.streams.*; +public abstract class IoEngine_base implements IoEngine { + public abstract String Key(); + public abstract boolean ExistsFil_api(Io_url url); + public abstract void SaveFilText_api(IoEngine_xrg_saveFilStr args); + public abstract String LoadFilStr(IoEngine_xrg_loadFilStr args); + public abstract void DeleteFil_api(IoEngine_xrg_deleteFil args); + public abstract void CopyFil(IoEngine_xrg_xferFil args); + public abstract void MoveFil(IoEngine_xrg_xferFil args); + public abstract IoItmFil QueryFil(Io_url url); + public abstract void UpdateFilAttrib(Io_url url, IoItmAttrib atr); // will fail if file does not exists + public abstract void UpdateFilModifiedTime(Io_url url, DateAdp modified); + public abstract IoStream OpenStreamRead(Io_url url); + public abstract IoStream OpenStreamWrite(IoEngine_xrg_openWrite args); + public abstract void XferFil(IoEngine_xrg_xferFil args); + public abstract boolean Truncate_fil(Io_url url, long size); + + public abstract boolean ExistsDir(Io_url url); + public abstract void CreateDir(Io_url url); // creates all folder levels (EX: C:\a\b\c\ will create C:\a\ and C:\a\b\). will not fail if called on already existing folders. + public abstract void DeleteDir(Io_url url); + public abstract void MoveDir(Io_url src, Io_url trg); // will fail if trg exists + public abstract void CopyDir(Io_url src, Io_url trg); + public abstract IoItmDir QueryDir(Io_url url); + + public abstract void DeleteDirDeep(IoEngine_xrg_deleteDir args); + public abstract void MoveDirDeep(IoEngine_xrg_xferDir args); // will fail if trg exists + public abstract IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args); + public abstract void XferDir(IoEngine_xrg_xferDir args); + public abstract boolean DownloadFil(IoEngine_xrg_downloadFil xrg); + public abstract Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg); + + public void RecycleFil(IoEngine_xrg_recycleFil xrg) { + Io_url recycleUrl = xrg.RecycleUrl(); + if (recycleUrl.Type_fil()) { + this.MoveFil(IoEngine_xrg_xferFil.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true).MissingFails_(xrg.MissingFails())); + IoRecycleBin.Instance.Regy_add(xrg); + } + else + this.MoveDirDeep(IoEngine_xrg_xferDir.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true)); + } +} diff --git a/100_core/src/gplx/core/ios/IoEngine_memory.java b/100_core/src/gplx/core/ios/IoEngine_memory.java new file mode 100644 index 000000000..5e89d513a --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_memory.java @@ -0,0 +1,203 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.ios.streams.*; +public class IoEngine_memory extends IoEngine_base { + @Override public String Key() {return key;} private String key = IoEngine_.MemKey; + @Override public boolean ExistsFil_api(Io_url url) {return FetchFil(url) != IoItmFil_mem.Null;} + @Override public void DeleteFil_api(IoEngine_xrg_deleteFil args) { + Io_url url = args.Url(); + IoItmDir dir = FetchDir(url.OwnerDir()); if (dir == null) return; // url doesn't exist; just exit + IoItmFil fil = IoItmFil_.as_(dir.SubFils().Get_by(url.NameAndExt())); + if (fil != null && fil.ReadOnly() && args.ReadOnlyFails()) throw IoErr.FileIsReadOnly(url); + dir.SubFils().Del(url); + } + void DeleteFil(Io_url url) {DeleteFil_api(IoEngine_xrg_deleteFil.new_(url));} + @Override public void XferFil(IoEngine_xrg_xferFil args) {utl.XferFil(this, args);} + @Override public void MoveFil(IoEngine_xrg_xferFil args) { + Io_url src = args.Src(), trg = args.Trg(); boolean overwrite = args.Overwrite(); + if (String_.Eq(src.Xto_api(), trg.Xto_api())) throw Err_.new_wo_type("move failed; src is same as trg", "raw", src.Raw()); + CheckTransferArgs("move", src, trg, overwrite); + if (overwrite) DeleteFil(trg); + IoItmFil_mem curFil = FetchFil(src); curFil.Name_(trg.NameAndExt()); + AddFilToDir(trg.OwnerDir(), curFil); + DeleteFil(src); + } + @Override public void CopyFil(IoEngine_xrg_xferFil args) { + Io_url src = args.Src(), trg = args.Trg(); boolean overwrite = args.Overwrite(); + CheckTransferArgs("copy", src, trg, overwrite); + if (overwrite) DeleteFil(trg); + IoItmFil_mem srcFil = FetchFil(src); + IoItmFil_mem curFil = srcFil.Clone(); curFil.Name_(trg.NameAndExt()); + AddFilToDir(trg.OwnerDir(), curFil); + } + @Override public IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args) {return utl.QueryDirDeep(this, args);} + @Override public void UpdateFilAttrib(Io_url url, IoItmAttrib atr) {FetchFil(url).ReadOnly_(atr.ReadOnly());} + @Override public void UpdateFilModifiedTime(Io_url url, DateAdp modified) {FetchFil(url).ModifiedTime_(modified);} + @Override public IoItmFil QueryFil(Io_url url) {return FetchFil(url);} + @Override public void SaveFilText_api(IoEngine_xrg_saveFilStr args) { + Io_url url = args.Url(); + IoItmDir dir = FetchDir(url.OwnerDir()); + if (dir != null) { + IoItmFil fil = IoItmFil_.as_(dir.SubFils().Get_by(url.NameAndExt())); + if (fil != null && fil.ReadOnly()) throw IoErr.FileIsReadOnly(url); + } + + if (args.Append()) + AppendFilStr(args); + else + SaveFilStr(args.Url(), args.Text()); + } + @Override public boolean Truncate_fil(Io_url url, long size) {throw Err_.new_unimplemented();} + @Override public String LoadFilStr(IoEngine_xrg_loadFilStr args) { + return FetchFil(args.Url()).Text(); + } + void SaveFilStr(Io_url url, String text) { + DateAdp time = Datetime_now.Get(); + IoItmFil_mem fil = IoItmFil_mem.new_(url, String_.Len(text), time, text); + AddFilToDir(url.OwnerDir(), fil); + } + void AppendFilStr(IoEngine_xrg_saveFilStr args) { + Io_url url = args.Url(); String text = args.Text(); + if (ExistsFil_api(url)) { + IoItmFil_mem fil = FetchFil(url); + fil.ModifiedTime_(Datetime_now.Get()); + fil.Text_set(fil.Text() + text); + } + else + SaveFilStr(args.Url(), args.Text()); + } + @Override public IoStream OpenStreamRead(Io_url url) { + IoItmFil_mem fil = FetchFil(url); + fil.Stream().Position_set(0); + return fil.Stream(); + } + @Override public IoStream OpenStreamWrite(IoEngine_xrg_openWrite args) { + Io_url url = args.Url(); + IoItmFil_mem fil = FetchFil(url); + if (fil == IoItmFil_mem.Null) { // file doesn't exist; create new one + SaveFilStr(url, ""); + fil = FetchFil(url); + } + else { + if (args.Mode() == IoStream_.Mode_wtr_create) + fil.Text_set(""); // NOTE: clear text b/c it still has pointer to existing stream + } + return fil.Stream(); + } + + @Override public boolean ExistsDir(Io_url url) {return FetchDir(url) != null;} + @Override public void CreateDir(Io_url url) { + IoItmDir dir = FetchDir(url); if (dir != null) return; // dir exists; exit + dir = IoItmDir_.top_(url); + dirs.Add(dir); + IoItmDir ownerDir = FetchDir(url.OwnerDir()); + if (ownerDir == null && !url.OwnerDir().Eq(Io_url_.Empty)) { // no owner dir && not "driveDir" -> create + CreateDir(url.OwnerDir()); // recursive + ownerDir = FetchDir(url.OwnerDir()); + } + if (ownerDir != null) + ownerDir.SubDirs().Add(dir); + } + @Override public void DeleteDir(Io_url url) { + FetchDir(url); // force creation if exists? + dirs.Del(url); + IoItmDir ownerDir = FetchDir(url.OwnerDir()); if (ownerDir == null) return; // no ownerDir; no need to unregister + ownerDir.SubDirs().Del(url); + } + @Override public void XferDir(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool.Instance.Get_by(trg.Info().EngineKey()), trg, args);} + @Override public void MoveDirDeep(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool.Instance.Get_by(trg.Info().EngineKey()), trg, args);} + @Override public void MoveDir(Io_url src, Io_url trg) {if (ExistsDir(trg)) throw Err_.new_wo_type("trg already exists", "trg", trg); + IoItmDir dir = FetchDir(src); dir.Name_(trg.NameAndExt()); + for (Object filObj : dir.SubFils()) { // move all subFiles + IoItmFil fil = (IoItmFil)filObj; + fil.OwnerDir_set(dir); + } + dirs.Add(dir); + DeleteDir(src); + } + @Override public IoItmDir QueryDir(Io_url url) { + IoItmDir dir = FetchDir(url); + IoItmDir rv = IoItmDir_.top_(url); // always return copy b/c caller may add/del itms directly + if (dir == null) { + rv.Exists_set(false); + return rv; + } + for (Object subDirObj : dir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + rv.SubDirs().Add(IoItmDir_.scan_(subDir.Url())); + } + for (Object subFilObj : dir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + rv.SubFils().Add(subFil); + } + return rv; + } + @Override public void DeleteDirDeep(IoEngine_xrg_deleteDir args) {utl.DeleteDirDeep(this, args.Url(), args);} + @Override public void CopyDir(Io_url src, Io_url trg) { + IoEngine_xrg_xferDir.copy_(src, trg).Recur_().Exec(); + } + void AddFilToDir(Io_url dirPath, IoItmFil fil) { + IoItmDir dir = FetchDir(dirPath); + if (dir == null) { + CreateDir(dirPath); + dir = FetchDir(dirPath); + } + dir.SubFils().Del(fil.Url()); + dir.SubFils().Add(fil); + } + IoItmDir FetchDir(Io_url url) {return IoItmDir_.as_(dirs.Get_by(url));} + IoItmFil_mem FetchFil(Io_url url) { + IoItmDir ownerDir = FetchDir(url.OwnerDir()); + if (ownerDir == null) return IoItmFil_mem.Null; + IoItmFil_mem rv = IoItmFil_mem.as_(ownerDir.SubFils().Get_by(url.NameAndExt())); + if (rv == null) rv = IoItmFil_mem.Null; + return rv; + } + void CheckTransferArgs(String op, Io_url src, Io_url trg, boolean overwrite) { + if (!ExistsFil_api(src)) throw Err_.new_wo_type("src does not exist", "src", src); + if (ExistsFil_api(trg) && !overwrite) throw Err_.new_invalid_op("trg already exists").Args_add("op", op, "overwrite", false, "src", src, "trg", trg); + } + public void Clear() {dirs.Clear();} + @Override public boolean DownloadFil(IoEngine_xrg_downloadFil xrg) { + Io_url src = Io_url_.mem_fil_(xrg.Src()); + if (!ExistsFil_api(src)) { + xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + return false; + } + XferFil(IoEngine_xrg_xferFil.copy_(src, xrg.Trg()).Overwrite_()); + return true; + } + @Override public Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg) { + Io_url src = Io_url_.mem_fil_(xrg.Src()); + if (!ExistsFil_api(src)) { + xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + return Io_stream_rdr_.Noop; + } + byte[] bry = Bry_.new_u8(FetchFil(Io_url_.mem_fil_(xrg.Src())).Text()); + return Io_stream_rdr_.New__mem(bry); + } + + IoItmHash dirs = IoItmHash.new_(); + IoEngineUtl utl = IoEngineUtl.new_(); + @gplx.Internal protected static IoEngine_memory new_(String key) { + IoEngine_memory rv = new IoEngine_memory(); + rv.key = key; + return rv; + } IoEngine_memory() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_system.java b/100_core/src/gplx/core/ios/IoEngine_system.java new file mode 100644 index 000000000..4a95747ef --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_system.java @@ -0,0 +1,740 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.*; +import java.nio.channels.*; +import java.util.Date; + +import javax.print.FlavorException; +import javax.tools.JavaCompiler; +import gplx.core.criterias.*; import gplx.core.bits.*; import gplx.core.envs.*; +import gplx.core.ios.streams.*; +import gplx.core.progs.*; +public class IoEngine_system extends IoEngine_base { + @Override public String Key() {return IoEngine_.SysKey;} + @Override public void DeleteDirDeep(IoEngine_xrg_deleteDir args) {utl.DeleteDirDeep(this, args.Url(), args);} + @Override public void XferDir(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool.Instance.Get_by(trg.Info().EngineKey()), trg, args);} + @Override public void XferFil(IoEngine_xrg_xferFil args) {utl.XferFil(this, args);} + @Override public IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args) {return utl.QueryDirDeep(this, args);} + @Override public void CopyDir(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.copy_(src, trg).Recur_().Exec();} + @Override public void MoveDirDeep(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool.Instance.Get_by(trg.Info().EngineKey()), trg, args);} + @Override public void DeleteFil_api(IoEngine_xrg_deleteFil args) { + Io_url url = args.Url(); + File fil = Fil_(url); + if (!Fil_Exists(fil)) return; + MarkFileWritable(fil, url, args.ReadOnlyFails(), "DeleteFile"); + DeleteFil_lang(fil, url); + } + @Override public boolean ExistsFil_api(Io_url url) { + File f = new File(url.Xto_api()); + return f.exists(); + } + @Override public void SaveFilText_api(IoEngine_xrg_saveFilStr mpo) { + Io_url url = mpo.Url(); + + // encode string + byte[] textBytes = null; + textBytes = Bry_.new_u8(mpo.Text()); + + FileChannel fc = null; FileOutputStream fos = null; + if (!ExistsDir(url.OwnerDir())) CreateDir(url.OwnerDir()); + try { + // open file + try {fos = new FileOutputStream(url.Xto_api(), mpo.Append());} + catch (FileNotFoundException e) {throw Err_Fil_NotFound(e, url);} + fc = fos.getChannel(); + + // write text + try {fc.write(ByteBuffer.wrap(textBytes));} + catch (IOException e) { + Closeable_close(fc, url, false); + Closeable_close(fos, url, false); + throw Err_.new_exc(e, "io", "write data to file failed", "url", url.Xto_api()); + } + if (!Op_sys.Cur().Tid_is_drd()) { + File fil = new File(url.Xto_api()); + IoEngine_system_xtn.SetExecutable(fil, true); + } + } + finally { + // cleanup + Closeable_close(fc, url, false); + Closeable_close(fos, url, false); + } + } + @Override public String LoadFilStr(IoEngine_xrg_loadFilStr args) { + Io_url url = args.Url(); String url_str = url.Xto_api(); + boolean file_exists = ExistsFil_api(url); // check if file exists first to avoid throwing exception; note that most callers pass Missing_ignored; DATE:2015-02-24 + if (!file_exists) { + if (args.MissingIgnored()) return ""; + else throw Err_Fil_NotFound(url); + } + // get reader for file + InputStream stream = null; + try {stream = new FileInputStream(url_str);} + catch (FileNotFoundException e) { + if (args.MissingIgnored()) return ""; + throw Err_Fil_NotFound(e, url); + } + return Load_from_stream_as_str(stream, url_str); + } + public static String Load_from_stream_as_str(InputStream stream, String url_str) { + InputStreamReader reader = null; + try {reader = new InputStreamReader(stream, IoEngineArgs.Instance.LoadFilStr_Encoding);} + catch (UnsupportedEncodingException e) { + Closeable_close(stream, url_str, false); + throw Err_text_unsupported_encoding(IoEngineArgs.Instance.LoadFilStr_Encoding, "", url_str, e); + } + + // make other objects + char[] readerBuffer = new char[IoEngineArgs.Instance.LoadFilStr_BufferSize]; + int pos = 0; + StringWriter sw = new StringWriter(); + + // transfer data + while (true) { + try {pos = reader.read(readerBuffer);} + catch (IOException e) { + try { + stream.close(); + reader.close(); + } + catch (IOException e2) {} + throw Err_.new_exc(e, "io", "read data from file failed", "url", url_str, "pos", pos); + } + if (pos == -1) break; + sw.write(readerBuffer, 0, pos); + } + + // cleanup + Closeable_close(stream, url_str, false); + Closeable_close(reader, url_str, false); + return sw.toString(); + } + public static byte[] Load_from_stream_as_bry(InputStream stream, String url_str) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + byte[] data = new byte[4096]; + int read = 0; + try { + while ((read = stream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, read); + } + buffer.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + return buffer.toByteArray(); + } + @Override public boolean ExistsDir(Io_url url) {return new File(url.Xto_api()).exists();} + @Override public void CreateDir(Io_url url) {new File(url.Xto_api()).mkdirs();} + @Override public void DeleteDir(Io_url url) { + File dir = new File(url.Xto_api()); + if (!dir.exists()) return; + boolean rv = dir.delete(); + if (!rv) throw Err_.new_(IoEngineArgs.Instance.Err_IoException, "delete dir failed", "url", url.Xto_api()); + } + @Override public IoItmDir QueryDir(Io_url url) { + IoItmDir rv = IoItmDir_.scan_(url); + String url_api = url.Xto_api(); + if ( gplx.core.envs.Op_sys.Cur().Tid_is_wnt() // op_sys is wnt + && String_.Eq(url.OwnerDir().Raw(), String_.Empty) // folder is drive; EX: "C:" + ) + url_api = url_api + "\\"; // add "\\"; else listFiles will return working folder's files, not C:; DATE:2016-04-07 + File dirInfo = new File(url_api); + if (!dirInfo.exists()) { + rv.Exists_set(false); + return rv; + } + IoUrlInfo urlInfo = url.Info(); + File[] subItmAry = dirInfo.listFiles(); + if (subItmAry == null) return rv; // directory has no files + for (int i = 0; i < subItmAry.length; i++) { + File subItm = subItmAry[i]; + if (subItm.isFile()) { + IoItmFil subFil = QueryMkr_fil(urlInfo, subItm); + rv.SubFils().Add(subFil); + } + else { + IoItmDir subDir = QueryMkr_dir(urlInfo, subItm); + rv.SubDirs().Add(subDir); + } + } + return rv; + } + IoItmFil QueryMkr_fil(IoUrlInfo urlInfo, File apiFil) { + Io_url filUrl = Io_url_.new_inf_(apiFil.getPath(), urlInfo); // NOTE: may throw PathTooLongException when url is > 248 (exception messages states 260) + long fil_len = apiFil.exists() ? apiFil.length() : IoItmFil.Size_invalid; // NOTE: if file doesn't exist, set len to -1; needed for "boolean Exists() {return size != Size_Invalid;}"; DATE:2014-06-21 + IoItmFil rv = IoItmFil_.new_(filUrl, fil_len, DateAdp_.MinValue, DateAdp_.unixtime_lcl_ms_(apiFil.lastModified())); + rv.ReadOnly_(!apiFil.canWrite()); + return rv; + } + IoItmDir QueryMkr_dir(IoUrlInfo urlInfo, File apiDir) { + Io_url dirUrl = Io_url_.new_inf_(apiDir.getPath() + urlInfo.DirSpr(), urlInfo); // NOTE: may throw PathTooLongException when url is > 248 (exception messages states 260) + return IoItmDir_.scan_(dirUrl); + } + @Override public IoItmFil QueryFil(Io_url url) { + File fil = new File(url.Xto_api()); + return QueryMkr_fil(url.Info(), fil); + } + @Override public void UpdateFilAttrib(Io_url url, IoItmAttrib atr) { + File f = new File(url.Xto_api()); + boolean rv = true; + if (atr.ReadOnly() != Fil_ReadOnly(f)) { + if (atr.ReadOnly()) + rv = f.setReadOnly(); + else { + if (!Op_sys.Cur().Tid_is_drd()) + IoEngine_system_xtn.SetWritable(f, true); + } + if (!rv) throw Err_.new_(IoEngineArgs.Instance.Err_IoException, "set file attribute failed", "attribute", "readOnly", "cur", Fil_ReadOnly(f), "new", atr.ReadOnly(), "url", url.Xto_api()); + } + if (atr.Hidden() != f.isHidden()) { + //Runtime.getRuntime().exec("attrib +H myHiddenFile.java"); + } + } + @Override public void UpdateFilModifiedTime(Io_url url, DateAdp modified) { + File f = new File(url.Xto_api()); + long timeInt = modified.UnderDateTime().getTimeInMillis(); +// if (timeInt < 0) { +// UsrDlg_._.Notify("{0} {1}", url.Xto_api(), timeInt); +// return; +// } + if (!f.setLastModified(timeInt)) { + if (Fil_ReadOnly(f)) { + boolean success = false; + try { + UpdateFilAttrib(url, IoItmAttrib.normal_()); + success = f.setLastModified(timeInt); + } + finally { + UpdateFilAttrib(url, IoItmAttrib.readOnly_()); + } + if (!success) throw Err_.new_wo_type("could not update file modified time", "url", url.Xto_api(), "modifiedTime", modified.XtoStr_gplx_long()); + } + } + } + @Override public IoStream OpenStreamRead(Io_url url) {return IoStream_base.new_(url, IoStream_.Mode_rdr);} + @Override public IoStream OpenStreamWrite(IoEngine_xrg_openWrite args) { + Io_url url = args.Url(); + if (!ExistsFil_api(url)) SaveFilText_api(IoEngine_xrg_saveFilStr.new_(url, "")); + return IoStream_base.new_(url, args.Mode()); + } + @Override public void CopyFil(IoEngine_xrg_xferFil args) { + // TODO:JAVA6 hidden property ignored; 1.6 does not allow OS-independent way of setting isHidden (wnt only possible through jni) + boolean overwrite = args.Overwrite(); + Io_url srcUrl = args.Src(), trgUrl = args.Trg(); + File srcFil = new File(srcUrl.Xto_api()), trgFil = new File(trgUrl.Xto_api()); + if (trgFil.isFile()) { // trgFil exists; check if overwrite set and trgFil is writable + Chk_TrgFil_Overwrite(overwrite, trgUrl); + MarkFileWritable(trgFil, trgUrl, args.ReadOnlyFails(), "copy"); + } + else { // trgFil doesn't exist; must create file first else fileNotFound exception thrown + boolean rv = true; + if (!ExistsDir(trgUrl.OwnerDir())) CreateDir(trgUrl.OwnerDir()); + try { + trgFil.createNewFile(); + if (!Op_sys.Cur().Tid_is_drd()) + IoEngine_system_xtn.SetExecutable(trgFil, true); + } + catch (IOException e) { + rv = false; + } + if (!rv) + throw Err_.new_wo_type("create file failed", "trg", trgUrl.Xto_api()); + } + FileInputStream srcStream = null; FileOutputStream trgStream = null; + FileChannel srcChannel = null, trgChannel = null; + try { + // make objects + try {srcStream = new FileInputStream(srcFil);} + catch (FileNotFoundException e) {throw IoErr.FileNotFound("copy", srcUrl);} + try {trgStream = new FileOutputStream(trgFil);} + catch (FileNotFoundException e) { + trgStream = TryToUnHideFile(trgFil, trgUrl); + if (trgStream == null) + throw IoErr.FileNotFound("copy", trgUrl); +// else +// wasHidden = true; + } + srcChannel = srcStream.getChannel(); + trgChannel = trgStream.getChannel(); + + // transfer data + long pos = 0, count = 0, read = 0; + try {count = srcChannel.size();} + catch (IOException e) {throw Err_.new_exc(e, "io", "size failed", "src", srcUrl.Xto_api());} + int totalBufferSize = IoEngineArgs.Instance.LoadFilStr_BufferSize; + long transferSize = (count > totalBufferSize) ? totalBufferSize : count; // transfer as much as fileSize, but limit to LoadFilStr_BufferSize + while (pos < count) { + try {read = trgChannel.transferFrom(srcChannel, pos, transferSize);} + catch (IOException e) { + Closeable_close(srcChannel, srcUrl, false); + Closeable_close(trgChannel, trgUrl, false); + Closeable_close(srcStream, srcUrl, false); + Closeable_close(trgStream, srcUrl, false); + throw Err_.new_exc(e, "io", "transfer data failed", "src", srcUrl.Xto_api(), "trg", trgUrl.Xto_api()); + } + if (read == -1) break; + pos += read; + } +// if (wasHidden) +// + } + finally { + // cleanup + Closeable_close(srcChannel, srcUrl, false); + Closeable_close(trgChannel, trgUrl, false); + Closeable_close(srcStream, srcUrl, false); + Closeable_close(trgStream, srcUrl, false); + } + UpdateFilModifiedTime(trgUrl, QueryFil(srcUrl).ModifiedTime()); // must happen after file is closed + } + public void CopyFil(gplx.core.progs.Gfo_prog_ui prog_ui, Io_url src_url, Io_url trg_url, boolean overwrite, boolean readonly_fails) { + // TODO:JAVA6 hidden property ignored; 1.6 does not allow OS-independent way of setting isHidden (wnt only possible through jni) + File src_fil = new File(src_url.Xto_api()), trg_fil = new File(trg_url.Xto_api()); + if (trg_fil.isFile()) { // trg_fil exists; check if overwrite set and trg_fil is writable + Chk_TrgFil_Overwrite(overwrite, trg_url); + MarkFileWritable(trg_fil, trg_url, readonly_fails, "copy"); + } + else { // trg_fil doesn't exist; must create file first else fileNotFound exception thrown + boolean rv = true; + if (!ExistsDir(trg_url.OwnerDir())) CreateDir(trg_url.OwnerDir()); + try { + trg_fil.createNewFile(); + if (!Op_sys.Cur().Tid_is_drd()) + IoEngine_system_xtn.SetExecutable(trg_fil, true); + } + catch (IOException e) { + rv = false; + } + if (!rv) + throw Err_.new_wo_type("create file failed", "trg", trg_url.Xto_api()); + } + FileInputStream src_stream = null; FileOutputStream trg_stream = null; + FileChannel src_channel = null, trg_channel = null; + try { + // make objects + try {src_stream = new FileInputStream(src_fil);} + catch (FileNotFoundException e) {throw IoErr.FileNotFound("copy", src_url);} + try {trg_stream = new FileOutputStream(trg_fil);} + catch (FileNotFoundException e) { + trg_stream = TryToUnHideFile(trg_fil, trg_url); + if (trg_stream == null) + throw IoErr.FileNotFound("copy", trg_url); +// else +// wasHidden = true; + } + src_channel = src_stream.getChannel(); + trg_channel = trg_stream.getChannel(); + + // transfer data + long pos = 0, count = 0, read = 0; + try {count = src_channel.size();} + catch (IOException e) {throw Err_.new_exc(e, "io", "size failed", "src", src_url.Xto_api());} + int buffer_size = IoEngineArgs.Instance.LoadFilStr_BufferSize; + long transfer_size = (count > buffer_size) ? buffer_size : count; // transfer as much as fileSize, but limit to LoadFilStr_BufferSize + while (pos < count) { + try {read = trg_channel.transferFrom(src_channel, pos, transfer_size);} + catch (IOException e) { + Closeable_close(src_channel, src_url, false); + Closeable_close(trg_channel, trg_url, false); + Closeable_close(src_stream, src_url, false); + Closeable_close(trg_stream, src_url, false); + throw Err_.new_exc(e, "io", "transfer data failed", "src", src_url.Xto_api(), "trg", trg_url.Xto_api()); + } + if (read == -1) break; + if (prog_ui.Prog_notify_and_chk_if_suspended(pos, count)) return; + pos += read; + } +// if (wasHidden) +// + } + finally { + // cleanup + Closeable_close(src_channel, src_url, false); + Closeable_close(trg_channel, trg_url, false); + Closeable_close(src_stream, src_url, false); + Closeable_close(trg_stream, src_url, false); + } + UpdateFilModifiedTime(trg_url, QueryFil(src_url).ModifiedTime()); // must happen after file is closed + } + FileOutputStream TryToUnHideFile(File trgFil, Io_url trgUrl) { + FileOutputStream trgStream = null; + if (trgFil.exists()) { // WORKAROUND: java fails when writing to hidden files; unmark hidden and try again + Process p = null; + try { + String d = "attrib -H \"" + trgUrl.Xto_api() + "\""; + p = Runtime.getRuntime().exec(d); + } catch (IOException e1) { + e1.printStackTrace(); + } + try { + p.waitFor(); + } catch (InterruptedException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + try {trgStream = new FileOutputStream(trgFil);} + catch (FileNotFoundException e) { + return null; + } + } + return trgStream; + } + @Override public void MoveFil(IoEngine_xrg_xferFil args) { + Io_url srcUrl = args.Src(), trgUrl = args.Trg(); + String src_api = srcUrl.Xto_api(), trg_api = trgUrl.Xto_api(); + if (String_.Eq(src_api, trg_api)) return; // ignore command if src and trg is same; EX: C:\a.txt -> C:\a.txt should be noop + File srcFil = new File(src_api), trgFil = new File(trg_api); + + // if drive is same, then rename file + if (String_.Eq(srcUrl.OwnerRoot().Raw(), trgUrl.OwnerRoot().Raw())) { + boolean overwrite = args.Overwrite(); + if (!srcFil.exists() && args.MissingFails()) throw IoErr.FileNotFound("move", srcUrl); + if (trgFil.exists()) { + Chk_TrgFil_Overwrite(overwrite, trgUrl); + MarkFileWritable(trgFil, trgUrl, args.ReadOnlyFails(), "move"); + DeleteFil_lang(trgFil, args.Trg()); // overwrite is specified and file is writable -> delete + } + if (!ExistsDir(trgUrl.OwnerDir())) CreateDir(trgUrl.OwnerDir()); + srcFil.renameTo(trgFil); + } + // else copy fil and delete + else { + if (!srcFil.exists() && !args.MissingFails()) return; + CopyFil(args); + DeleteFil_lang(srcFil, srcUrl); + } + } + void Chk_TrgFil_Overwrite(boolean overwrite, Io_url trg) { + if (!overwrite) + throw Err_.new_invalid_op("trgFile exists but overwriteFlag not set").Args_add("trg", trg.Xto_api()); + } + @Override public void MoveDir(Io_url src, Io_url trg) { + String srcStr = src.Xto_api(), trgStr = trg.Xto_api(); + File srcFil = new File(srcStr), trgFil = new File(trgStr); + if (trgFil.exists()) {throw Err_.new_invalid_op("cannot move dir if trg exists").Args_add("src", src, "trg", trg);} + if (String_.Eq(src.OwnerRoot().Raw(), trg.OwnerRoot().Raw())) { + srcFil.renameTo(trgFil); + } + else { + XferDir(IoEngine_xrg_xferDir.copy_(src, trg)); + } + } + public static void Closeable_close(Closeable closeable, Io_url url, boolean throwErr) {Closeable_close(closeable, url.Xto_api(), throwErr);} + public static void Closeable_close(Closeable closeable, String url_str, boolean throwErr) { + if (closeable == null) return; + try {closeable.close();} + catch (IOException e) { + if (throwErr) + throw Err_.new_exc(e, "io", "close object failed", "class", Type_adp_.NameOf_obj(closeable), "url", url_str); +// else +// UsrDlg_._.Finally("failed to close FileChannel", "url", url, "apiErr", Err_.Message_err_arg(e)); + } + } + + File Fil_(Io_url url) {return new File(url.Xto_api());} + boolean Fil_Exists(File fil) {return fil.exists();} + boolean Fil_ReadOnly(File fil) {return !fil.canWrite();} + boolean Fil_Delete(File fil) {return fil.delete();} + void Fil_Writable(File fil) { + if (!Op_sys.Cur().Tid_is_drd()) + IoEngine_system_xtn.SetWritable(fil, true); + } + private static Err Err_text_unsupported_encoding(String encodingName, String text, String url_str, Exception e) { + return Err_.new_exc(e, "io", "text is in unsupported encoding").Args_add("encodingName", encodingName, "text", text, "url", url_str); + } + boolean user_agent_needs_resetting = true; + @Override public Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg) { + Io_stream_rdr_http rdr = new Io_stream_rdr_http(xrg); + rdr.Open(); + return rdr; + } + @Override public boolean DownloadFil(IoEngine_xrg_downloadFil xrg) { + IoStream trg_stream = null; + java.io.BufferedInputStream src_stream = null; + java.net.URL src_url = null; + HttpURLConnection src_conn = null; + if (user_agent_needs_resetting) {user_agent_needs_resetting = false; System.setProperty("http.agent", "");} + boolean exists = Io_mgr.Instance.ExistsDir(xrg.Trg().OwnerDir()); + Gfo_usr_dlg prog_dlg = null; + String src_str = xrg.Src(); + Io_download_fmt xfer_fmt = xrg.Download_fmt(); + prog_dlg = xfer_fmt.Usr_dlg(); + if (!Web_access_enabled) { + if (prog_dlg != null) { + if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt"); + prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download disabled: src='~{0}' trg='~{1}'", xrg.Src(), xrg.Trg().Raw()); + } + return false; + } + try { + trg_stream = Io_mgr.Instance.OpenStreamWrite(xrg.Trg()); + src_url = new java.net.URL(src_str); + src_conn = (HttpURLConnection)src_url.openConnection(); +// src_conn.setReadTimeout(5000); // do not set; if file does not exist, will wait 5 seconds before timing out; want to fail immediately + String user_agent = xrg.User_agent(); if (user_agent != null) src_conn.setRequestProperty("User-Agent", user_agent); + long content_length = Long_.parse_or(src_conn.getHeaderField("Content-Length"), IoItmFil.Size_invalid_int); + xrg.Src_content_length_(content_length); + if (xrg.Src_last_modified_query()) // NOTE: only files will have last modified (api calls will not); if no last_modified, then src_conn will throw get nullRef; avoid nullRef + xrg.Src_last_modified_(DateAdp_.unixtime_lcl_ms_(src_conn.getLastModified())); + if (xrg.Exec_meta_only()) return true; + src_stream = new java.io.BufferedInputStream(src_conn.getInputStream()); + if (!exists) { + Io_mgr.Instance.CreateDir(xrg.Trg().OwnerDir()); // dir must exist for OpenStreamWrite; create dir at last possible moment in case stream does not exist. + } + byte[] download_bfr = new byte[Download_bfr_len]; // NOTE: download_bfr was originally member variable; DATE:2013-05-03 + xfer_fmt.Bgn(content_length); + int count = 0; + while ((count = src_stream.read(download_bfr, 0, Download_bfr_len)) != -1) { + if (xrg.Prog_cancel()) { + src_stream.close(); + trg_stream.Rls(); + Io_mgr.Instance.DeleteFil(xrg.Trg()); + } + xfer_fmt.Prog(count); + trg_stream.Write(download_bfr, 0, count); + } + if (prog_dlg != null) { + xfer_fmt.Term(); + if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt"); + prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download pass: src='~{0}' trg='~{1}'", src_str, xrg.Trg().Raw()); + } + return true; + } + catch (Exception exc) { + xrg.Rslt_err_(exc); + if (Type_adp_.Eq_typeSafe(exc, java.net.UnknownHostException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_host_not_found); + else if (Type_adp_.Eq_typeSafe(exc, java.io.FileNotFoundException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + else xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_unknown); + if (prog_dlg != null && !xrg.Prog_cancel()) { + if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt"); + prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download fail: src='~{0}' trg='~{1}' error='~{2}'", src_str, xrg.Trg().Raw(), Err_.Message_lang(exc)); + } + if (trg_stream != null) { + try { + trg_stream.Rls(); + DeleteFil_api(IoEngine_xrg_deleteFil.new_(xrg.Trg())); + } + catch (Exception e2) {Err_.Noop(e2);} + } + return false; + } + finally { + xrg.Prog_running_(false); + try { + if (src_stream != null) src_stream.close(); + if (src_conn != null) { + src_conn.disconnect(); + src_conn.getInputStream().close(); + } + } catch (Exception exc) { + Err_.Noop(exc); + } + if (trg_stream != null) trg_stream.Rls(); + } + } Io_url session_fil; Bry_bfr prog_fmt_bfr; + @Override public boolean Truncate_fil(Io_url url, long size) { + FileOutputStream stream = null; + FileChannel channel = null; + try {stream = new FileOutputStream(url.Xto_api(), true);} + catch (FileNotFoundException e) {throw Err_.new_("io", "truncate: open failed", "url", url.Xto_api(), "err", Err_.Message_gplx_log(e));} + channel = stream.getChannel(); + try {channel.truncate(size); return true;} + catch (IOException e) {return false;} + finally { + try { + if (stream != null) stream.close(); + if (channel != null) channel.close(); + } catch (IOException e) {return false;} + } + } + byte[] download_bfr; static final int Download_bfr_len = Io_mgr.Len_kb * 128; + public static Err Err_Fil_NotFound(Io_url url) { + return Err_.new_(IoEngineArgs.Instance.Err_FileNotFound, "file not found", "url", url.Xto_api()).Trace_ignore_add_1_(); + } + public static Err Err_Fil_NotFound(Exception e, Io_url url) { + return Err_.new_exc(e, "io", "file not found", "url", url.Xto_api()).Trace_ignore_add_1_(); + } + void MarkFileWritable(File fil, Io_url url, boolean readOnlyFails, String op) { + if (Fil_ReadOnly(fil)) { + if (readOnlyFails) // NOTE: java will always allow final files to be deleted; programmer api is responsible for check + throw Err_.new_(IoEngineArgs.Instance.Err_ReadonlyFileNotWritable, "writable operation attempted on readOnly file", "op", op, "url", url.Xto_api()); + else + Fil_Writable(fil); + } + } + void DeleteFil_lang(File fil, Io_url url) { + boolean rv = Fil_Delete(fil); + if (!rv) + throw Err_.new_(IoEngineArgs.Instance.Err_IoException, "file not deleted", "url", url.Xto_api()); + } + IoEngineUtl utl = IoEngineUtl.new_(); + public static IoEngine_system new_() {return new IoEngine_system();} IoEngine_system() {} + static final String GRP_KEY = "Io_engine"; + public static boolean Web_access_enabled = true; +} +class IoEngineArgs { + public int LoadFilStr_BufferSize = 4096 * 256; + public String LoadFilStr_Encoding = "UTF-8"; + public String Err_ReadonlyFileNotWritable = "gplx.core.ios.ReadonlyFileNotWritable"; + public String Err_FileNotFound = "gplx.core.ios.FileNotFound"; + public String Err_IoException = "gplx.core.ios.IoException"; + public static final IoEngineArgs Instance = new IoEngineArgs(); +} +class IoEngine_system_xtn { + // PATCH.DROID:VerifyError if file.setExecutable is referenced directly in IoEngine_system. However, if placed in separate class + public static void SetExecutable(java.io.File file, boolean v) {file.setExecutable(v);} + public static void SetWritable(java.io.File file, boolean v) {file.setWritable(v);} +} +class Io_download_http { + public static boolean User_agent_reset_needed = true; + public static void User_agent_reset() { + User_agent_reset_needed = false; + System.setProperty("http.agent", ""); // need to set http.agent to '' in order for "User-agent" to take effect + } + public static void Save_to_fsys(IoEngine_xrg_downloadFil xrg) { + Io_stream_rdr_http rdr = new Io_stream_rdr_http(xrg); + IoStream trg_stream = null; + try { + boolean exists = Io_mgr.Instance.ExistsDir(xrg.Trg().OwnerDir()); + if (!exists) + Io_mgr.Instance.CreateDir(xrg.Trg().OwnerDir()); // dir must exist for OpenStreamWrite; create dir at last possible moment in case stream does not exist. + trg_stream = Io_mgr.Instance.OpenStreamWrite(xrg.Trg()); + byte[] bfr = new byte[Download_bfr_len]; + rdr.Open(); + while (rdr.Read(bfr, 0, Download_bfr_len) != Read_done) { + } + } + finally { + rdr.Rls(); + if (trg_stream != null) trg_stream.Rls(); + } + if (xrg.Rslt() != IoEngine_xrg_downloadFil.Rslt_pass) + Io_mgr.Instance.DeleteFil_args(xrg.Trg()).MissingFails_off().Exec(); + } + public static final int Read_done = -1; + public static final int Download_bfr_len = Io_mgr.Len_kb * 128; +} +class Io_stream_rdr_http implements Io_stream_rdr { + public Io_stream_rdr_http(IoEngine_xrg_downloadFil xrg) { + this.xrg = xrg; + } private IoEngine_xrg_downloadFil xrg; + public byte Tid() {return Io_stream_tid_.Tid__raw;} + public boolean Exists() {return exists;} private boolean exists = false; + public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {url = v; return this;} private Io_url url; + public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len = IoItmFil.Size_invalid; // NOTE: must default size to -1; DATE:2014-06-21 + private String src_str; private HttpURLConnection src_conn; private java.io.BufferedInputStream src_stream; + private Io_download_fmt xfer_fmt; private Gfo_usr_dlg prog_dlg; + private boolean read_done = true, read_failed = false; + public Io_stream_rdr Open() { + if (Io_download_http.User_agent_reset_needed) Io_download_http.User_agent_reset(); + if (!IoEngine_system.Web_access_enabled) { + read_done = read_failed = true; + if (prog_dlg != null) + prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download disabled: src='~{0}' trg='~{1}'", xrg.Src(), xrg.Trg().Raw()); + return this; + } + src_str = xrg.Src(); + xfer_fmt = xrg.Download_fmt(); prog_dlg = xfer_fmt.Usr_dlg(); + try { + src_conn = (HttpURLConnection)new java.net.URL(src_str).openConnection(); + String user_agent = xrg.User_agent(); + if (user_agent != null) src_conn.setRequestProperty("User-Agent", user_agent); // NOTE: must be set right after openConnection +// src_conn.setReadTimeout(5000); // do not set; if file does not exist, will wait 5 seconds before timing out; want to fail immediately + long content_length = Long_.parse_or(src_conn.getHeaderField("Content-Length"), IoItmFil.Size_invalid_int); + xrg.Src_content_length_(content_length); + this.len = content_length; + if (xrg.Src_last_modified_query()) // NOTE: only files will have last modified (api calls will not); if no last_modified, then src_conn will throw get nullRef; avoid nullRef + xrg.Src_last_modified_(DateAdp_.unixtime_lcl_ms_(src_conn.getLastModified())); + if (xrg.Exec_meta_only()) { + read_done = true; + return this; + } + read_done = false; + this.exists = Int_.In(src_conn.getResponseCode(), 200, 301); // ASSUME: response code of 200 (OK) or 301 (Redirect) means that file exists; note that content_length seems to always be -1; DATE:2015-05-20 + src_stream = new java.io.BufferedInputStream(src_conn.getInputStream()); + xfer_fmt.Bgn(content_length); + } + catch (Exception e) {Err_handle(e);} + return this; + } + public void Open_mem(byte[] v) {} + public Object Under() {return src_stream;} + public int Read(byte[] bry, int bgn, int len) { + if (read_done) return Io_download_http.Read_done; + if (xrg.Prog_cancel()) {read_failed = true; return Io_download_http.Read_done;} + try { + int read = src_stream.read(bry, bgn, len); + xfer_fmt.Prog(read); + return read; + } + catch (Exception e) { + Err_handle(e); + return Io_download_http.Read_done; + } + } + private Io_url session_fil = null; + private boolean rls_done = false; + public long Skip(long len) {return 0;} + public void Rls() { + if (rls_done) return; + try { + read_done = true; + if (prog_dlg != null) { + xfer_fmt.Term(); + } + if (session_fil == null && prog_dlg != null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt"); + if (read_failed) { + } + else { + if (prog_dlg != null) + prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download pass: src='~{0}' trg='~{1}'", src_str, xrg.Trg().Raw()); + xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_pass); + } + xrg.Prog_running_(false); + } + catch (Exception e) {Err_.Noop(e);} // ignore close errors; also Err_handle calls Rls() so it would be circular + finally { + try {if (src_stream != null) src_stream.close();} + catch (Exception e) {Err_.Noop(e);} // ignore failures when cleaning up + if (src_conn != null) src_conn.disconnect(); + src_stream = null; + src_conn = null; + rls_done = true; + } + } + private void Err_handle(Exception exc) { + read_done = read_failed = true; + len = -1; + xrg.Rslt_err_(exc); + if (Type_adp_.Eq_typeSafe(exc, java.net.UnknownHostException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_host_not_found); + else if (Type_adp_.Eq_typeSafe(exc, java.io.FileNotFoundException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + else xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_unknown); + if (prog_dlg != null && !xrg.Prog_cancel()) { + if (session_fil == null) session_fil = prog_dlg.Log_wkr().Session_dir().GenSubFil("internet.txt"); + prog_dlg.Log_wkr().Log_msg_to_url_fmt(session_fil, "download fail: src='~{0}' trg='~{1}' error='~{2}'", src_str, xrg.Trg().Raw(), Err_.Message_lang(exc)); + } + this.Rls(); + } +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_deleteDir.java b/100_core/src/gplx/core/ios/IoEngine_xrg_deleteDir.java new file mode 100644 index 000000000..2da411c37 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_deleteDir.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.consoles.*; import gplx.core.criterias.*; +public class IoEngine_xrg_deleteDir { + public Io_url Url() {return url;} public IoEngine_xrg_deleteDir Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean Recur() {return recur;} public IoEngine_xrg_deleteDir Recur_() {return Recur_(true);} public IoEngine_xrg_deleteDir Recur_(boolean v) {recur = v; return this;} private boolean recur = false; + public boolean ReadOnlyFails() {return readOnlyFails;} public IoEngine_xrg_deleteDir ReadOnlyFails_off() {return ReadOnlyFails_(false);} public IoEngine_xrg_deleteDir ReadOnlyFails_(boolean v) {readOnlyFails = v; return this;} private boolean readOnlyFails = true; + public boolean MissingIgnored() {return missingIgnored;} public IoEngine_xrg_deleteDir MissingIgnored_() {return MissingIgnored_(true);} public IoEngine_xrg_deleteDir MissingIgnored_(boolean v) {missingIgnored = v; return this;} private boolean missingIgnored = true; + public Criteria MatchCrt() {return matchCrt;} public IoEngine_xrg_deleteDir MatchCrt_(Criteria v) {matchCrt = v; return this;} Criteria matchCrt = Criteria_.All; + public Criteria SubDirScanCrt() {return subDirScanCrt;} public IoEngine_xrg_deleteDir SubDirScanCrt_(Criteria v) {subDirScanCrt = v; return this;} Criteria subDirScanCrt = Criteria_.All; + public Console_adp UsrDlg() {return usrDlg;} public IoEngine_xrg_deleteDir UsrDlg_(Console_adp v) {usrDlg = v; return this;} Console_adp usrDlg = Console_adp_.Noop; + public void Exec() {IoEnginePool.Instance.Get_by(url.Info().EngineKey()).DeleteDirDeep(this);} + public static IoEngine_xrg_deleteDir new_(Io_url url) { + IoEngine_xrg_deleteDir rv = new IoEngine_xrg_deleteDir(); + rv.url = url; + return rv; + } IoEngine_xrg_deleteDir() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_deleteFil.java b/100_core/src/gplx/core/ios/IoEngine_xrg_deleteFil.java new file mode 100644 index 000000000..41cd7f61b --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_deleteFil.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoEngine_xrg_deleteFil extends IoEngine_xrg_fil_affects1_base { + @gplx.New public IoEngine_xrg_deleteFil Url_(Io_url val) {Url_set(val); return this;} + public IoEngine_xrg_deleteFil ReadOnlyFails_off() {return ReadOnlyFails_(false);} public IoEngine_xrg_deleteFil ReadOnlyFails_(boolean v) {ReadOnlyFails_set(v); return this;} + public IoEngine_xrg_deleteFil MissingFails_off() {return MissingFails_(false);} public IoEngine_xrg_deleteFil MissingFails_(boolean v) {MissingFails_set(v); return this;} + @Override public void Exec() {IoEnginePool.Instance.Get_by(this.Url().Info().EngineKey()).DeleteFil_api(this);} + public static IoEngine_xrg_deleteFil proto_() {return new IoEngine_xrg_deleteFil();} + public static IoEngine_xrg_deleteFil new_(Io_url url) { + IoEngine_xrg_deleteFil rv = new IoEngine_xrg_deleteFil(); + rv.Url_set(url); + return rv; + } IoEngine_xrg_deleteFil() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_downloadFil.java b/100_core/src/gplx/core/ios/IoEngine_xrg_downloadFil.java new file mode 100644 index 000000000..911620ecf --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_downloadFil.java @@ -0,0 +1,71 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.brys.fmtrs.*; +import gplx.core.ios.streams.*; +public class IoEngine_xrg_downloadFil { + public String Src() {return src;} public IoEngine_xrg_downloadFil Src_(String v) {src = v; return this;} private String src; + public Io_url Trg() {return trg;} public IoEngine_xrg_downloadFil Trg_(Io_url v) {trg = v; return this;} private Io_url trg; + public byte Rslt() {return rslt;} public IoEngine_xrg_downloadFil Rslt_(byte v) {rslt = v; return this;} private byte rslt = Rslt_pass; + public Exception Rslt_err() {return rslt_err;} public IoEngine_xrg_downloadFil Rslt_err_(Exception v) {rslt_err = v; return this;} private Exception rslt_err; + public String Rslt_err_str() { + return rslt_err == null ? "none" : Err_.Message_gplx_full(rslt_err); + } + public String User_agent() {return user_agent;} public IoEngine_xrg_downloadFil User_agent_(String v) {user_agent = v; return this;} private String user_agent; + public Gfo_usr_dlg Prog_dlg() {return prog_dlg;} public IoEngine_xrg_downloadFil Prog_dlg_(Gfo_usr_dlg v) {prog_dlg = v; download_fmt.Ctor(prog_dlg); return this;} private Gfo_usr_dlg prog_dlg; + public Bry_fmtr Prog_fmtr() {return prog_fmtr;} private final Bry_fmtr prog_fmtr = Bry_fmtr.new_("~{download_header}: ~{download_read} of ~{download_length} kb;", "download_header", "download_url", "download_read", "download_length"); + public String Prog_fmt_hdr() {return prog_fmt_hdr;} public IoEngine_xrg_downloadFil Prog_fmt_hdr_(String v) {prog_fmt_hdr = v; return this;} private String prog_fmt_hdr = ""; // NOTE: must init to "", else null ref when building String + public boolean Prog_cancel() {return prog_cancel;} public IoEngine_xrg_downloadFil Prog_cancel_y_() {prog_cancel = true; return this;} private volatile boolean prog_cancel; + public boolean Prog_running() {return prog_running;} public IoEngine_xrg_downloadFil Prog_running_(boolean v) {prog_running = v; return this;} private boolean prog_running; + public long Src_content_length() {return src_content_length;} public IoEngine_xrg_downloadFil Src_content_length_(long v) {src_content_length = v; return this;} private long src_content_length; + public DateAdp Src_last_modified() {return src_last_modified;} public IoEngine_xrg_downloadFil Src_last_modified_(DateAdp v) {src_last_modified = v; return this;} private DateAdp src_last_modified; + public boolean Src_last_modified_query() {return src_last_modified_query;} public IoEngine_xrg_downloadFil Src_last_modified_query_(boolean v) {src_last_modified_query = v; return this;} private boolean src_last_modified_query; + public String Trg_engine_key() {return trg_engine_key;} public IoEngine_xrg_downloadFil Trg_engine_key_(String v) {trg_engine_key = v; return this;} private String trg_engine_key = IoEngine_.SysKey; + public Io_download_fmt Download_fmt() {return download_fmt;} private final Io_download_fmt download_fmt = new Io_download_fmt(); + public boolean Exec() {return IoEnginePool.Instance.Get_by(trg.Info().EngineKey()).DownloadFil(this);} + public Io_stream_rdr Exec_as_rdr() {return IoEnginePool.Instance.Get_by(IoEngine_.SysKey).DownloadFil_as_rdr(this);} + public boolean Exec_meta_only() {return exec_meta_only;} private boolean exec_meta_only; + public byte[] Exec_as_bry(String src) { + this.Src_(src); this.Trg_(trg_mem); + download_fmt.Download_init(src, prog_fmt_hdr); // NOTE: must set src else NULL error + boolean pass = IoEnginePool.Instance.Get_by(trg_engine_key).DownloadFil(this); + return pass ? Io_mgr.Instance.LoadFilBry(trg_mem) : null; + } private Io_url trg_mem = Io_url_.mem_fil_("mem/download.tmp"); + public boolean Exec_meta(String src) { + this.Src_(src); this.Trg_(trg_mem); // NOTE: set Trg_ else error in download proc + download_fmt.Download_init(src, prog_fmt_hdr); // NOTE: must set src else NULL error + exec_meta_only = true; + boolean rv = IoEnginePool.Instance.Get_by(trg_engine_key).DownloadFil(this); + exec_meta_only = false; + return rv; + } + public void Init(String src, Io_url trg) { + this.src = src; this.trg = trg; + prog_cancel = false; + rslt_err = null; + rslt = Rslt_pass; + prog_running = true; + download_fmt.Download_init(src, "downloading ~{src_name}: ~{prog_left} left (@ ~{prog_rate}); ~{prog_done} of ~{src_len} (~{prog_pct}%)"); + } + public static IoEngine_xrg_downloadFil new_(String src, Io_url trg) { + IoEngine_xrg_downloadFil rv = new IoEngine_xrg_downloadFil(); + rv.src = src; rv.trg = trg; + return rv; + } IoEngine_xrg_downloadFil() {} + public static final byte Rslt_pass = 0, Rslt_fail_host_not_found = 1, Rslt_fail_file_not_found = 2, Rslt_fail_unknown = 3; +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_fil_affects1_base.java b/100_core/src/gplx/core/ios/IoEngine_xrg_fil_affects1_base.java new file mode 100644 index 000000000..442b4ae6c --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_fil_affects1_base.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoEngine_xrg_fil_affects1_base { + public Io_url Url() {return url;} public void Url_set(Io_url v) {url = v;} Io_url url; + public IoEngine_xrg_fil_affects1_base Url_(Io_url v) {url = v; return this;} + public boolean MissingFails() {return missingFails;} public void MissingFails_set(boolean v) {missingFails = v;} private boolean missingFails = true; + public boolean ReadOnlyFails() {return readOnlyFails;} public void ReadOnlyFails_set(boolean v) {readOnlyFails = v;} private boolean readOnlyFails = true; + @gplx.Virtual public void Exec() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_loadFilStr.java b/100_core/src/gplx/core/ios/IoEngine_xrg_loadFilStr.java new file mode 100644 index 000000000..645f0d4f5 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_loadFilStr.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.texts.*; import gplx.core.envs.*; +public class IoEngine_xrg_loadFilStr { + public Io_url Url() {return url;} public IoEngine_xrg_loadFilStr Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean MissingIgnored() {return missingIgnored;} public IoEngine_xrg_loadFilStr MissingIgnored_() {return MissingIgnored_(true);} public IoEngine_xrg_loadFilStr MissingIgnored_(boolean v) {missingIgnored = v; return this;} private boolean missingIgnored = false; + public boolean BomUtf8Convert() {return bomUtf8Convert;} public IoEngine_xrg_loadFilStr BomUtf8Convert_(boolean v) {bomUtf8Convert = v; return this;} private boolean bomUtf8Convert = true; + public String Exec() { + String s = IoEnginePool.Instance.Get_by(url.Info().EngineKey()).LoadFilStr(this); + if (bomUtf8Convert && String_.Len(s) > 0 && String_.CodePointAt(s, 0) == Bom_Utf8) { + s = String_.Mid(s, 1); + UsrDlg_.Instance.Warn(UsrMsg.new_("UTF8 BOM removed").Add("url", url.Xto_api())); + } + return s; + } + public String[] ExecAsStrAry() {return String_.Split(Exec(), String_.CrLf);} + public String[] ExecAsStrAryLnx() { + String raw = Exec(); + if (String_.Len(raw) == 0) return String_.Ary_empty; + return String_.Split(raw, Op_sys.Nl_char_lnx, false); + } + int Bom_Utf8 = 65279; // U+FEFF; see http://en.wikipedia.org/wiki/Byte_order_mark + public static IoEngine_xrg_loadFilStr new_(Io_url url) { + IoEngine_xrg_loadFilStr rv = new IoEngine_xrg_loadFilStr(); + rv.url = url; + return rv; + } IoEngine_xrg_loadFilStr() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_openRead.java b/100_core/src/gplx/core/ios/IoEngine_xrg_openRead.java new file mode 100644 index 000000000..feafce755 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_openRead.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.ios.streams.*; +public class IoEngine_xrg_openRead { + public Io_url Url() {return url;} Io_url url; + public String ErrMsg() {return errMsg;} private String errMsg; + public IoStream ExecAsIoStreamOrFail() {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).OpenStreamRead(url);} + public IoStream ExecAsIoStreamOrNull() { + try {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).OpenStreamRead(url);} + catch (Exception exc) { + errMsg = Err_.Message_lang(exc); + return IoStream_.Null; + } + } + public static IoEngine_xrg_openRead new_(Io_url url) { + IoEngine_xrg_openRead rv = new IoEngine_xrg_openRead(); + rv.url = url; + return rv; + } IoEngine_xrg_openRead() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_openWrite.java b/100_core/src/gplx/core/ios/IoEngine_xrg_openWrite.java new file mode 100644 index 000000000..e2ddc97ee --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_openWrite.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.ios.streams.*; +public class IoEngine_xrg_openWrite { + public Io_url Url() {return url;} public IoEngine_xrg_openWrite Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean ReadOnlyIgnored() {return readOnlyIgnored;} public IoEngine_xrg_openWrite ReadOnlyIgnored_() {return ReadOnlyIgnored_(true);} public IoEngine_xrg_openWrite ReadOnlyIgnored_(boolean v) {readOnlyIgnored = v; return this;} private boolean readOnlyIgnored = false; + public boolean MissingIgnored() {return missingIgnored;} public IoEngine_xrg_openWrite MissingIgnored_() {return MissingIgnored_(true);} public IoEngine_xrg_openWrite MissingIgnored_(boolean v) {missingIgnored = v; return this;} private boolean missingIgnored = false; + public byte Mode() {return mode;} public IoEngine_xrg_openWrite Mode_(byte v) {mode = v; return this;} private byte mode = IoStream_.Mode_wtr_create; + public IoEngine_xrg_openWrite Mode_update_() {return Mode_(IoStream_.Mode_wtr_update);} + public IoStream Exec() {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).OpenStreamWrite(this);} + public static IoEngine_xrg_openWrite new_(Io_url url) { + IoEngine_xrg_openWrite rv = new IoEngine_xrg_openWrite(); + rv.url = url; + return rv; + } IoEngine_xrg_openWrite() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_queryDir.java b/100_core/src/gplx/core/ios/IoEngine_xrg_queryDir.java new file mode 100644 index 000000000..a7ed4e1ef --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_queryDir.java @@ -0,0 +1,55 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.consoles.*; import gplx.core.criterias.*; +public class IoEngine_xrg_queryDir { + public Io_url Url() {return url;} public IoEngine_xrg_queryDir Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean Recur() {return recur;} public IoEngine_xrg_queryDir Recur_() {return Recur_(true);} public IoEngine_xrg_queryDir Recur_(boolean val) {recur = val; return this;} private boolean recur = false; + public boolean DirInclude() {return dirInclude;} public IoEngine_xrg_queryDir DirInclude_() {return DirInclude_(true);} public IoEngine_xrg_queryDir DirInclude_(boolean val) {dirInclude = val; return this;} private boolean dirInclude = false; + public Criteria FilCrt() {return filCrt;} public IoEngine_xrg_queryDir FilCrt_(Criteria val) {filCrt = val; return this;} Criteria filCrt; + public Criteria DirCrt() {return dirCrt;} public IoEngine_xrg_queryDir DirCrt_(Criteria val) {dirCrt = val; return this;} Criteria dirCrt; + public Criteria SubDirScanCrt() {return subDirScanCrt;} public IoEngine_xrg_queryDir SubDirScanCrt_(Criteria val) {subDirScanCrt = val; return this;} Criteria subDirScanCrt; + public IoEngine_xrg_queryDir DirOnly_() { + DirInclude_(true); + filCrt = Criteria_.None; + return this; + } + + public Console_adp UsrDlg() {return usrDlg;} public IoEngine_xrg_queryDir UsrDlg_(Console_adp val) {usrDlg = val; return this;} Console_adp usrDlg = Console_adp_.Noop; + public IoEngine_xrg_queryDir FilPath_(String val) { + Criteria_ioMatch crt = Criteria_ioMatch.parse(true, val, url.Info().CaseSensitive()); + filCrt = Criteria_fld.new_(IoItm_base_.Prop_Path, crt); + return this; + } + public IoItmDir ExecAsDir() {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).QueryDirDeep(this);} + public Io_url[] ExecAsUrlAry() {return ExecAsItmHash().XtoIoUrlAry();} + public IoItmHash ExecAsItmHash() { + Criteria crt = dirInclude ? Criteria_.All : Criteria_fld.new_(IoItm_base_.Prop_Type, Criteria_.eq_(IoItmFil.Type_Fil)); + IoItmHash list = ExecAsDir().XtoIoItmList(crt); + list.Sort_by(IoItmBase_comparer_nest.Instance); + return list; + } + public static IoEngine_xrg_queryDir new_(Io_url url) { + IoEngine_xrg_queryDir rv = new IoEngine_xrg_queryDir(); + rv.url = url; + rv.filCrt = Criteria_fld.new_(IoItm_base_.Prop_Path, Criteria_.All); + rv.dirCrt = Criteria_fld.new_(IoItm_base_.Prop_Path, Criteria_.All); + rv.subDirScanCrt = Criteria_fld.new_(IoItm_base_.Prop_Path, Criteria_.All); + return rv; + } IoEngine_xrg_queryDir() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_recycleFil.java b/100_core/src/gplx/core/ios/IoEngine_xrg_recycleFil.java new file mode 100644 index 000000000..a909fc5bf --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_recycleFil.java @@ -0,0 +1,59 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; +public class IoEngine_xrg_recycleFil extends IoEngine_xrg_fil_affects1_base { + public IoEngine_xrg_recycleFil MissingFails_off() {return MissingFails_(false);} public IoEngine_xrg_recycleFil MissingFails_(boolean v) {MissingFails_set(v); return this;} + + public int Mode() {return mode;} public IoEngine_xrg_recycleFil Mode_(int v) {mode = v; return this;} int mode; + public String AppName() {return appName;} public IoEngine_xrg_recycleFil AppName_(String val) {appName = val; return this;} private String appName = "unknown_app"; + public Guid_adp Uuid() {return uuid;} public IoEngine_xrg_recycleFil Uuid_(Guid_adp val) {uuid = val; return this;} Guid_adp uuid; + public boolean Uuid_include() {return uuid_include;} public IoEngine_xrg_recycleFil Uuid_include_() {uuid_include = true; return this;} private boolean uuid_include; + public DateAdp Time() {return time;} public IoEngine_xrg_recycleFil Time_(DateAdp val) {time = val; return this;} DateAdp time; + public List_adp RootDirNames() {return rootDirNames;} public IoEngine_xrg_recycleFil RootDirNames_(List_adp val) {rootDirNames = val; return this;} List_adp rootDirNames; + public Io_url RecycleUrl() { + String dayName = time.XtoStr_fmt("yyyyMMdd"), timeName = time.XtoStr_fmt("hhmmssfff"); + String rootDirStr = ConcatWith_ary(this.Url().Info().DirSpr(), rootDirNames); + Io_url recycleDir = this.Url().OwnerRoot().GenSubDir_nest(rootDirStr, dayName); + String uuidStr = uuid_include ? uuid.To_str() : ""; + return recycleDir.GenSubFil_ary(appName, ";", timeName, ";", uuidStr, ";", String_.LimitToFirst(this.Url().NameAndExt(), 128)); + } + String ConcatWith_ary(String separator, List_adp ary) { + String_bldr sb = String_bldr_.new_(); + int aryLen = ary.Count(); + for (int i = 0; i < aryLen; i++) { + if (i != 0) sb.Add(separator); + Object val = ary.Get_at(i); + sb.Add_obj(Object_.Xto_str_strict_or_empty(val)); + } + return sb.To_str(); + } + @Override public void Exec() { + IoEnginePool.Instance.Get_by(this.Url().Info().EngineKey()).RecycleFil(this); + } + public IoEngine_xrg_recycleFil(int v) { + mode = v; + time = Datetime_now.Get(); + uuid = Guid_adp_.New(); + rootDirNames = List_adp_.New(); rootDirNames.Add("z_trash"); + } + public static IoEngine_xrg_recycleFil sysm_(Io_url url) {return new IoEngine_xrg_recycleFil(SysmConst);} + public static IoEngine_xrg_recycleFil gplx_(Io_url url) {IoEngine_xrg_recycleFil rv = new IoEngine_xrg_recycleFil(GplxConst); rv.Url_set(url); return rv;} + public static IoEngine_xrg_recycleFil proto_() {return gplx_(Io_url_.Empty);} + public static final int GplxConst = 0, SysmConst = 1; +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_saveFilStr.java b/100_core/src/gplx/core/ios/IoEngine_xrg_saveFilStr.java new file mode 100644 index 000000000..ec4419977 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_saveFilStr.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.texts.*; +public class IoEngine_xrg_saveFilStr { + public Io_url Url() {return url;} public IoEngine_xrg_saveFilStr Url_(Io_url val) {url = val; return this;} Io_url url; + public String Text() {return text;} public IoEngine_xrg_saveFilStr Text_(String val) {text = val; return this;} private String text = ""; + public boolean Append() {return append;} public IoEngine_xrg_saveFilStr Append_() {return Append_(true);} public IoEngine_xrg_saveFilStr Append_(boolean val) {append = val; return this;} private boolean append = false; + public void Exec() { + if (String_.Eq(text, "") && append) return; // no change; don't bother writing to disc + IoEnginePool.Instance.Get_by(url.Info().EngineKey()).SaveFilText_api(this); + } + public static IoEngine_xrg_saveFilStr new_(Io_url url, String text) { + IoEngine_xrg_saveFilStr rv = new IoEngine_xrg_saveFilStr(); + rv.url = url; rv.text = text; + return rv; + } IoEngine_xrg_saveFilStr() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_xferDir.java b/100_core/src/gplx/core/ios/IoEngine_xrg_xferDir.java new file mode 100644 index 000000000..378e9f6bb --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_xferDir.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.criterias.*; +public class IoEngine_xrg_xferDir { + public boolean Type_move() {return move;} public boolean Type_copy() {return !move;} private boolean move = false; + public Io_url Src() {return src;} public IoEngine_xrg_xferDir Src_(Io_url val) {src = val; return this;} Io_url src; + public Io_url Trg() {return trg;} public IoEngine_xrg_xferDir Trg_(Io_url val) {trg = val; return this;} Io_url trg; + public boolean Recur() {return recur;} public IoEngine_xrg_xferDir Recur_() {recur = true; return this;} private boolean recur = false; + public boolean Overwrite() {return overwrite;} public IoEngine_xrg_xferDir Overwrite_() {return Overwrite_(true);} public IoEngine_xrg_xferDir Overwrite_(boolean v) {overwrite = v; return this;} private boolean overwrite = false; + public boolean ReadOnlyFails() {return readOnlyFails;} public IoEngine_xrg_xferDir ReadOnlyFails_() {return ReadOnlyFails_(true);} public IoEngine_xrg_xferDir ReadOnlyFails_(boolean v) {readOnlyFails = v; return this;} private boolean readOnlyFails = false; + public Criteria MatchCrt() {return matchCrt;} public IoEngine_xrg_xferDir MatchCrt_(Criteria v) {matchCrt = v; return this;} Criteria matchCrt = Criteria_.All; + public Criteria SubDirScanCrt() {return subDirScanCrt;} public IoEngine_xrg_xferDir SubDirScanCrt_(Criteria v) {subDirScanCrt = v; return this;} Criteria subDirScanCrt = Criteria_.All; + public void Exec() {IoEnginePool.Instance.Get_by(src.Info().EngineKey()).XferDir(this);} + public static IoEngine_xrg_xferDir move_(Io_url src, Io_url trg) {return new_(src, trg, true);} + public static IoEngine_xrg_xferDir copy_(Io_url src, Io_url trg) {return new_(src, trg, false);} + static IoEngine_xrg_xferDir new_(Io_url src, Io_url trg, boolean move) { + IoEngine_xrg_xferDir rv = new IoEngine_xrg_xferDir(); + rv.src = src; rv.trg = trg; rv.move = move; + return rv; + } IoEngine_xrg_xferDir() {} +} diff --git a/100_core/src/gplx/core/ios/IoEngine_xrg_xferFil.java b/100_core/src/gplx/core/ios/IoEngine_xrg_xferFil.java new file mode 100644 index 000000000..1e9b606ac --- /dev/null +++ b/100_core/src/gplx/core/ios/IoEngine_xrg_xferFil.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoEngine_xrg_xferFil { + public boolean Type_move() {return move;} private boolean move = false; + public Io_url Src() {return src;} Io_url src; + public Io_url Trg() {return trg;} Io_url trg; + public boolean Overwrite() {return overwrite;} public IoEngine_xrg_xferFil Overwrite_() {return Overwrite_(true);} public IoEngine_xrg_xferFil Overwrite_(boolean v) {overwrite = v; return this;} private boolean overwrite = false; + public boolean ReadOnlyFails() {return readOnlyFails;} public IoEngine_xrg_xferFil ReadOnlyFails_off() {return ReadOnlyFails_(false);} public IoEngine_xrg_xferFil ReadOnlyFails_(boolean v) {readOnlyFails = v; return this;} private boolean readOnlyFails = true; + public boolean MissingFails() {return missingFails;} public IoEngine_xrg_xferFil MissingFails_off() {return MissingFails_(false);} public IoEngine_xrg_xferFil MissingFails_(boolean v) {missingFails = v; return this;} private boolean missingFails = true; + public void Exec() {IoEnginePool.Instance.Get_by(src.Info().EngineKey()).XferFil(this);} + public static IoEngine_xrg_xferFil move_(Io_url src, Io_url trg) {return new_(src, trg, true);} + public static IoEngine_xrg_xferFil copy_(Io_url src, Io_url trg) {return new_(src, trg, false);} + static IoEngine_xrg_xferFil new_(Io_url src, Io_url trg, boolean move) { + IoEngine_xrg_xferFil rv = new IoEngine_xrg_xferFil(); + rv.src = src; rv.trg = trg; rv.move = move; + return rv; + } IoEngine_xrg_xferFil() {} +} diff --git a/100_core/src/gplx/core/ios/IoErr.java b/100_core/src/gplx/core/ios/IoErr.java new file mode 100644 index 000000000..78560ab70 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoErr.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoErr { + public static String Namespace = "gplx.core.ios."; + public static String FileIsReadOnly_key = Namespace + "FileIsReadOnlyError"; + public static String FileNotFound_key = Namespace + "FileNotFoundError"; + public static Err FileIsReadOnly(Io_url url) { + return Err_.new_(FileIsReadOnly_key, "file is read-only", "url", url.Xto_api()).Trace_ignore_add_1_(); + } + public static Err FileNotFound(String op, Io_url url) { + // file is missing -- op='copy' file='C:\a.txt' copyFile_target='D:\a.txt' + return Err_.new_(FileNotFound_key, "file not found", "op", op, "file", url.Xto_api()).Trace_ignore_add_1_(); + } +} \ No newline at end of file diff --git a/100_core/src/gplx/core/ios/IoItmAttrib.java b/100_core/src/gplx/core/ios/IoItmAttrib.java new file mode 100644 index 000000000..e60249725 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmAttrib.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoItmAttrib { + public boolean ReadOnly() {return readOnly;} public IoItmAttrib ReadOnly_() {return ReadOnly_(true);} public IoItmAttrib ReadOnly_(boolean val) {readOnly = val; return this;} private boolean readOnly; + public boolean Hidden() {return hidden;} public IoItmAttrib Hidden_() {return Hidden_(true);} public IoItmAttrib Hidden_(boolean val) {hidden = val; return this;} private boolean hidden; + public static IoItmAttrib readOnly_() {return new IoItmAttrib().ReadOnly_();} + public static IoItmAttrib hidden_() {return new IoItmAttrib().Hidden_();} + public static IoItmAttrib normal_() {return new IoItmAttrib().ReadOnly_(false).Hidden_(false);} +} diff --git a/100_core/src/gplx/core/ios/IoItmClassXtn.java b/100_core/src/gplx/core/ios/IoItmClassXtn.java new file mode 100644 index 000000000..0cfcf3ef0 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmClassXtn.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.type_xtns.*; +public class IoItmClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "ioItemType"; + @Override public Class UnderClass() {return int.class;} + public Object DefaultValue() {return IoItmDir.Type_Dir;} + public boolean Eq(Object lhs, Object rhs) {return ((IoItm_base)lhs).compareTo(rhs) == CompareAble_.Same;} + @Override public Object ParseOrNull(String raw) { + String rawLower = String_.Lower(raw); + if (String_.Eq(rawLower, "dir")) return IoItmDir.Type_Dir; + else if (String_.Eq(rawLower, "fil")) return IoItmFil.Type_Fil; + else throw Err_.new_unhandled(raw); + } + @Override public Object XtoDb(Object obj) {return Int_.cast(obj);} + public static final IoItmClassXtn Instance = new IoItmClassXtn(); IoItmClassXtn() {} +} diff --git a/100_core/src/gplx/core/ios/IoItmDir.java b/100_core/src/gplx/core/ios/IoItmDir.java new file mode 100644 index 000000000..6538167e7 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmDir.java @@ -0,0 +1,71 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.criterias.*; +public class IoItmDir extends IoItm_base { + public boolean Exists() {return exists;} public void Exists_set(boolean v) {exists = v;} private boolean exists = true; + @Override public int TypeId() {return Type_Dir;} @Override public boolean Type_dir() {return true;} @Override public boolean Type_fil() {return false;} public static final int Type_Dir = 1; + @gplx.New public IoItmDir XtnProps_set(String key, Object val) {return (IoItmDir)super.XtnProps_set(key, val);} + public IoItmList SubDirs() {return subDirs;} IoItmList subDirs; + public IoItmList SubFils() {return subFils;} IoItmList subFils; + public IoItmHash XtoIoItmList(Criteria crt) { + IoItmHash rv = IoItmHash.list_(this.Url()); + XtoItmList_recur(rv, this, crt); + return rv; + } + Io_url[] XtoIoUrlAry() { + IoItmHash list = this.XtoIoItmList(Criteria_.All); +//#plat_wce list.Sort(); // NOTE: on wce, subFils retrieved in unexpected order; createTime vs pathString + int count = list.Count(); + Io_url[] rv = new Io_url[count]; + for (int i = 0; i < count; i++) + rv[i] = list.Get_at(i).Url(); + return rv; + } + public IoItmDir FetchDeepOrNull(Io_url findDirUrl) { + String dirSpr = this.Url().Info().DirSpr(); int dirSprLen = String_.Len(dirSpr); + String currDirStr = this.Url().Raw(); + String findDirStr = findDirUrl.Raw(); + if (!String_.Has_at_bgn(findDirStr, currDirStr)) return null; // findUrl must start with currUrl; + String findName = String_.DelEnd(currDirStr, dirSprLen); // seed findName for String_.MidByLen below; + IoItmDir curDir = this; + while (true) { + findDirStr = String_.DelBgn(findDirStr, String_.Len(findName) + dirSprLen); // NOTE: findName will never have trailingDirSpr; subDirs.Get_by() takes NameOnly; ex: "dir" not "dir\" + int nextDirSprPos = String_.FindFwd(findDirStr, dirSpr); if (nextDirSprPos == String_.Find_none) nextDirSprPos = String_.Len(findDirStr); + findName = String_.MidByLen(findDirStr, 0, nextDirSprPos); + if (String_.Eq(findDirStr, "")) return curDir; // findDirStr completely removed; all parts match; return curDir + curDir = IoItmDir_.as_(curDir.subDirs.Get_by(findName)); // try to find dir + if (curDir == null) return null; // dir not found; exit; NOTE: if dir found, loop restarts; with curDir as either findDir, or owner of findDir + } + } + void XtoItmList_recur(IoItmHash list, IoItmDir curDir, Criteria dirCrt) { + for (Object subFilObj : curDir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + list.Add(subFil); + } + for (Object subDirObj : curDir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + if (dirCrt.Matches(subDir)) list.Add(subDir); + XtoItmList_recur(list, subDir, dirCrt); + } + } + @gplx.Internal protected IoItmDir(boolean caseSensitive) { + subDirs = IoItmList.new_(this, caseSensitive); + subFils = IoItmList.new_(this, caseSensitive); + } +} diff --git a/100_core/src/gplx/core/ios/IoItmDir_.java b/100_core/src/gplx/core/ios/IoItmDir_.java new file mode 100644 index 000000000..23efce2e7 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmDir_.java @@ -0,0 +1,55 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoItmDir_ { + public static IoItmDir as_(Object obj) {return obj instanceof IoItmDir ? (IoItmDir)obj : null;} + public static final IoItmDir Null = null_(); + public static IoItmDir top_(Io_url url) {return scan_(url);} + public static IoItmDir scan_(Io_url url) { + IoItmDir rv = new IoItmDir(url.Info().CaseSensitive()); + rv.ctor_IoItmBase_url(url); + return rv; + } + public static IoItmDir sub_(String name) { + IoItmDir rv = new IoItmDir(Bool_.Y); + rv.ctor_IoItmBase_url(Io_url_.mem_dir_("mem/" + name)); + return rv; + } + static IoItmDir null_() { + IoItmDir rv = new IoItmDir(true); // TODO_OLD: NULL should be removed + rv.ctor_IoItmBase_url(Io_url_.Empty); + rv.Exists_set(false); + return rv; + } + public static void Make(IoItmDir dir) { + Io_mgr.Instance.CreateDir(dir.Url()); + int len = dir.SubDirs().Count(); + for (int i = 0; i < len; ++i) { + IoItmDir sub_dir = (IoItmDir)dir.SubDirs().Get_at(i); + Make(sub_dir); + } + len = dir.SubFils().Count(); + for (int i = 0; i < len; ++i) { + IoItmFil sub_fil = (IoItmFil)dir.SubFils().Get_at(i); + String text = String_.Repeat("a", (int)sub_fil.Size()); + Io_url sub_url = sub_fil.Url(); + Io_mgr.Instance.SaveFilStr(sub_url, text); + Io_mgr.Instance.UpdateFilModifiedTime(sub_url, sub_fil.ModifiedTime()); + } + } +} diff --git a/100_core/src/gplx/core/ios/IoItmFil.java b/100_core/src/gplx/core/ios/IoItmFil.java new file mode 100644 index 000000000..ed39a6e89 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmFil.java @@ -0,0 +1,43 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.langs.gfs.*; +public class IoItmFil extends IoItm_base { + @Override public int TypeId() {return IoItmFil.Type_Fil;} @Override public boolean Type_dir() {return false;} @Override public boolean Type_fil() {return true;} public static final int Type_Fil = 2; + public boolean Exists() {return size != Size_invalid;} // NOTE: questionable logic, but preserved for historical reasons; requires that length be set to -1 if !.exists + public DateAdp ModifiedTime() {return modifiedTime;} + public IoItmFil ModifiedTime_(DateAdp val) {modifiedTime = val; return this;} DateAdp modifiedTime; + public IoItmFil ModifiedTime_(String val) {return ModifiedTime_(DateAdp_.parse_gplx(val));} + @gplx.Virtual public long Size() {return size;} public IoItmFil Size_(long val) {size = val; return this;} private long size; + public IoItmAttrib Attrib() {return attrib;} public IoItmFil Attrib_(IoItmAttrib val) {attrib = val; return this;} IoItmAttrib attrib = IoItmAttrib.normal_(); + public boolean ReadOnly() {return attrib.ReadOnly();} public IoItmFil ReadOnly_(boolean val) {attrib.ReadOnly_(val); return this;} + @gplx.New public IoItmFil XtnProps_set(String key, Object val) {return (IoItmFil)super.XtnProps_set(key, val);} + + @gplx.Internal protected IoItmFil ctor_IoItmFil(Io_url url, long size, DateAdp modifiedTime) { + ctor_IoItmBase_url(url); this.size = size; this.modifiedTime = modifiedTime; + return this; + } + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, IoItmFil_.Prop_Size)) return size; + else if (ctx.Match(k, IoItmFil_.Prop_Modified)) return modifiedTime; + else return super.Invk(ctx, ikey, k, m); + } + @gplx.Internal protected IoItmFil() {} + public static final long Size_invalid = -1; + public static final int Size_invalid_int = -1; +} diff --git a/100_core/src/gplx/core/ios/IoItmFil_.java b/100_core/src/gplx/core/ios/IoItmFil_.java new file mode 100644 index 000000000..667d58800 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmFil_.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoItmFil_ { + public static IoItmFil as_(Object obj) {return obj instanceof IoItmFil ? (IoItmFil)obj : null;} + public static final String + Prop_Size = "size" + , Prop_Modified = "modified"; + public static IoItmFil new_(Io_url url, long size, DateAdp created, DateAdp modified) {return new IoItmFil().ctor_IoItmFil(url, size, modified);} + public static IoItmFil sub_(String name, long size, DateAdp modified) { + IoItmFil rv = new IoItmFil(); + rv.ctor_IoItmFil(Io_url_.mem_fil_("mem/" + name), size, modified); + rv.Name_(name); + return rv; + } +} diff --git a/100_core/src/gplx/core/ios/IoItmFil_mem.java b/100_core/src/gplx/core/ios/IoItmFil_mem.java new file mode 100644 index 000000000..c58e0243e --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmFil_mem.java @@ -0,0 +1,39 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.ios.streams.*; /*IoStream_mem*/ import gplx.core.texts.*; /*Encoding_*/ +class IoItmFil_mem extends IoItmFil { public static IoItmFil_mem as_(Object obj) {return obj instanceof IoItmFil_mem ? (IoItmFil_mem)obj : null;} + @gplx.Internal protected IoStream_mem Stream() {return stream;} IoStream_mem stream; // NOTE: using stream instead of Text, b/c no events for IoStream.Dispose; ex: stream.OpenStreamWrite; stream.Write("hi"); stream.Dispose(); "hi" would not be saved if Text is member variable + @Override public long Size() {return (int)stream.Len();} + public String Text() {return Text_get();} public void Text_set(String v) {stream = IoStream_mem.rdr_txt_(this.Url(), v);} + String Text_get() { + int len = (int)stream.Len(); + byte[] buffer = new byte[len]; + stream.Position_set(0); + stream.Read(buffer, 0, len); + return String_.new_u8(buffer); + } + public IoItmFil_mem Clone() {return new_(this.Url(), this.Size(), this.ModifiedTime(), this.Text());} + public static IoItmFil_mem new_(Io_url filPath, long size, DateAdp modified, String text) { + IoItmFil_mem rv = new IoItmFil_mem(); + rv.ctor_IoItmFil(filPath, size, modified); + rv.stream = IoStream_mem.rdr_txt_(filPath, text); + return rv; + } + public static final IoItmFil_mem Null = new_(Io_url_.Empty, -1, DateAdp_.MinValue, ""); // NOTE: size must be -1 for .Exists to be false; DATE:2015-05-16 +} diff --git a/100_core/src/gplx/core/ios/IoItmHash.java b/100_core/src/gplx/core/ios/IoItmHash.java new file mode 100644 index 000000000..f3d55f30b --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmHash.java @@ -0,0 +1,43 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoItmHash extends Ordered_hash_base { + public Io_url Url() {return url;} Io_url url; + public void Add(IoItm_base itm) {Add_base(MakeKey(itm.Url()), itm);} + public void Del(Io_url url) {Del(MakeKey(url));} + public IoItm_base Get_by(Io_url url) {return IoItm_base_.as_(Fetch_base(MakeKey(url)));} + @gplx.New public IoItm_base Get_at(int i) {return IoItm_base_.as_(Get_at_base(i));} + public Io_url[] XtoIoUrlAry() { + int count = this.Count(); + Io_url[] rv = new Io_url[count]; + for (int i = 0; i < count; i++) + rv[i] = this.Get_at(i).Url(); + return rv; + } + String MakeKey(Io_url url) {return url.XtoCaseNormalized();} + public static IoItmHash new_() { + IoItmHash rv = new IoItmHash(); + rv.url = null;//Io_url_.Empty; + return rv; + } IoItmHash() {} + public static IoItmHash list_(Io_url url) { + IoItmHash rv = new IoItmHash(); + rv.url = url; + return rv; + } +} diff --git a/100_core/src/gplx/core/ios/IoItmList.java b/100_core/src/gplx/core/ios/IoItmList.java new file mode 100644 index 000000000..d60bd1162 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItmList.java @@ -0,0 +1,76 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.lists.*; /*Ordered_hash_base*/ +public class IoItmList extends Ordered_hash_base { + public boolean Has(Io_url url) {return Has_base(MakeKey(url));} + public void Add(IoItm_base itm) { + if (ownerDir != null) itm.OwnerDir_set(ownerDir); + Add_base(MakeKey(itm.Url()), itm); + } + public void Del(Io_url url) { + String key = MakeKey(url); + IoItm_base itm = IoItm_base_.as_(Fetch_base(key)); if (itm == null) return; + itm.OwnerDir_set(null); + super.Del(key); + } + public Io_url[] XtoIoUrlAry() { + int count = this.Count(); + Io_url[] rv = new Io_url[count]; + for (int i = 0; i < count; i++) + rv[i] = IoItm_base_.as_(i).Url(); + return rv; + } + @Override public void Sort() {Sort_by(IoItmBase_comparer_nest.Instance);} + @Override protected Object Fetch_base(Object keyObj) { + String key = MakeKey((String)keyObj); + return super.Fetch_base(key); + } + @Override public void Del(Object keyObj) { + String key = MakeKey((String)keyObj); + super.Del(key); + } + String MakeKey(Io_url url) { + String itmName = url.Type_dir() ? url.NameOnly() : url.NameAndExt(); + return MakeKey(itmName); + } + String MakeKey(String s) { + return caseSensitive ? s : String_.Lower(s); + } + IoItmDir ownerDir; boolean caseSensitive; + @gplx.Internal protected static IoItmList new_(IoItmDir v, boolean caseSensitive) { + IoItmList rv = new IoItmList(); + rv.ownerDir = v; rv.caseSensitive = caseSensitive; + return rv; + } + @gplx.Internal protected static IoItmList list_(boolean caseSensitive) {return new_(null, caseSensitive);} +} +class IoItmBase_comparer_nest implements ComparerAble { + public int compare(Object lhsObj, Object rhsObj) { + IoItm_base lhsItm = (IoItm_base)lhsObj, rhsItm = (IoItm_base)rhsObj; + Io_url lhsUrl = lhsItm.Url(), rhsUrl = rhsItm.Url(); + return String_.Eq(lhsUrl.OwnerDir().Raw(), rhsUrl.OwnerDir().Raw()) // is same dir + ? CompareAble_.Compare_obj(lhsUrl.NameAndExt(), rhsUrl.NameAndExt()) // same dir: compare name + : CompareAble_.Compare_obj(DepthOf(lhsItm), DepthOf(rhsItm)); // diff dir: compare by depth; ex: c:\fil.txt < c:\dir\fil.txt + } + int DepthOf(IoItm_base itm) { + Io_url url = itm.Url(); + return String_.Count(url.OwnerDir().Raw(), url.Info().DirSpr()); // use OwnerDir, else dir.Raw will return extra dirSeparator + } + public static final IoItmBase_comparer_nest Instance = new IoItmBase_comparer_nest(); IoItmBase_comparer_nest() {} +} diff --git a/100_core/src/gplx/core/ios/IoItm_base.java b/100_core/src/gplx/core/ios/IoItm_base.java new file mode 100644 index 000000000..55f8d2c6b --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItm_base.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.langs.gfs.*; +public abstract class IoItm_base implements Gfo_invk, CompareAble { + public abstract int TypeId(); public abstract boolean Type_dir(); public abstract boolean Type_fil(); + public Io_url Url() {return ownerDir == null ? url : ownerDir.Url().GenSubFil(name); /*NOTE: must call .Url*/} Io_url url; + public IoItmDir OwnerDir() {return ownerDir;} IoItmDir ownerDir; + public void OwnerDir_set(IoItmDir v) {if (v == this) throw Err_.new_wo_type("dir cannot be its own owner", "url", v.url.Raw()); + url = v == null && ownerDir != null + ? ownerDir.url.GenSubFil(name) // create url, since ownerDir will soon be null; NOTE: must call .url + : Io_url_.Empty; // delete url, since ownerDir will be avail + ownerDir = v; + } + public String Name() {return name;} private String name; + public IoItm_base Name_(String v) { + name = v; + if (ownerDir == null) url = url.OwnerDir().GenSubFil(name); + return this; + } + public Object XtnProps_get(String key) {return props.Get_by(key);} Hash_adp props = Hash_adp_.Noop; + public IoItm_base XtnProps_set(String key, Object val) { + if (props == Hash_adp_.Noop) props = Hash_adp_.New(); + props.Del(key); + props.Add(key, val); + return this; + } + public int compareTo(Object comp) {return url.compareTo(((IoItm_base)comp).url);} // NOTE: needed for comic importer (sort done on IoItmHash which contains IoItm_base) +// public Object Data_get(String name) {return Gfo_invk_.Invk_by_key(this, name);} + @gplx.Virtual public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, IoItm_base_.Prop_Type)) return this.TypeId(); + else if (ctx.Match(k, IoItm_base_.Prop_Path)) return this.Url(); + else if (ctx.Match(k, IoItm_base_.Prop_Title)) return this.Url().NameOnly(); // needed for gfio script criteria; + else if (ctx.Match(k, IoItm_base_.Prop_Ext)) return this.Url().Ext(); // needed for gfio script criteria; EX: where "ext LIKE '.java'" + else return Gfo_invk_.Rv_unhandled; + } + @gplx.Internal protected void ctor_IoItmBase_url(Io_url url) {this.url = url; this.name = url.NameAndExt();} + @gplx.Internal protected void ctor_IoItmBase_name(String name) {this.name = name;} +} diff --git a/100_core/src/gplx/core/ios/IoItm_base_.java b/100_core/src/gplx/core/ios/IoItm_base_.java new file mode 100644 index 000000000..d7b7add63 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItm_base_.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoItm_base_ { + public static IoItm_base as_(Object obj) {return obj instanceof IoItm_base ? (IoItm_base)obj : null;} + public static final String + Prop_Type = "type" + , Prop_Path = "url" + , Prop_Title = "title" + , Prop_Ext = "ext"; +} diff --git a/100_core/src/gplx/core/ios/IoItm_fxt.java b/100_core/src/gplx/core/ios/IoItm_fxt.java new file mode 100644 index 000000000..c7d57b87d --- /dev/null +++ b/100_core/src/gplx/core/ios/IoItm_fxt.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoItm_fxt { + public IoItmFil fil_wnt_(String s) {return fil_(Io_url_.wnt_fil_(s));} + public IoItmFil fil_(Io_url url) {return IoItmFil_.new_(url, 1, DateAdp_.parse_gplx("2001-01-01"), DateAdp_.parse_gplx("2001-01-01"));} + public IoItmDir dir_wnt_(String s) {return dir_(Io_url_.wnt_dir_(s));} + public IoItmDir dir_(Io_url url, IoItm_base... ary) { + IoItmDir rv = IoItmDir_.top_(url); + for (IoItm_base itm : ary) { + if (itm.Type_dir()) + rv.SubDirs().Add(itm); + else + rv.SubFils().Add(itm); + } + return rv; + } + public static IoItm_fxt new_() {return new IoItm_fxt();} IoItm_fxt() {} +} diff --git a/100_core/src/gplx/core/ios/IoRecycleBin.java b/100_core/src/gplx/core/ios/IoRecycleBin.java new file mode 100644 index 000000000..32c1efac2 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoRecycleBin.java @@ -0,0 +1,60 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; +public class IoRecycleBin { + public void Send(Io_url url) {Send_xrg(url).Exec();} + public IoEngine_xrg_recycleFil Send_xrg(Io_url url) {return IoEngine_xrg_recycleFil.gplx_(url);} + public void Recover(Io_url url) { + String_bldr sb = String_bldr_.new_(); + List_adp list = Regy_search(url, sb); + int listCount = list.Count(); if (listCount > 1) throw Err_.new_wo_type("found more than 1 url", "count", list.Count()); + Io_url trgUrl = (Io_url)list.Get_at(0); + IoEngine_xrg_xferFil.move_(url, trgUrl).ReadOnlyFails_(true).Overwrite_(false).Exec(); + IoEngine_xrg_saveFilStr.new_(FetchRegistryUrl(url), sb.To_str()).Exec(); + } + public void Regy_add(IoEngine_xrg_recycleFil xrg) { + Io_url url = xrg.RecycleUrl(); + Io_url regyUrl = FetchRegistryUrl(url); + String text = String_.Concat_with_obj("|", url.NameAndExt_noDirSpr(), xrg.Url().GenRelUrl_orEmpty(url.OwnerRoot()), xrg.Uuid().To_str(), xrg.AppName(), xrg.Time()); + IoEngine_xrg_saveFilStr.new_(regyUrl, text).Append_().Exec(); + } + public List_adp Regy_search(Io_url url, String_bldr sb) { + List_adp list = List_adp_.New(); + Io_url regyUrl = FetchRegistryUrl(url); + String[] lines = IoEngine_xrg_loadFilStr.new_(regyUrl).ExecAsStrAry(); + int linesLen = Array_.Len(lines); + String nameAndExt = url.NameAndExt_noDirSpr() + "|"; + for (int i = linesLen; i > 0; i--) { + String line = lines[i - 1]; + if (String_.Has_at_bgn(line, nameAndExt)) { + String[] terms = String_.Split(line, "|"); + Io_url origUrl = url.OwnerRoot().GenSubFil(terms[1]); + list.Add(origUrl); + } + else + sb.Add_str_w_crlf(line); + } + return list; + } + Io_url FetchRegistryUrl(Io_url url) { + String sourceApp = String_.GetStrBefore(url.NameAndExt_noDirSpr(), ";"); + return url.OwnerDir().GenSubFil_ary(sourceApp, ".recycle.csv"); + } + public static final IoRecycleBin Instance = new IoRecycleBin(); IoRecycleBin() {} +} diff --git a/100_core/src/gplx/core/ios/IoUrlInfo.java b/100_core/src/gplx/core/ios/IoUrlInfo.java new file mode 100644 index 000000000..f812c072d --- /dev/null +++ b/100_core/src/gplx/core/ios/IoUrlInfo.java @@ -0,0 +1,245 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.envs.*; +public interface IoUrlInfo { + String Key(); + byte DirSpr_byte(); + String DirSpr(); + boolean CaseSensitive(); + String EngineKey(); + + boolean Match(String raw); + boolean IsDir(String raw); + String Xto_api(String raw); + String OwnerDir(String raw); + String OwnerRoot(String raw); + String NameAndExt(String raw); + String NameOnly(String raw); + String Ext(String raw); + String XtoRootName(String raw, int rawLen); +} +class IoUrlInfo_nil implements IoUrlInfo { + public String Key() {return KeyConst;} public static final String KeyConst = String_.Null_mark; + public String EngineKey() {return "<>";} + public String DirSpr() {return "<>";} + public byte DirSpr_byte() {return Byte_ascii.Slash;} + public String VolSpr() {return "<>";} + public boolean CaseSensitive() {return false;} + public boolean Match(String raw) {return false;} + public boolean IsDir(String raw) {return false;} + public String Xto_api(String raw) {return "";} + public String OwnerDir(String raw) {return IoUrlInfo_base.NullString;} + public String OwnerRoot(String raw) {return IoUrlInfo_base.NullString;} + public String NameAndExt(String raw) {return "";} + public String NameOnly(String raw) {return "";} + public String Ext(String raw) {return "";} + public String XtoRootName(String raw, int rawLen) {return "";} + public static final IoUrlInfo_nil Instance = new IoUrlInfo_nil(); IoUrlInfo_nil() {} +} +abstract class IoUrlInfo_base implements IoUrlInfo { + @gplx.Internal protected static final int DirSprLen = 1; + @gplx.Internal protected static final String NullString = "", ExtSeparator = "."; + public abstract String Key(); + public abstract byte DirSpr_byte(); + public abstract String DirSpr(); + public abstract boolean CaseSensitive(); + public abstract boolean Match(String raw); + public abstract String EngineKey(); + public boolean IsDir(String raw) {return String_.Has_at_end(raw, DirSpr());} + public abstract String XtoRootName(String raw, int rawLen); + @gplx.Virtual public String Xto_api(String raw) { + return IsDir(raw) + ? String_.DelEnd(raw, IoUrlInfo_base.DirSprLen) // if Dir, remove trailing DirSpr, since most api will not expect it (ex: .Delete will malfunction) + : raw; + } + public String OwnerDir(String raw) { + int rawLen = String_.Len(raw); + int ownerDirSprPos = OwnerDirPos(raw, rawLen); + if (ownerDirSprPos <= OwnerDirPos_hasNoOwner) return IoUrlInfo_base.NullString; // no ownerDir found; return Null; only (a) NullUrls (b) RootUrls ("C:\") (c) relative ("fil.txt") + return String_.MidByLen(raw, 0, ownerDirSprPos + 1); // +1 to include backslash + } + @gplx.Virtual public String OwnerRoot(String raw) { + String temp = raw, rv = raw; + while (true) { + temp = OwnerDir(temp); + if (String_.Eq(temp, IoUrlInfo_base.NullString)) break; + rv = temp; + } + return rv; + } + public String NameAndExt(String raw) { // if Dir, will return \ as last char + int rawLen = String_.Len(raw); + int ownerDirSprPos = OwnerDirPos(raw, rawLen); + if (ownerDirSprPos == OwnerDirPos_isNull) return IoUrlInfo_base.NullString; // NullUrl and RootUrl return Null; + return ownerDirSprPos == OwnerDirPos_hasNoOwner || ownerDirSprPos == OwnerDirPos_isRoot + ? raw // no PathSeparator b/c (a) RootDir ("C:\"); (b) relative ("fil.txt") + : String_.DelBgn(raw, ownerDirSprPos + 1); // +1 to skip backslash + } + public String NameOnly(String raw) { + String nameAndExt = NameAndExt(raw); + if (IsDir(raw)) { + String rootName = XtoRootName(raw, String_.Len(raw)); // C:\ -> C; / -> root + return rootName == null + ? String_.DelEnd(nameAndExt, IoUrlInfo_base.DirSprLen) + : rootName; + } + int pos = String_.FindBwd(nameAndExt, IoUrlInfo_base.ExtSeparator); + return pos == String_.Find_none + ? nameAndExt // Ext not found; return entire NameAndExt + : String_.MidByLen(nameAndExt, 0, pos); + } + public String Ext(String raw) { // if Dir, return DirSpr; if Fil, return . as first char; ex: .txt; .png + if (IsDir(raw)) return this.DirSpr(); + String nameAndExt = NameAndExt(raw); + int pos = String_.FindBwd(nameAndExt, IoUrlInfo_base.ExtSeparator); + return pos == String_.Find_none ? "" : String_.DelBgn(nameAndExt, pos); + } + int OwnerDirPos(String raw, int rawLen) { + if (rawLen == 0) return OwnerDirPos_isNull; + else if (XtoRootName(raw, rawLen) != null) return OwnerDirPos_isRoot; + else {// NullUrls and RootUrls have no owners + int posAdj = IsDir(raw) ? IoUrlInfo_base.DirSprLen : 0; // Dir ends with DirSpr, adjust lastIndex by DirSprLen + return String_.FindBwd(raw, this.DirSpr(), rawLen - 1 - posAdj); // -1 to adjust for LastIdx + } + } + static final int + OwnerDirPos_hasNoOwner = -1 // List_adp_.Not_found + , OwnerDirPos_isNull = -2 + , OwnerDirPos_isRoot = -3; +} +class IoUrlInfo_wnt extends IoUrlInfo_base { + @Override public String Key() {return "wnt";} + @Override public String EngineKey() {return IoEngine_.SysKey;} + @Override public String DirSpr() {return Op_sys.Wnt.Fsys_dir_spr_str();} + @Override public byte DirSpr_byte() {return Byte_ascii.Backslash;} + @Override public boolean CaseSensitive() {return Op_sys.Wnt.Fsys_case_match();} + @Override public boolean Match(String raw) {return String_.Len(raw) > 1 && String_.CharAt(raw, 1) == ':';} // 2nd char is :; assumes 1 letter drives + @Override public String XtoRootName(String raw, int rawLen) { + return rawLen == 3 && String_.CharAt(raw, 1) == ':' // only allow single letter drives; ex: C:\; note, CharAt(raw, 1) to match Match + ? Char_.To_str(String_.CharAt(raw, 0)) + : null; + } + public static final IoUrlInfo_wnt Instance = new IoUrlInfo_wnt(); IoUrlInfo_wnt() {} +} +class IoUrlInfo_lnx extends IoUrlInfo_base { + @Override public String Key() {return "lnx";} + @Override public String EngineKey() {return IoEngine_.SysKey;} + @Override public String DirSpr() {return DirSprStr;} static final String DirSprStr = Op_sys.Lnx.Fsys_dir_spr_str(); + @Override public byte DirSpr_byte() {return Byte_ascii.Slash;} + @Override public boolean CaseSensitive() {return Op_sys.Lnx.Fsys_case_match();} + @Override public boolean Match(String raw) {return String_.Has_at_bgn(raw, DirSprStr);} // anything that starts with / + @Override public String XtoRootName(String raw, int rawLen) { + return rawLen == 1 && String_.Eq(raw, DirSprStr) + ? "root" + : null; + } + @Override public String OwnerRoot(String raw) {return DirSprStr;} // drive is always / + @Override public String Xto_api(String raw) { + return String_.Eq(raw, DirSprStr) // is root + ? DirSprStr + : super.Xto_api(raw); // NOTE: super.Xto_api will strip off last / + } + public static final IoUrlInfo_lnx Instance = new IoUrlInfo_lnx(); IoUrlInfo_lnx() {} +} +class IoUrlInfo_rel extends IoUrlInfo_base { + @Override public String Key() {return "rel";} + @Override public String EngineKey() {return IoEngine_.SysKey;} + @Override public String DirSpr() {return info.DirSpr();} + @Override public byte DirSpr_byte() {return info.DirSpr_byte();} + @Override public boolean CaseSensitive() {return info.CaseSensitive();} + @Override public String XtoRootName(String raw, int rawLen) {return info.XtoRootName(raw, rawLen);} + @Override public boolean Match(String raw) {return true;} // relPath is always lastResort; return true + IoUrlInfo info; + public static IoUrlInfo_rel new_(IoUrlInfo info) { + IoUrlInfo_rel rv = new IoUrlInfo_rel(); + rv.info = info; + return rv; + } IoUrlInfo_rel() {} +} +class IoUrlInfo_mem extends IoUrlInfo_base { + @Override public String Key() {return key;} private String key; + @Override public String EngineKey() {return engineKey;} private String engineKey; + @Override public String DirSpr() {return "/";} + @Override public byte DirSpr_byte() {return Byte_ascii.Slash;} + @Override public boolean CaseSensitive() {return false;} + @Override public String XtoRootName(String raw, int rawLen) { + return String_.Eq(raw, key) ? String_.DelEnd(key, 1) : null; + } + @Override public boolean Match(String raw) {return String_.Has_at_bgn(raw, key);} + public static IoUrlInfo_mem new_(String key, String engineKey) { + IoUrlInfo_mem rv = new IoUrlInfo_mem(); + rv.key = key; rv.engineKey = engineKey; + return rv; + } IoUrlInfo_mem() {} +} +class IoUrlInfo_alias extends IoUrlInfo_base { + @Override public String Key() {return srcDir;} + @Override public String EngineKey() {return engineKey;} private String engineKey; + @Override public String DirSpr() {return srcDirSpr;} + @Override public byte DirSpr_byte() {return srcDirSpr_byte;} private byte srcDirSpr_byte; + @Override public boolean CaseSensitive() {return false;} + @Override public String XtoRootName(String raw, int rawLen) { + return String_.Eq(raw, srcRootDir) ? srcRootName : null; + } + @Override public boolean Match(String raw) {return String_.Has_at_bgn(raw, srcDir);} + @Override public String Xto_api(String raw) { + String rv = String_.Replace(raw, srcDir, trgDir); // replace src with trg + if (!String_.Eq(srcDirSpr, trgDirSpr)) rv = String_.Replace(rv, srcDirSpr, trgDirSpr); // replace dirSprs + return IsDir(raw) + ? String_.DelEnd(rv, IoUrlInfo_base.DirSprLen) // remove trailingSeparator, else Directory.Delete will not work properly + : rv; + } + void SrcDir_set(String v) { + srcDir = v; + boolean lnx = DirSpr_lnx(v); + if (srcDirSpr == null) { + if (lnx) { + srcDirSpr = Op_sys.Lnx.Fsys_dir_spr_str(); + srcDirSpr_byte = Op_sys.Lnx.Fsys_dir_spr_byte(); + } + else { + srcDirSpr = Op_sys.Wnt.Fsys_dir_spr_str(); + srcDirSpr_byte = Op_sys.Wnt.Fsys_dir_spr_byte(); + } + } + if (srcRootName == null) srcRootName = lnx ? "root" : String_.Mid(srcDir, 0, String_.FindFwd(srcDir, ":")); + if (srcRootDir == null) srcRootDir = lnx ? "/" : srcDir; + } + void TrgDir_set(String v) { + trgDir = v; + boolean lnx = DirSpr_lnx(v); + if (trgDirSpr == null) trgDirSpr = lnx ? Op_sys.Lnx.Fsys_dir_spr_str() : Op_sys.Wnt.Fsys_dir_spr_str(); + } + boolean DirSpr_lnx(String s) {return String_.Has(s, Op_sys.Lnx.Fsys_dir_spr_str());} + void EngineKey_set(String v) {engineKey = v;} + String srcDir, trgDir, srcDirSpr, trgDirSpr, srcRootDir, srcRootName; + public static IoUrlInfo_alias new_(String srcDir, String trgDir, String engineKey) { + IoUrlInfo_alias rv = new IoUrlInfo_alias(); + rv.SrcDir_set(srcDir); + rv.TrgDir_set(trgDir); + rv.EngineKey_set(engineKey); + return rv; + } + public static final IoUrlInfo_alias KEYS = new IoUrlInfo_alias(); + public final String + Data_EngineKey = "engineKey" + , Data_SrcDir = "srcDir" + , Data_TrgDir = "trgDir" + ; +} diff --git a/100_core/src/gplx/core/ios/IoUrlInfoRegy.java b/100_core/src/gplx/core/ios/IoUrlInfoRegy.java new file mode 100644 index 000000000..cd2f99e4f --- /dev/null +++ b/100_core/src/gplx/core/ios/IoUrlInfoRegy.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.envs.*; +import gplx.langs.gfs.*; +public class IoUrlInfoRegy implements Gfo_invk { + public void Reg(IoUrlInfo info) {hash.Add_if_dupe_use_nth(info.Key(), info);} + public IoUrlInfo Match(String raw) { + if (String_.Len(raw) == 0) return IoUrlInfo_.Nil; + for (int i = hash.Count(); i > 0; i--) { + IoUrlInfo info = (IoUrlInfo)hash.Get_at(i - 1); + if (info.Match(raw)) return info; + } + throw Err_.new_wo_type("could not match ioPathInfo", "raw", raw, "count", hash.Count()); + } + public void Reset() { + hash.Clear(); + Reg(IoUrlInfo_rel.new_(Op_sys.Cur().Tid_is_wnt() ? (IoUrlInfo)IoUrlInfo_wnt.Instance : (IoUrlInfo)IoUrlInfo_lnx.Instance)); + Reg(IoUrlInfo_.Mem); + Reg(IoUrlInfo_lnx.Instance); + Reg(IoUrlInfo_wnt.Instance); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Add)) { + String srcDirStr = m.ReadStr("srcDir"); + String trgDirStr = m.ReadStr("trgDir"); + String engineKey = m.ReadStrOr("engineKey", IoEngine_.SysKey); + if (ctx.Deny()) return this; + IoUrlInfo_alias alias = IoUrlInfo_alias.new_(srcDirStr, trgDirStr, engineKey); + IoUrlInfoRegy.Instance.Reg(alias); + } + return this; + } public static final String Invk_Add = "Add"; + Ordered_hash hash = Ordered_hash_.New(); + public static final IoUrlInfoRegy Instance = new IoUrlInfoRegy(); + IoUrlInfoRegy() { + this.Reset(); + } +} \ No newline at end of file diff --git a/100_core/src/gplx/core/ios/IoUrlInfo_.java b/100_core/src/gplx/core/ios/IoUrlInfo_.java new file mode 100644 index 000000000..ea8f2a67b --- /dev/null +++ b/100_core/src/gplx/core/ios/IoUrlInfo_.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class IoUrlInfo_ { + public static final IoUrlInfo Nil = IoUrlInfo_nil.Instance; + public static final IoUrlInfo Wnt = IoUrlInfo_wnt.Instance; + public static final IoUrlInfo Lnx = IoUrlInfo_lnx.Instance; + public static final IoUrlInfo Mem = IoUrlInfo_mem.new_("mem", IoEngine_.MemKey); + + public static IoUrlInfo mem_(String key, String engineKey) {return IoUrlInfo_mem.new_(key, engineKey);} + public static IoUrlInfo alias_(String srcRoot, String trgRoot, String engineKey) {return IoUrlInfo_alias.new_(srcRoot, trgRoot, engineKey);} +} +/* +wnt C:\dir\fil.txt +wce \dir\fil.txt +lnx /dir/fil.txt +mem mem/dir/fil.txt +alias app:\dir\fil.txt +*/ \ No newline at end of file diff --git a/100_core/src/gplx/core/ios/IoUrlTypeRegy.java b/100_core/src/gplx/core/ios/IoUrlTypeRegy.java new file mode 100644 index 000000000..47032e615 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoUrlTypeRegy.java @@ -0,0 +1,76 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; import gplx.langs.gfs.*; +public class IoUrlTypeRegy implements Gfo_invk { + public String[] FetchAryOr(String key, String... or) { + IoUrlTypeGrp itm = (IoUrlTypeGrp)hash.Get_by(key); + return itm == null ? or : itm.AsAry(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Get)) { + String key = m.ReadStr(k); + if (ctx.Deny()) return this; + IoUrlTypeGrp itm = (IoUrlTypeGrp)hash.Get_by(key); + if (itm == null) { + itm = new IoUrlTypeGrp(key); + hash.Add(key, itm); + } + return itm; + } + else return Gfo_invk_.Rv_unhandled; +// return this; + } public static final String Invk_Get = "Get"; + Ordered_hash hash = Ordered_hash_.New(); + public static final IoUrlTypeRegy Instance = new IoUrlTypeRegy(); IoUrlTypeRegy() {} +} +class IoUrlTypeGrp implements Gfo_invk { + public String[] AsAry() { + String[] rv = new String[list.Count()]; + for (int i = 0; i < list.Count(); i++) + rv[i] = (String)list.Get_at(i); + return rv; + } + Ordered_hash list = Ordered_hash_.New(); + public IoUrlTypeGrp(String key) {this.key = key;} private String key; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_AddMany)) { + if (ctx.Deny()) return this; + for (int i = 0; i < m.Args_count(); i++) { + String s = m.ReadStr("v"); + if (list.Has(s)) { + ctx.Write_warn(UsrMsg.new_("itm already has filter").Add("key", key).Add("filter", s).To_str()); + list.Del(s); + } + list.Add(s, s); + } + } + else if (ctx.Match(k, Invk_Print)) { + if (ctx.Deny()) return this; + String_bldr sb = String_bldr_.new_(); + sb.Add(key).Add("{"); + for (int i = 0; i < list.Count(); i++) + sb.Add_spr_unless_first((String)list.Get_at(i), " ", i); + sb.Add("}"); + return sb.To_str(); + } + else if (ctx.Match(k, Invk_Clear)) {if (ctx.Deny()) return this; list.Clear();} + else return Gfo_invk_.Rv_unhandled; + return this; + } public static final String Invk_AddMany = "Add_many", Invk_Clear = "Clear", Invk_Print = "Print"; +} diff --git a/100_core/src/gplx/core/ios/IoZipWkr.java b/100_core/src/gplx/core/ios/IoZipWkr.java new file mode 100644 index 000000000..a51c05e98 --- /dev/null +++ b/100_core/src/gplx/core/ios/IoZipWkr.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.envs.*; import gplx.core.stores.*; /*GfoNdeRdr_*/ +import gplx.core.gfo_regys.*; +public class IoZipWkr { + public Io_url ExeUrl() {return (Io_url)GfoRegy.Instance.FetchValOrFail(Regy_ExeUrl);} + public String ExeArgFmt() {return (String)GfoRegy.Instance.FetchValOrFail(Regy_ExeArgFmt);} + public void Expand(Io_url srcUrl, Io_url trgUrl) { + String exeArgs = Expand_genCmdString(srcUrl, trgUrl); + process.Exe_url_(this.ExeUrl()).Args_str_(exeArgs); + process.Run_wait(); + } + @gplx.Internal protected String Expand_genCmdString(Io_url srcUrl, Io_url trgUrl) { + return String_.Format(this.ExeArgFmt(), srcUrl.Xto_api(), trgUrl.Xto_api()); + } + Process_adp process = new Process_adp(); + public static IoZipWkr regy_() {return new IoZipWkr();} + static final String Regy_ExeUrl = "gplx.core.ios.IoZipWkr.ExeUrl", Regy_ExeArgFmt = "gplx.core.ios.IoZipWkr.ExeArgFmt"; + public static IoZipWkr new_(Io_url exeUrl, String expandArgs) { + GfoRegy.Instance.RegObj(Regy_ExeUrl, exeUrl); + GfoRegy.Instance.RegObj(Regy_ExeArgFmt, expandArgs); + IoZipWkr rv = new IoZipWkr(); + return rv; + } +} diff --git a/100_core/src/gplx/core/ios/IoZipWkr_tst.java b/100_core/src/gplx/core/ios/IoZipWkr_tst.java new file mode 100644 index 000000000..1c56abcdf --- /dev/null +++ b/100_core/src/gplx/core/ios/IoZipWkr_tst.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoZipWkr_tst { + @Test public void Basic() { + wkr = IoZipWkr.new_(Io_url_.Empty, "e \"{0}\" -o\"{1}\" -y"); + tst_Expand_genCmdString(Io_url_.wnt_fil_("C:\\fil1.zip"), Io_url_.wnt_dir_("D:\\out\\"), "e \"C:\\fil1.zip\" -o\"D:\\out\" -y"); // NOTE: not "D:\out\" because .Xto_api + } IoZipWkr wkr; + void tst_Expand_genCmdString(Io_url srcUrl, Io_url trgUrl, String expd) { + Tfds.Eq(expd, wkr.Expand_genCmdString(srcUrl, trgUrl)); + } +} diff --git a/100_core/src/gplx/core/ios/Io_download_fmt.java b/100_core/src/gplx/core/ios/Io_download_fmt.java new file mode 100644 index 000000000..9a3e879d2 --- /dev/null +++ b/100_core/src/gplx/core/ios/Io_download_fmt.java @@ -0,0 +1,94 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.brys.args.*; import gplx.core.brys.fmtrs.*; import gplx.core.envs.*; +public class Io_download_fmt { + private final Io_size_fmtr_arg size_fmtr_arg = new Io_size_fmtr_arg(), rate_fmtr_arg = new Io_size_fmtr_arg().Suffix_(Bry_.new_a7("ps")); + private final Bfr_arg__time prog_left_fmtr_arg = new Bfr_arg__time(); private final Bfr_arg__decimal_int prog_pct_fmtr_arg = new Bfr_arg__decimal_int().Places_(2); + private long time_checkpoint_interval = 250; + private long time_checkpoint = 0; + private long time_prv = 0; + public Io_download_fmt() { + this.src_name = prog_msg_hdr = ""; // NOTE: set to "" else prog_mgr will fail with null ref + } + private final Bry_bfr prog_bfr = Bry_bfr_.New(); Bry_fmtr prog_fmtr = Bry_fmtr.new_().Fail_when_invalid_escapes_(false); // NOTE: prog_fmtr can be passed file_names with ~ which are not easy to escape; DATE:2013-02-19 + public long Time_bgn() {return time_bgn;} private long time_bgn; + public long Time_now() {return time_now;} private long time_now; + public long Time_dif() {return time_dif;} private long time_dif; + public long Time_end() {return time_end;} private long time_end; + public String Src_url() {return src_url;} private String src_url; + public String Src_name() {return src_name;} private String src_name; + public long Src_len() {return src_len;} private long src_len; + public long Prog_done() {return prog_done;} private long prog_done; + public long Prog_rate() {return prog_rate;} private long prog_rate; + public long Prog_left() {return prog_left;} private long prog_left; + public long Prog_pct() {return prog_pct;} private long prog_pct; + public String Prog_msg_hdr() {return prog_msg_hdr;} private String prog_msg_hdr; + public int Prog_num_units() {return prog_num_units;} private int prog_num_units = Io_mgr.Len_kb; + public String Prog_num_fmt() {return prog_num_fmt;} private String prog_num_fmt = "#,##0"; + public String Prog_msg() {return prog_msg;} private String prog_msg; + public Gfo_usr_dlg Usr_dlg() {return usr_dlg;} private Gfo_usr_dlg usr_dlg; + public void Ctor(Gfo_usr_dlg usr_dlg) { + this.usr_dlg = usr_dlg; + } + public void Download_init(String src_url, String prog_msg_hdr) { + this.src_url = src_url; + this.src_name = String_.Extract_after_bwd(src_url, "/"); + this.prog_msg_hdr = prog_msg_hdr; + } + public void Bgn(long src_len) { + this.src_len = src_len; + prog_fmtr.Fmt_(prog_msg_hdr).Keys_("src_name", "src_len").Bld_bfr_many_and_set_fmt(src_name, size_fmtr_arg.Val_(src_len)); + prog_fmtr.Keys_("prog_done", "prog_pct", "prog_rate", "prog_left"); + prog_done = 0; + prog_pct = 0; + prog_rate = 0; + prog_left = 0; + time_bgn = time_prv = System_.Ticks(); + time_checkpoint = 0; + } + public void Prog(int prog_read) { + time_now = System_.Ticks(); + time_dif = time_now - time_bgn; if (time_dif == 0) time_dif = 1; // avoid div by zero error below + prog_done += prog_read; + time_checkpoint += time_now - time_prv; + time_prv = time_now; + if ((time_checkpoint < time_checkpoint_interval)) return; // NOTE: using time_checkpoint instead of size_checkpoint b/c WMF dump servers transfer in spurts (sends 5 packets, and then waits); + time_checkpoint = 0; + prog_rate = (prog_done * 1000) / (time_dif); + prog_pct = (prog_done * 10000) / src_len; // 100 00 to get 2 decimal places; EX: .1234 -> 1234 -> 12.34% + prog_left = (1000 * (src_len - prog_done)) / prog_rate; + prog_fmtr.Bld_bfr_many(prog_bfr + , size_fmtr_arg.Val_(prog_done) + , prog_pct_fmtr_arg.Val_((int)prog_pct) + , rate_fmtr_arg.Val_(prog_rate) + , prog_left_fmtr_arg.Seconds_(prog_left / 1000) + ); + prog_msg = prog_bfr.To_str_and_clear(); + if (usr_dlg != null) + usr_dlg.Prog_none("", "prog", prog_msg); + } + public void Term() { + time_end = System_.Ticks(); +// prog_rate = (prog_done * 1000) / (time_dif); +// prog_pct = (prog_done * 10000) / src_len; // 100 00 to get 2 decimal places; EX: .1234 -> 1234 -> 12.34% +// prog_left = (1000 * (src_len - prog_done)) / prog_rate; +// if (usr_dlg != null) usr_dlg.Prog_none(GRP_KEY, "clear", ""); + } + public static final Io_download_fmt Null = null; +} diff --git a/100_core/src/gplx/core/ios/Io_download_fmt_tst.java b/100_core/src/gplx/core/ios/Io_download_fmt_tst.java new file mode 100644 index 000000000..604d70df7 --- /dev/null +++ b/100_core/src/gplx/core/ios/Io_download_fmt_tst.java @@ -0,0 +1,73 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.core.envs.*; +public class Io_download_fmt_tst { + Io_download_fmt_fxt fxt = new Io_download_fmt_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Fmt() { + fxt.Clear().Ini("downloading ~{src_name}: ~{prog_left} left (@ ~{prog_rate}); ~{prog_done} of ~{src_len} (~{prog_pct}%)", "http://a.org/b.png", Io_mgr.Len_kb * 10); + fxt.Now_add_f(1000).Prog_done_(1 * Io_mgr.Len_kb).Prog_pct_(1 * 1000).Prog_rate_(Io_mgr.Len_kb).Prog_left_(9 * 1000) + .Prog_msg_("downloading b.png: 09s left (@ 1.000 KBps); 1.000 KB of 10.000 KB (10.00%)") + .Download_(Io_mgr.Len_kb); + fxt.Now_add_f(1000).Prog_done_(2 * Io_mgr.Len_kb).Prog_pct_(2 * 1000).Prog_rate_(Io_mgr.Len_kb).Prog_left_(8 * 1000) + .Prog_msg_("downloading b.png: 08s left (@ 1.000 KBps); 2.000 KB of 10.000 KB (20.00%)") + .Download_(Io_mgr.Len_kb) + ; + fxt.Now_add_f(2000).Prog_done_(3 * Io_mgr.Len_kb).Prog_pct_(3 * 1000).Prog_rate_(768).Prog_left_(9333) + .Prog_msg_("downloading b.png: 09s left (@ 768.000 Bps); 3.000 KB of 10.000 KB (30.00%)") + .Download_(Io_mgr.Len_kb) + ; + } + @Test public void Tilde() { + fxt.Clear().Ini("a~b", "http://a.org/b.png", Io_mgr.Len_kb * 10); + } +} +class Io_download_fmt_fxt { + public Io_download_fmt_fxt Clear() { + if (fmt == null) { + fmt = new Io_download_fmt(); + } + System_.Ticks__test_set(0); + prog_done = prog_rate = prog_pct = prog_left = -1; + prog_msg = null; + return this; + } Io_download_fmt fmt; + public Io_download_fmt_fxt Now_add_f(int v) {System_.Ticks__test_add(v); return this;} + public Io_download_fmt_fxt Prog_done_(int v) {prog_done = v; return this;} long prog_done = -1; + public Io_download_fmt_fxt Prog_pct_ (int v) {prog_pct = v; return this;} long prog_pct = -1; + public Io_download_fmt_fxt Prog_rate_(int v) {prog_rate = v; return this;} long prog_rate = -1; + public Io_download_fmt_fxt Prog_left_(int v) {prog_left = v; return this;} long prog_left = -1; + public Io_download_fmt_fxt Prog_msg_(String v) { + prog_msg = v; return this; + } String prog_msg; + public Io_download_fmt_fxt Download_(int v) { + fmt.Prog(v); + if (prog_done != -1) Tfds.Eq(prog_done, fmt.Prog_done(), "prog_done"); + if (prog_pct != -1) Tfds.Eq(prog_pct , fmt.Prog_pct(), "prog_pct"); + if (prog_rate != -1) Tfds.Eq(prog_rate, fmt.Prog_rate(), "prog_rate"); + if (prog_left != -1) Tfds.Eq(prog_left, fmt.Prog_left(), "prog_left"); + if (prog_msg != null) Tfds.Eq(prog_msg, fmt.Prog_msg(), "prog_msg"); + return this; + } + public Io_download_fmt_fxt Ini(String prog_msg_hdr, String src_url, int src_len) { + fmt.Download_init(src_url, prog_msg_hdr); + fmt.Bgn(src_len); + return this; + } +} diff --git a/100_core/src/gplx/core/ios/Io_fil.java b/100_core/src/gplx/core/ios/Io_fil.java new file mode 100644 index 000000000..36348805a --- /dev/null +++ b/100_core/src/gplx/core/ios/Io_fil.java @@ -0,0 +1,38 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class Io_fil implements gplx.CompareAble { + public Io_fil(Io_url url, String data) {this.url = url; this.data = data;} + public Io_url Url() {return url;} public Io_fil Url_(Io_url v) {url = v; return this;} Io_url url; + public String Data() {return data;} public Io_fil Data_(String v) {data = v; return this;} private String data; + public int compareTo(Object obj) { + return gplx.CompareAble_.Compare(url.Raw(), ((Io_fil)obj).Url().Raw()); + } + public static Io_fil[] new_ary_(Io_url[] url_ary) { + int url_ary_len = url_ary.length; + Io_fil[] rv = new Io_fil[url_ary_len]; + for (int i = 0; i < url_ary_len; i++) { + Io_url url = url_ary[i]; + String data = Io_mgr.Instance.LoadFilStr(url); + Io_fil fil = new Io_fil(url, data); + rv[i] = fil; + } + return rv; + } + public static final Io_fil[] Ary_empty = new Io_fil[0]; +} diff --git a/100_core/src/gplx/core/ios/Io_fil_mkr.java b/100_core/src/gplx/core/ios/Io_fil_mkr.java new file mode 100644 index 000000000..3e1eb11bf --- /dev/null +++ b/100_core/src/gplx/core/ios/Io_fil_mkr.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class Io_fil_mkr { + private final List_adp list = List_adp_.New(); + public Io_fil_mkr Add(String url, String data) {return Add(Io_url_.mem_fil_(url), data);} + public Io_fil_mkr Add(Io_url url, String data) {list.Add(new Io_fil(url, data)); return this;} + public Io_fil[] To_ary() {return (Io_fil[])list.To_ary(Io_fil.class);} +} diff --git a/100_core/src/gplx/core/ios/Io_size_.java b/100_core/src/gplx/core/ios/Io_size_.java new file mode 100644 index 000000000..180684beb --- /dev/null +++ b/100_core/src/gplx/core/ios/Io_size_.java @@ -0,0 +1,172 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import gplx.core.brys.*; +public class Io_size_ { + public static String To_str(long val) {return To_str(val, "#,##0.000");} + public static String To_str(long val, String val_fmt) { + long cur = val; int pow = 0; + while (cur >= 1024) { + cur /= 1024; + pow++; + } + long div = (long)Math_.Pow((long)1024, (long)pow); + Decimal_adp valDecimal = Decimal_adp_.divide_(val, div); + String[] unit = Io_size_.Units[pow]; + return valDecimal.To_str(val_fmt) + " " + String_.PadBgn(unit[0], 2, " "); + } + public static String To_str(long val, int exp_1024, String val_fmt, String unit_pad, boolean round_0_to_1) { + long exp_val = (long)Math_.Pow(1024, exp_1024); + Decimal_adp val_as_decimal = Decimal_adp_.divide_(val, exp_val); + if (round_0_to_1 && val_as_decimal.Comp_lt(1)) val_as_decimal = Decimal_adp_.One; + String[] unit = Io_size_.Units[exp_1024]; + return val_as_decimal.To_str(val_fmt) + " " + String_.PadBgn(unit[0], 2, unit_pad); + } + public static String To_str_new(Bry_bfr bfr, long val, int decimal_places) {To_bfr_new(bfr, val, decimal_places); return bfr.To_str_and_clear();} + public static void To_bfr_new(Bry_bfr bfr, long val, int decimal_places) { + // init + int unit_idx = 0; + int mult = 1024; + long cur_val = val; + long cur_exp = 1; + long nxt_exp = mult; + + // get 1024 mult; EX: 1549 -> 1024 + for (unit_idx = 0; unit_idx < 6; ++unit_idx) { + if (cur_val < nxt_exp) break; + cur_exp = nxt_exp; + nxt_exp *= mult; + } + + // calc integer / decimal values + int int_val = (int)(val / cur_exp); + int dec_val = (int)(val % cur_exp); + if (decimal_places == 0) { // if 0 decimal places, round up + if (dec_val >= .5) ++int_val; + dec_val = 0; + } + else {// else, calculate decimal value as integer; EX: 549 -> .512 -> 512 + long dec_factor = 0; + switch (decimal_places) { + case 1: dec_factor = 10; break; + case 2: dec_factor = 100; break; + default: + case 3: dec_factor = 1000; break; + } + dec_val = (int)((dec_val * dec_factor) / cur_exp); + } + + // calc unit_str + String unit_str = ""; + switch (unit_idx) { + case 0: unit_str = " B"; break; + case 1: unit_str = " KB"; break; + case 2: unit_str = " MB"; break; + case 3: unit_str = " GB"; break; + case 4: unit_str = " PB"; break; + case 5: + default: unit_str = " EB"; break; + } + + // build String + bfr.Add_long_variable(int_val); + if (decimal_places > 0 && unit_idx != 0) { + bfr.Add_byte_dot(); + bfr.Add_long_variable(dec_val); + } + bfr.Add_str_a7(unit_str); + } + public static long parse_or(String raw, long or) { + if (raw == null || raw == String_.Empty) return or; + String[] terms = String_.Split(raw, " "); + int termsLen = Array_.Len(terms); if (termsLen > 2) return or; + + Decimal_adp val = null; + try {val = Decimal_adp_.parse(terms[0]);} catch (Exception exc) {Err_.Noop(exc); return or;} + + int unitPow = 0; + if (termsLen > 1) { + String unitStr = String_.Upper(terms[1]); + unitPow = parse_unitPow_(unitStr); if (unitPow == -1) return or; + } + int curPow = unitPow; + while (curPow > 0) { + val = val.Multiply(1024); + curPow--; + } + // DELETED:do not check for fractional bytes; EX: 10.7 GB DATE:2015-01-06 + // Decimal_adp comp = val.Op_truncate_decimal(); + // if (!val.Eq(comp)) return or; + return val.To_long(); + } + private static int parse_unitPow_(String unitStr) { + int unitLen = Array_.Len(Units); + int unitPow = -1; + for (int i = 0; i < unitLen; i++) { + if (String_.Eq(unitStr, String_.Upper(Units[i][0]))) return i; + if (String_.Eq(unitStr, String_.Upper(Units[i][1]))) return i; + } + return unitPow; + } + private static final String[][] Units = new String[][] + { String_.Ary("B", "BYTE") + , String_.Ary("KB", "KILOBYTE") + , String_.Ary("MB", "MEGABYTE") + , String_.Ary("GB", "GIGABYTE") + , String_.Ary("TB", "TERABYTE") + , String_.Ary("PB", "PETABYTE") + , String_.Ary("EB", "EXABYTE") + }; + public static final byte[][] Units_bry = new byte[][] + { Bry_.new_a7("B") + , Bry_.new_a7("KB") + , Bry_.new_a7("MB") + , Bry_.new_a7("GB") + , Bry_.new_a7("TB") + , Bry_.new_a7("PB") + , Bry_.new_a7("EB") + }; + public static int Load_int_(GfoMsg m) {return (int)Load_long_(m);} + public static long Load_long_(GfoMsg m) { + String v = m.ReadStr("v"); + long rv = parse_or(v, Long_.Min_value); if (rv == Long_.Min_value) throw Err_.new_wo_type("invalid val", "val", v); + return rv; + } + public static String To_str_mb(long v) {return Long_.To_str(v / Io_mgr.Len_mb_long);} + public static long To_long_by_int_mb(int v) {return (long)v * Io_mgr.Len_mb_long;} + public static long To_long_by_msg_mb(GfoMsg m, long cur) { + long val = m.ReadLongOr("v", Int_.Min_value); + return val == Int_.Min_value ? cur : (val * Io_mgr.Len_mb_long); + } +} +class Io_size_fmtr_arg implements Bfr_arg { + public long Val() {return val;} public Io_size_fmtr_arg Val_(long v) {val = v; return this;} long val; + public byte[] Suffix() {return suffix;} public Io_size_fmtr_arg Suffix_(byte[] v) {suffix = v; return this;} private byte[] suffix; + public void Bfr_arg__add(Bry_bfr bfr) { + long cur = val; int pow = 0; + while (cur >= 1024) { + cur /= 1024; + pow++; + } + long div = (long)Math_.Pow((long)1024, (long)pow); + Decimal_adp val_decimal = Decimal_adp_.divide_(val, div); + bfr.Add_str_a7(val_decimal.To_str("#,###.000")).Add_byte(Byte_ascii.Space).Add(gplx.core.ios.Io_size_.Units_bry[pow]); + if (suffix != null) + bfr.Add(suffix); + } +} diff --git a/100_core/src/gplx/core/ios/Io_size__tst.java b/100_core/src/gplx/core/ios/Io_size__tst.java new file mode 100644 index 000000000..3ae883e1a --- /dev/null +++ b/100_core/src/gplx/core/ios/Io_size__tst.java @@ -0,0 +1,85 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Io_size__tst { + private Io_size__fxt fxt = new Io_size__fxt(); + @Test public void XtoLong() { + fxt.Test_XtoLong("1", 1); + fxt.Test_XtoLong("1 KB", 1024); + fxt.Test_XtoLong("1 MB", 1024 * 1024); + fxt.Test_XtoLong("1 GB", 1024 * 1024 * 1024); + fxt.Test_XtoLong("12 kb", 12 * 1024); + fxt.Test_XtoLong("1.5 kb", 1024 + 512); // 1536 + fxt.Test_XtoLong("1.5 mb", (long)(1024 * 1024 * 1.5)); + fxt.Test_XtoLong("-1", -1); // NOTE: negative bytes allowed + + fxt.Test_XtoLongFail("1 kilobite"); + fxt.Test_XtoLongFail("1 BB"); + // fxt.Test_XtoLongFail("1.1"); // DELETED:do not check for fractional bytes; EX: 10.7 GB DATE:2015-01-06 + // fxt.Test_XtoLongFail("1.51 kb"); + } + @Test public void To_str() { + fxt.Test_XtoStr(1, "1.000 B"); + fxt.Test_XtoStr(1024, "1.000 KB"); + fxt.Test_XtoStr(1536, "1.500 KB"); + fxt.Test_XtoStr(1024 * 1024, "1.000 MB"); + fxt.Test_XtoStr(1016, "1,016.000 B"); // NOTE: 1016 is not 1.016 KB + } + @Test public void Xto_str_full() { + fxt.Test_Xto_str( 500, 1, "#,###", " ", Bool_.Y, "1 KB"); + fxt.Test_Xto_str( 1000, 1, "#,###", " ", Bool_.Y, "1 KB"); + fxt.Test_Xto_str( 2000, 1, "#,###", " ", Bool_.Y, "2 KB"); + fxt.Test_Xto_str( 1234567, 1, "#,###", " ", Bool_.Y, "1,206 KB"); + fxt.Test_Xto_str(1234567890, 1, "#,###", " ", Bool_.Y, "1,205,633 KB"); + } + @Test public void EqualsTest() { + fxt.Test_Equals("1", "1"); + fxt.Test_Equals("1 kb", "1 kb"); + fxt.Test_Equals("1024", "1 kb"); + fxt.Test_Equals("1048576", "1 mb"); + fxt.Test_Equals("1024 kb", "1 mb"); + fxt.Test_Equals("1.5 kb", "1536 b"); + } + @Test public void To_str_new___b___0() {fxt.Test__to_str_new( 0, 2, "0 B");} + @Test public void To_str_new___b___1() {fxt.Test__to_str_new( 1, 2, "1 B");} + @Test public void To_str_new__kb___1_501__1() {fxt.Test__to_str_new( 1538, 1, "1.5 KB");} + @Test public void To_str_new__kb___1_501__2() {fxt.Test__to_str_new( 1538, 2, "1.50 KB");} + @Test public void To_str_new__kb___1_501__3() {fxt.Test__to_str_new( 1538, 3, "1.501 KB");} + @Test public void To_str_new__kb___1_512__1() {fxt.Test__to_str_new( 1549, 1, "1.5 KB");} + @Test public void To_str_new__kb___1_512__2() {fxt.Test__to_str_new( 1549, 2, "1.51 KB");} + @Test public void To_str_new__kb___1_512__3() {fxt.Test__to_str_new( 1549, 3, "1.512 KB");} + @Test public void To_str_new__mb___1_512__1() {fxt.Test__to_str_new((1024 * 1024) + 536871, 1, "1.5 MB");} + @Test public void To_str_new__mb___1_512__2() {fxt.Test__to_str_new((1024 * 1024) + 536871, 2, "1.51 MB");} + @Test public void To_str_new__mb___1_512__3() {fxt.Test__to_str_new((1024 * 1024) + 536871, 3, "1.512 MB");} + @Test public void To_str_new__gb___1() {fxt.Test__to_str_new(1819264175, 2, "1.69 GB");} +} +class Io_size__fxt { + public void Test_XtoLong(String raw, long expd) {Tfds.Eq(expd, Io_size_.parse_or(raw, Long_.Min_value));} + public void Test_XtoLongFail(String raw) { + long val = Io_size_.parse_or(raw, Long_.Min_value); + if (val != Long_.Min_value) Tfds.Fail("expd parse failure; raw=" + raw); + } + public void Test_Equals(String lhs, String rhs) {Tfds.Eq(Io_size_.parse_or(lhs, Long_.Min_value), Io_size_.parse_or(rhs, Long_.Min_value));} + public void Test_XtoStr(long val, String expd) {Tfds.Eq(expd, Io_size_.To_str(val));} + public void Test_Xto_str(long val, int exp_1024, String val_fmt, String unit_pad, boolean round_0_to_1, String expd) {Tfds.Eq(expd, Io_size_.To_str(val, exp_1024, val_fmt, unit_pad, round_0_to_1));} + public void Test__to_str_new(long val, int decimal_places, String expd) { + Bry_bfr bfr = Bry_bfr_.New(); + Tfds.Eq_str(expd, Io_size_.To_str_new(bfr, val, decimal_places)); + } +} diff --git a/100_core/src/gplx/core/ios/Io_url_obj_ref.java b/100_core/src/gplx/core/ios/Io_url_obj_ref.java new file mode 100644 index 000000000..c67719c22 --- /dev/null +++ b/100_core/src/gplx/core/ios/Io_url_obj_ref.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +public class Io_url_obj_ref { + public Io_url Val() {return val;} public Io_url_obj_ref Val_(Io_url v) {val = v; return this;} private Io_url val; + public String Val_as_str() {return val.Raw();} + @Override public String toString() {return val.Raw();} + @Override public int hashCode() {return val.hashCode();} + @Override public boolean equals(Object obj) {return String_.Eq(val.Raw(), ((Io_url_obj_ref)obj).val.Raw());} + public static Io_url_obj_ref new_(Io_url val) { + Io_url_obj_ref rv = new Io_url_obj_ref(); + rv.val = val; + return rv; + } Io_url_obj_ref() {} +} diff --git a/100_core/src/gplx/core/ios/drives/Io_drive.java b/100_core/src/gplx/core/ios/drives/Io_drive.java new file mode 100644 index 000000000..c475d3382 --- /dev/null +++ b/100_core/src/gplx/core/ios/drives/Io_drive.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.drives; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public interface Io_drive { + long Get_space_total(Io_url drive); + long Get_space_free (Io_url drive); +} diff --git a/100_core/src/gplx/core/ios/drives/Io_drive_.java b/100_core/src/gplx/core/ios/drives/Io_drive_.java new file mode 100644 index 000000000..ec5be4ae5 --- /dev/null +++ b/100_core/src/gplx/core/ios/drives/Io_drive_.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.drives; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public class Io_drive_ { + public static Io_drive Instance = new Io_drive__jre(); +} diff --git a/100_core/src/gplx/core/ios/drives/Io_drive__jre.java b/100_core/src/gplx/core/ios/drives/Io_drive__jre.java new file mode 100644 index 000000000..d2483d7e4 --- /dev/null +++ b/100_core/src/gplx/core/ios/drives/Io_drive__jre.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.drives; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public class Io_drive__jre implements Io_drive { + public long Get_space_total(Io_url drive) {return new java.io.File(drive.Xto_api()).getTotalSpace();} + public long Get_space_free (Io_url drive) {return new java.io.File(drive.Xto_api()).getFreeSpace();} +} diff --git a/100_core/src/gplx/core/ios/loaders/Io_loader.java b/100_core/src/gplx/core/ios/loaders/Io_loader.java new file mode 100644 index 000000000..eec265ed1 --- /dev/null +++ b/100_core/src/gplx/core/ios/loaders/Io_loader.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.loaders; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public interface Io_loader { + byte[] Load_fil_as_bry(Io_url url); +} diff --git a/100_core/src/gplx/core/ios/streams/IoStream.java b/100_core/src/gplx/core/ios/streams/IoStream.java new file mode 100644 index 000000000..629d8cb39 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public interface IoStream extends Rls_able { + Object UnderRdr(); + Io_url Url(); + long Pos(); + long Len(); + + int ReadAry(byte[] array); + int Read(byte[] array, int offset, int count); + long Seek(long pos); + void WriteAry(byte[] ary); + void Write(byte[] array, int offset, int count); + void Transfer(IoStream trg, int bufferLength); + void Flush(); + void Write_and_flush(byte[] bry, int bgn, int end); +} \ No newline at end of file diff --git a/100_core/src/gplx/core/ios/streams/IoStream_.java b/100_core/src/gplx/core/ios/streams/IoStream_.java new file mode 100644 index 000000000..50ff41524 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream_.java @@ -0,0 +1,64 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +public class IoStream_ { + public static final IoStream Null = new IoStream_null(); + public static IoStream mem_txt_(Io_url url, String v) {return IoStream_mem.rdr_txt_(url, v);} + public static IoStream ary_(byte[] v) {return IoStream_mem.rdr_ary_(Io_url_.Empty, v);} + public static final byte Mode_rdr = 0, Mode_wtr_create = 1, Mode_wtr_append = 2, Mode_wtr_update = 3; + public static IoStream stream_rdr_() {return new IoStream_stream_rdr();} + public static IoStream stream_input_(Io_url url) {return new IoStream_stream_rdr().UnderRdr_(input_stream_(url));} + public static Object input_stream_(Io_url url) { + try { + return new java.io.FileInputStream(url.Raw()); + } catch (Exception e) {throw Err_.new_wo_type("file not found", "url", url.Raw());} + } +} +class IoStream_null implements IoStream { + public Object UnderRdr() {return null;} + public Io_url Url() {return Io_url_.Empty;} + public long Pos() {return -1;} + public long Len() {return -1;} + public int ReadAry(byte[] array) {return -1;} + public int Read(byte[] array, int offset, int count) {return -1;} + public long Seek(long pos) {return -1;} + public void WriteAry(byte[] ary) {} + public void Write(byte[] array, int offset, int count) {} + public void Transfer(IoStream trg, int bufferLength) {} + public void Flush() {} + public void Write_and_flush(byte[] bry, int bgn, int end) {} + public void Rls() {} +} +/* +NOTE_1:stream mode +my understanding of mode +rw: read/write async? +rws: read/write sync; write content + metadata changes +rwd: read/write sync; write content +*/ +//#} \ No newline at end of file diff --git a/100_core/src/gplx/core/ios/streams/IoStream_base.java b/100_core/src/gplx/core/ios/streams/IoStream_base.java new file mode 100644 index 000000000..e69eeea59 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream_base.java @@ -0,0 +1,138 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +public class IoStream_base implements IoStream { + @gplx.Virtual public Io_url Url() {return url;} Io_url url = Io_url_.Empty; + public void Transfer(IoStream trg, int bufferLength) { + byte[] buffer = new byte[bufferLength]; + int read = -1; + while (read != 0) { + read = this.Read(buffer, 0, bufferLength); + trg.Write(buffer, 0, read); + } + trg.Flush(); + } + public int ReadAry(byte[] ary) {return this.Read(ary, 0, ary.length);} + public void WriteAry(byte[] ary) {this.Write(ary, 0, ary.length);} + @gplx.Virtual public Object UnderRdr() {return under;} + @gplx.Virtual public void UnderRdr_(Object v) {this.under = (RandomAccessFile)v;} + @gplx.Virtual public long Pos() {return pos;} long pos; + @gplx.Virtual public long Len() {return length;} long length; + @gplx.Virtual public int Read(byte[] array, int offset, int count) { + try { + int rv = under.read(array, offset, count); + return rv == -1 ? 0 : rv; // NOTE: fis returns -1 if nothing read; .NET returned 0; Hash will fail if -1 returned (will try to create array of 0 length) + } // NOTE: fis keeps track of offset, only need to pass in array (20110606: this NOTE no longer seems to make sense; deprecate) + catch (IOException e) {throw Err_.new_exc(e, "io", "file read failed", "url", url);} + } + public long Seek(long seek_pos) { + try { + under.seek(seek_pos); + pos = under.getFilePointer(); + return pos; + } + catch (IOException e) {throw Err_.new_exc(e, "io", "seek failed", "url", url);} + } + @gplx.Virtual public void Write(byte[] array, int offset, int count) {bfr.Add_mid(array, offset, offset + count); this.Flush();} Bry_bfr bfr = Bry_bfr_.Reset(16); + public void Write_and_flush(byte[] bry, int bgn, int end) { +// ConsoleAdp._.WriteLine(bry.length +" " + bgn + " " + end); + Flush();// flush anything already in buffer + int buffer_len = Io_mgr.Len_kb * 16; + byte[] buffer = new byte[buffer_len]; + int buffer_bgn = bgn; boolean loop = true; + while (loop) { + int buffer_end = buffer_bgn + buffer_len; + if (buffer_end > end) { + buffer_end = end; + buffer_len = end - buffer_bgn; + loop = false; + } + for (int i = 0; i < buffer_len; i++) + buffer[i] = bry[i + buffer_bgn]; + try {under.write(buffer, 0, buffer_len);} + catch (IOException e) {throw Err_.new_exc(e, "io", "write failed", "url", url);} + buffer_bgn = buffer_end; + } +// this.Rls(); +// OutputStream output_stream = null; +// try { +// output_stream = new FileOutputStream(url.Xto_api()); +// bry = ByteAry_.Mid(bry, bgn, end); +// output_stream.write(bry, 0, bry.length); +// } +// catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "write failed").Add("url", url);} +// finally { +// if (output_stream != null) { +// try {output_stream.close();} +// catch (IOException ignore) {} +// } +// } + } + @gplx.Virtual public void Flush() { + try { + if (mode_is_append) under.seek(under.length()); +// else under.seek(0); + } + catch (IOException e) {throw Err_.new_exc(e, "io", "seek failed", "url", url);} + try {under.write(bfr.Bfr(), 0, bfr.Len());} + catch (IOException e) {throw Err_.new_exc(e, "io", "write failed", "url", url);} + bfr.Clear(); + } + @gplx.Virtual public void Rls() { + IoEngine_system.Closeable_close(under, url, true); + } + RandomAccessFile under; boolean mode_is_append; byte mode; + public static IoStream_base rdr_wrapper_() {return new IoStream_base();} + public static IoStream_base new_(Io_url url, int mode) { + IoStream_base rv = new IoStream_base(); + rv.url = url; + rv.mode = (byte)mode; + File file = new File(url.Xto_api()); + String ctor_mode = ""; + switch (mode) { // mode; SEE:NOTE_1 + case IoStream_.Mode_wtr_append: + rv.mode_is_append = mode == IoStream_.Mode_wtr_append; + ctor_mode = "rws"; + break; + case IoStream_.Mode_wtr_create: + ctor_mode = "rws"; + break; + case IoStream_.Mode_rdr: + ctor_mode = "r"; + break; + } + try {rv.under = new RandomAccessFile(file, ctor_mode);} + catch (FileNotFoundException e) {throw Err_.new_exc(e, "io", "file open failed", "url", url);} + if (mode == IoStream_.Mode_wtr_create) { + try {rv.under.setLength(0);} + catch (IOException e) {throw Err_.new_exc(e, "io", "file truncate failed", "url", url);} + } + rv.length = file.length(); + return rv; + } + public static IoStream_base new_(Object stream) { + IoStream_base rv = new IoStream_base(); +// rv.stream = (System.IO.Stream)stream; + rv.url = Io_url_.Empty; + return rv; + } + } \ No newline at end of file diff --git a/100_core/src/gplx/core/ios/streams/IoStream_mem.java b/100_core/src/gplx/core/ios/streams/IoStream_mem.java new file mode 100644 index 000000000..f8af91594 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream_mem.java @@ -0,0 +1,70 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import gplx.core.texts.*; /*Encoding_*/ +public class IoStream_mem extends IoStream_base { + @Override public Io_url Url() {return url;} Io_url url; + @Override public Object UnderRdr() {throw Err_.new_unimplemented();} // NOTE: should not use System.IO.MemoryStream, b/c resized data will not be captured in this instance's buffer + @Override public long Len() {return Array_.Len(buffer);} + public int Position() {return position;} public void Position_set(int v) {position = v;} int position; + public byte[] Buffer() {return buffer;} private byte[] buffer = new byte[0]; + + @Override public int Read(byte[] array, int offset, int count) { + int read = 0; + int len = Array_.Len(buffer); + for (int i = 0; i < count; i++) { + if (position + i >= len) break; + array[offset + i] = buffer[position + i]; + read++; + } + position += read; + return read; + } + @Override public void Write(byte[] array, int offset, int count) { + // expand buffer if needed; necessary to emulate fileStream writing; ex: FileStream fs = new FileStream(); fs.Write(data); where data may be unknown length + int length = (int)position + count + -offset; + int bufLen = Array_.Len(buffer); + if (bufLen < length) buffer = Bry_.Resize(buffer, length); + for (int i = 0; i < count; i++) + buffer[position + i] = array[offset + i]; + position += count +-offset; + } + @Override public long Pos() {return position;} + @Override public long Seek(long pos) { + this.position = (int)pos; + return pos; + } + + @Override public void Flush() {} + @Override public void Rls() {} + + public static IoStream_mem rdr_txt_(Io_url url, String v) {return rdr_ary_(url, Bry_.new_u8(v));} + public static IoStream_mem rdr_ary_(Io_url url, byte[] v) { + IoStream_mem rv = new IoStream_mem(); + rv.buffer = v; + rv.url = url; + return rv; + } + public static IoStream_mem wtr_data_(Io_url url, int length) { + IoStream_mem rv = new IoStream_mem(); + rv.buffer = new byte[length]; + rv.url = url; + return rv; + } + IoStream_mem() {} +} diff --git a/100_core/src/gplx/core/ios/streams/IoStream_mem_tst.java b/100_core/src/gplx/core/ios/streams/IoStream_mem_tst.java new file mode 100644 index 000000000..3eaa020f5 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream_mem_tst.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import org.junit.*; //using System.IO; /*Stream*/ +public class IoStream_mem_tst { + @Test public void Write() { // confirm that changes written to Stream acquired via .AdpObj are written to IoStream_mem.Buffer + IoStream_mem stream = IoStream_mem.wtr_data_(Io_url_.Empty, 0); + byte[] data = Bry_.New_by_ints(1); + stream.Write(data, 0, Array_.Len(data)); + + Tfds.Eq(1L , stream.Len()); + Tfds.Eq((byte)1 , stream.Buffer()[0]); + stream.Rls(); + } +} diff --git a/100_core/src/gplx/core/ios/streams/IoStream_mock.java b/100_core/src/gplx/core/ios/streams/IoStream_mock.java new file mode 100644 index 000000000..0a123d8c4 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream_mock.java @@ -0,0 +1,45 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public class IoStream_mock implements IoStream { + public byte[] Data_bry() {return data_bry;} public IoStream_mock Data_bry_(byte[] v) {data_bry = v; data_bry_len = v.length; return this;} private byte[] data_bry; int data_bry_len; + public int Data_bry_pos() {return data_bry_pos;} int data_bry_pos; + public void Reset() {data_bry_pos = 0;} + public IoStream_mock Read_limit_(int v) {read_limit = v; return this;} int read_limit; + public int Read(byte[] bfr, int bfr_bgn, int bfr_len) { + int bytes_read = bfr_len; + if (bytes_read > read_limit) bytes_read = read_limit; // stream may limit maximum read; EX: bfr_len of 16k but only 2k will be filled + int bytes_left = data_bry_len - data_bry_pos; + if (bytes_read > bytes_left) bytes_read = bytes_left; // not enough bytes left in data_bry; bytes_read = whatever is left + Bry_.Copy_by_pos(data_bry, data_bry_pos, data_bry_pos + bytes_read, bfr, bfr_bgn); + data_bry_pos += bytes_read; + return bytes_read; + } + public Object UnderRdr() {return null;} + public Io_url Url() {return Io_url_.Empty;} + public long Pos() {return -1;} + public long Len() {return -1;} + public int ReadAry(byte[] array) {return -1;} + public long Seek(long pos) {return -1;} + public void WriteAry(byte[] ary) {} + public void Write(byte[] array, int offset, int count) {} + public void Transfer(IoStream trg, int bufferLength) {} + public void Flush() {} + public void Write_and_flush(byte[] bry, int bgn, int end) {} + public void Rls() {} +} diff --git a/100_core/src/gplx/core/ios/streams/IoStream_mock_tst.java b/100_core/src/gplx/core/ios/streams/IoStream_mock_tst.java new file mode 100644 index 000000000..b77400302 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream_mock_tst.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import org.junit.*; +public class IoStream_mock_tst { + @Before public void init() {fxt.Clear();} IoStream_mock_fxt fxt = new IoStream_mock_fxt(); + @Test public void Basic() { + fxt.Init_src_str_("abcde").Init_trg_len_(5).Init_rdr_limit_(2).Init_read_len_(2); + fxt.Test_read("ab").Test_read("cd").Test_read("e"); + } + @Test public void Read_limit() { + fxt.Init_src_str_("abcde").Init_trg_len_(5).Init_rdr_limit_(2).Init_read_len_(4); + fxt.Test_read("ab").Test_read("cd").Test_read("e"); + } +} +class IoStream_mock_fxt { + public void Clear() { + if (rdr == null) + rdr = new IoStream_mock(); + rdr.Reset(); + trg_bgn = 0; + } IoStream_mock rdr; byte[] trg_bry; + public IoStream_mock_fxt Init_src_str_(String v) {rdr.Data_bry_(Bry_.new_a7(v)); return this;} + public IoStream_mock_fxt Init_trg_len_(int v) {trg_bry = new byte[v]; return this;} + public IoStream_mock_fxt Init_read_len_(int v) {read_len = v; return this;} int read_len; + public IoStream_mock_fxt Init_rdr_limit_(int v) {rdr.Read_limit_(v); return this;} + public IoStream_mock_fxt Test_read(String expd) { + int bytes_read = rdr.Read(trg_bry, trg_bgn, read_len); + Tfds.Eq(expd, String_.new_a7(trg_bry, trg_bgn, trg_bgn + bytes_read)); + trg_bgn += bytes_read; + return this; + } int trg_bgn; +} diff --git a/100_core/src/gplx/core/ios/streams/IoStream_stream_rdr.java b/100_core/src/gplx/core/ios/streams/IoStream_stream_rdr.java new file mode 100644 index 000000000..bf724c54a --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/IoStream_stream_rdr.java @@ -0,0 +1,39 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public class IoStream_stream_rdr implements IoStream { + public int Read(byte[] bfr, int bfr_bgn, int bfr_len) { + try { + return stream.read(bfr, bfr_bgn, bfr_len); + } + catch (Exception e) {throw Err_.new_exc(e, "core", "failed to read from stream");} + } + public IoStream UnderRdr_(Object v) {this.stream = (java.io.InputStream)v; return this;} java.io.InputStream stream; + public Object UnderRdr() {return stream;} + public Io_url Url() {return Io_url_.Empty;} + public long Pos() {return -1;} + public long Len() {return -1;} + public int ReadAry(byte[] array) {return -1;} + public long Seek(long pos) {return -1;} + public void WriteAry(byte[] ary) {} + public void Write(byte[] array, int offset, int count) {} + public void Transfer(IoStream trg, int bufferLength) {} + public void Flush() {} + public void Write_and_flush(byte[] bry, int bgn, int end) {} + public void Rls() {} +} diff --git a/100_core/src/gplx/core/ios/streams/Io_stream_rdr.java b/100_core/src/gplx/core/ios/streams/Io_stream_rdr.java new file mode 100644 index 000000000..7cd94d671 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/Io_stream_rdr.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public interface Io_stream_rdr extends Rls_able { + byte Tid(); + boolean Exists(); + Io_url Url(); Io_stream_rdr Url_(Io_url v); + long Len(); Io_stream_rdr Len_(long v); + Io_stream_rdr Open(); + void Open_mem(byte[] v); + Object Under(); + + int Read(byte[] bry, int bgn, int len); + long Skip(long len); +} diff --git a/100_core/src/gplx/core/ios/streams/Io_stream_rdr_.java b/100_core/src/gplx/core/ios/streams/Io_stream_rdr_.java new file mode 100644 index 000000000..b845469fe --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/Io_stream_rdr_.java @@ -0,0 +1,102 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import gplx.core.ios.streams.rdrs.*; +public class Io_stream_rdr_ { + public static final int Read_done = -1, Read_done_compare = 1; + public static final Io_stream_rdr Noop = new Io_stream_rdr__noop(); + public static Io_stream_rdr New__raw(Io_url url) {return new Io_stream_rdr__raw().Url_(url);} + public static Io_stream_rdr New__raw(java.io.InputStream strm) {return new Io_stream_rdr__raw().Under_(strm);} + private static Io_stream_rdr New__zip(Io_url url) {return new Io_stream_rdr__zip().Url_(url);} + private static Io_stream_rdr New__gzip(Io_url url) {return new Io_stream_rdr__gzip().Url_(url);} + public static Io_stream_rdr New__bzip2(Io_url url) {return new Io_stream_rdr__bzip2().Url_(url);} + public static Io_stream_rdr New__mem(String v) {return New__mem(Bry_.new_u8(v));} + public static Io_stream_rdr New__mem(byte[] v) { + Io_stream_rdr rv = new Io_stream_rdr__adp(New__mem_as_stream(v)); + rv.Len_(v.length); + return rv; + } + public static java.io.InputStream New__mem_as_stream(byte[] v) { + return new java.io.ByteArrayInputStream(v); + } + public static Io_stream_rdr New_by_url(Io_url url) { + String ext = url.Ext(); + if (String_.Eq(ext, Io_stream_tid_.Ext__zip)) return Io_stream_rdr_.New__zip(url); + else if (String_.Eq(ext, Io_stream_tid_.Ext__gz)) return Io_stream_rdr_.New__gzip(url); + else if (String_.Eq(ext, Io_stream_tid_.Ext__bz2)) return Io_stream_rdr_.New__bzip2(url); + else if (String_.Eq(ext, Io_stream_tid_.Ext__xz)) return new Io_stream_rdr__xz().Url_(url); + else return Io_stream_rdr_.New__raw(url); + } + public static Io_stream_rdr New_by_tid(byte tid) { + switch (tid) { + case Io_stream_tid_.Tid__raw: return new Io_stream_rdr__raw(); + case Io_stream_tid_.Tid__zip: return new Io_stream_rdr__zip(); + case Io_stream_tid_.Tid__gzip: return new Io_stream_rdr__gzip(); + case Io_stream_tid_.Tid__bzip2: return new Io_stream_rdr__bzip2(); + case Io_stream_tid_.Tid__xz: return new Io_stream_rdr__xz(); + default: throw Err_.new_unhandled_default(tid); + } + } + public static String Load_all_as_str(Io_stream_rdr rdr) {return String_.new_u8(Load_all_as_bry(Bry_bfr_.New(), rdr));} + public static byte[] Load_all_as_bry(Bry_bfr rv, Io_stream_rdr rdr) { + Load_all_to_bfr(rv, rdr); + return rv.To_bry_and_clear(); + } + public static void Load_all_to_bfr(Bry_bfr rv, Io_stream_rdr rdr) { + try { + byte[] bry = new byte[4096]; + while (true) { + int read = rdr.Read(bry, 0, 4096); + if (read < gplx.core.ios.streams.Io_stream_rdr_.Read_done_compare) break; + rv.Add_mid(bry, 0, read); + } + } finally {rdr.Rls();} + } + public static int Read_by_parts(java.io.InputStream stream, int part_len, byte[] bry, int bgn, int len) { + /* + NOTE: BZip2CompressorInputStream will fail if large len is used + Instead, make smaller requests and fill bry + */ + try { + int rv = 0; + int end = bgn + len; + int cur = bgn; + while (true) { + int bry_len = part_len; // read in increments of part_len + if (cur + bry_len > end) // if cur + 8 kb > bry_len, trim to end; EX: 9 kb bry passed; 1st pass is 8kb, 2nd pass should be 1kb, not 8 kb; + bry_len = end - cur; + if (cur == end) break; // no more bytes needed; break; EX: 8 kb bry passed; 1st pass is 8kb; 2nd pass is 0 and cur == end + int read = stream.read(bry, cur, bry_len); + if (read == gplx.core.ios.streams.Io_stream_rdr_.Read_done) // read done; end + break; + rv += read; + cur += read; + } + return rv; + } + catch (Exception exc) { + throw Err_.new_exc(exc, "io", "read failed", "bgn", bgn, "len", len); + } + } + public static boolean Close(java.io.InputStream stream) { + try { + if (stream != null) stream.close(); + return true; + } catch (Exception e) {Err_.Noop(e); return false;} + } +} diff --git a/100_core/src/gplx/core/ios/streams/Io_stream_rdr__tst.java b/100_core/src/gplx/core/ios/streams/Io_stream_rdr__tst.java new file mode 100644 index 000000000..a3ea55cb9 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/Io_stream_rdr__tst.java @@ -0,0 +1,56 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import org.junit.*; +public class Io_stream_rdr__tst { + @Before public void init() {fxt.Clear();} private Io_stream_rdr__fxt fxt = new Io_stream_rdr__fxt(); + @After public void term() {fxt.Rls();} + @Test public void Bz2_read() { + fxt .Init_stream("abcd") // read everything at once + .Expd_bytes_read(4).Test_read(0, 4, "abcd"); + fxt .Init_stream("abcd") // read in steps + .Expd_bytes_read(1).Test_read(0, 1, "a") + .Expd_bytes_read(2).Test_read(1, 2, "bc") + .Expd_bytes_read(1).Test_read(3, 1, "d") + ; + } +} +class Io_stream_rdr__fxt { + private java.io.InputStream stream; + private int stream_bry_len; + public void Clear() { + expd_bytes_read = Int_.Min_value; + } + public Io_stream_rdr__fxt Expd_bytes_read(int v) {expd_bytes_read = v; return this;} private int expd_bytes_read = Int_.Min_value; + public Io_stream_rdr__fxt Init_stream(String v) { + byte[] stream_bry = Bry_.new_a7(v); + stream_bry_len = stream_bry.length; + stream = Io_stream_rdr_.New__mem_as_stream(stream_bry); + return this; + } + public Io_stream_rdr__fxt Test_read(int bgn, int len, String expd_str) { + byte[] bfr = new byte[stream_bry_len]; // allocate whole stream; may not use it all + int actl_bytes_read = Io_stream_rdr_.Read_by_parts(stream, 8, bfr, bgn, len); + Tfds.Eq(expd_bytes_read, actl_bytes_read, "bytes_read"); + Tfds.Eq(expd_str, String_.new_u8(bfr, bgn, bgn + actl_bytes_read), "str"); + return this; + } + public void Rls() { + Io_stream_rdr_.Close(stream); + } +} diff --git a/100_core/src/gplx/core/ios/streams/Io_stream_tid_.java b/100_core/src/gplx/core/ios/streams/Io_stream_tid_.java new file mode 100644 index 000000000..ec5b3b2c4 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/Io_stream_tid_.java @@ -0,0 +1,58 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public class Io_stream_tid_ { + public static final byte Tid__null = 0, Tid__raw = 1, Tid__zip = 2, Tid__gzip = 3, Tid__bzip2 = 4, Tid__xz = 5; // SERIALIZED:xo.text_db;xo.html_db + public static final String Ext__zip = ".zip", Ext__gz = ".gz", Ext__bz2 = ".bz2", Ext__xz = ".xz"; + private static final String Key__raw = "raw", Key__zip = "zip", Key__gzip = "gzip", Key__bzip2 = "bzip2", Key__xz = "xz"; + + public static String To_key(byte v) { + switch (v) { + case Io_stream_tid_.Tid__raw : return Key__raw; + case Io_stream_tid_.Tid__zip : return Key__zip; + case Io_stream_tid_.Tid__gzip : return Key__gzip; + case Io_stream_tid_.Tid__bzip2 : return Key__bzip2; + case Io_stream_tid_.Tid__xz : return Key__xz; + default : throw Err_.new_unhandled_default(v); + } + } + public static byte To_tid(String v) { + if (String_.Eq(v, Key__raw)) return Io_stream_tid_.Tid__raw; + else if (String_.Eq(v, Key__zip)) return Io_stream_tid_.Tid__zip; + else if (String_.Eq(v, Key__gzip)) return Io_stream_tid_.Tid__gzip; + else if (String_.Eq(v, Key__bzip2)) return Io_stream_tid_.Tid__bzip2; + else if (String_.Eq(v, Key__xz)) return Io_stream_tid_.Tid__xz; + else throw Err_.new_unhandled_default(v); + } + public static String Obsolete_to_str(byte v) { + switch (v) { + case Io_stream_tid_.Tid__raw : return ".xdat"; + case Io_stream_tid_.Tid__zip : return ".zip"; + case Io_stream_tid_.Tid__gzip : return ".gz"; + case Io_stream_tid_.Tid__bzip2 : return ".bz2"; + default : throw Err_.new_unhandled_default(v); + } + } + public static byte Obsolete_to_tid(String v) { + if (String_.Eq(v, ".xdat")) return Io_stream_tid_.Tid__raw; + else if (String_.Eq(v, ".zip")) return Io_stream_tid_.Tid__zip; + else if (String_.Eq(v, ".gz")) return Io_stream_tid_.Tid__gzip; + else if (String_.Eq(v, ".bz2")) return Io_stream_tid_.Tid__bzip2; + else throw Err_.new_unhandled_default(v); + } +} diff --git a/100_core/src/gplx/core/ios/streams/Io_stream_wtr.java b/100_core/src/gplx/core/ios/streams/Io_stream_wtr.java new file mode 100644 index 000000000..ad49c49b1 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/Io_stream_wtr.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public interface Io_stream_wtr extends Rls_able { + byte Tid(); + Io_url Url(); Io_stream_wtr Url_(Io_url v); + void Trg_bfr_(Bry_bfr v); + Io_stream_wtr Open(); + byte[] To_ary_and_clear(); + + void Write(byte[] bry, int bgn, int len); + void Flush(); +} diff --git a/100_core/src/gplx/core/ios/streams/Io_stream_wtr_.java b/100_core/src/gplx/core/ios/streams/Io_stream_wtr_.java new file mode 100644 index 000000000..0a1a75eff --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/Io_stream_wtr_.java @@ -0,0 +1,68 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import gplx.core.ios.streams.wtrs.*; +public class Io_stream_wtr_ { + public static Io_stream_wtr New__raw(Io_url url) {return new Io_stream_wtr__raw().Url_(url);} + private static Io_stream_wtr New__zip(Io_url url) {return new Io_stream_wtr__zip().Url_(url);} + private static Io_stream_wtr New__gzip(Io_url url) {return new Io_stream_wtr__gzip().Url_(url);} + private static Io_stream_wtr New__bzip2(Io_url url) {return new Io_stream_wtr__bzip2().Url_(url);} + public static Io_stream_wtr New_by_url(Io_url url) { + String ext = url.Ext(); + if (String_.Eq(ext, Io_stream_tid_.Ext__zip)) return Io_stream_wtr_.New__zip(url); + else if (String_.Eq(ext, Io_stream_tid_.Ext__gz)) return Io_stream_wtr_.New__gzip(url); + else if (String_.Eq(ext, Io_stream_tid_.Ext__bz2)) return Io_stream_wtr_.New__bzip2(url); + else if (String_.Eq(ext, Io_stream_tid_.Ext__xz)) return new Io_stream_wtr__xz().Url_(url); + else return Io_stream_wtr_.New__raw(url); + } + public static Io_stream_wtr New_by_tid(byte v) { + switch (v) { + case Io_stream_tid_.Tid__raw: return new Io_stream_wtr__raw(); + case Io_stream_tid_.Tid__zip: return new Io_stream_wtr__zip(); + case Io_stream_tid_.Tid__gzip: return new Io_stream_wtr__gzip(); + case Io_stream_tid_.Tid__bzip2: return new Io_stream_wtr__bzip2(); + case Io_stream_tid_.Tid__xz: return new Io_stream_wtr__xz(); + default: throw Err_.new_unhandled(v); + } + } + public static Io_stream_wtr New_by_mem(Bry_bfr bfr, byte tid) { + Io_stream_wtr wtr = New_by_tid(tid).Url_(Io_url_.Empty); + wtr.Trg_bfr_(bfr); + return wtr; + } + public static void Save_rdr(Io_url url, Io_stream_rdr rdr, Io_download_fmt download_progress) { + byte[] bry = new byte[4096]; + Io_stream_wtr wtr = New_by_url(url); + try { + wtr.Open(); + if (download_progress != Io_download_fmt.Null) + download_progress.Bgn(rdr.Len()); + while (true) { + int read = rdr.Read(bry, 0, 4096); + if (read < gplx.core.ios.streams.Io_stream_rdr_.Read_done_compare) break; + if (download_progress != Io_download_fmt.Null) + download_progress.Prog(read); + wtr.Write(bry, 0, read); + } + wtr.Flush(); + if (download_progress != Io_download_fmt.Null) + download_progress.Term(); + } + finally {wtr.Rls(); rdr.Rls();} + } +} diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__adp.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__adp.java new file mode 100644 index 000000000..022c93060 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__adp.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_rdr__adp implements Io_stream_rdr { + private java.io.InputStream strm; + public Io_stream_rdr__adp(java.io.InputStream strm) {this.strm = strm;} + public Object Under() {return strm;} + public byte Tid() {return Io_stream_tid_.Tid__raw;} + public boolean Exists() {return len > 0;} + public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {this.url = v; return this;} private Io_url url; + public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len = Io_mgr.Len_null; + public void Open_mem(byte[] v) {} + public Io_stream_rdr Open() {return this;} + public int Read(byte[] bry, int bgn, int len) { + try {return strm.read(bry, bgn, len);} + catch (Exception e) {throw Err_.new_exc(e, "io", "read failed", "bgn", bgn, "len", len);} + } + public long Skip(long len) { + try {return strm.skip(len);} + catch (Exception e) {throw Err_.new_exc(e, "io", "skip failed", "len", len);} + } + public void Rls() { + try {strm.close();} + catch (Exception e) {throw Err_.new_exc(e, "io", "close failed", "url", url.Xto_api());} + } +} diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__base.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__base.java new file mode 100644 index 000000000..5958cfdc5 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__base.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public abstract class Io_stream_rdr__base implements Io_stream_rdr { + public abstract byte Tid(); + public Io_url Url() {return url;} protected Io_url url; + public long Len() {return len;} private long len = Io_mgr.Len_null; + public boolean Exists() {return this.Len() > 0;} + public Io_stream_rdr Url_(Io_url v) {this.url = v; return this;} + public Io_stream_rdr Len_(long v) {len = v; return this;} + public Object Under() {return stream;} public Io_stream_rdr Under_(java.io.InputStream v) {this.stream = v; return this;} protected java.io.InputStream stream; + public void Open_mem(byte[] v) { + stream = Wrap_stream(new java.io.ByteArrayInputStream(v)); + } + public Io_stream_rdr Open() { + try {stream = Wrap_stream(new java.io.FileInputStream(url.Xto_api()));} + catch (Exception e) {throw Err_.new_exc(e, "io", "open failed", "url", url.Xto_api());} + return this; + } + public int Read(byte[] bry, int bgn, int len) { + try {return stream.read(bry, bgn, len);} + catch (Exception e) {throw Err_.new_exc(e, "io", "read failed", "bgn", bgn, "len", len);} + } + public long Skip(long len) { + try {return stream.skip(len);} + catch (Exception e) {throw Err_.new_exc(e, "io", "skip failed", "len", len);} + } + public void Rls() { + try {stream.close();} + catch (Exception e) {throw Err_.new_exc(e, "io", "close failed", "url", url.Xto_api());} + } + public abstract java.io.InputStream Wrap_stream(java.io.InputStream stream); + } diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__bzip2.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__bzip2.java new file mode 100644 index 000000000..baf173d0c --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__bzip2.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_rdr__bzip2 extends Io_stream_rdr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__bzip2;} + @Override public int Read(byte[] bry, int bgn, int len) { + return Io_stream_rdr_.Read_by_parts(stream, Read_len, bry, bgn, len); + } + @Override public java.io.InputStream Wrap_stream(java.io.InputStream stream) { + try {return new org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream(stream, true);} + catch (Exception exc) {throw Err_.new_wo_type("failed to open bzip2 stream");} + } + private static final int Read_len = Io_mgr.Len_mb * 128; + } diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__gzip.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__gzip.java new file mode 100644 index 000000000..7ec7ad93b --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__gzip.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_rdr__gzip extends Io_stream_rdr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__gzip;} + @Override public int Read(byte[] bry, int bgn, int len) { + synchronized (this) { + try { + int total_read = 0; + while (true) { // NOTE: the gz stream reads partially; (request 100; only get back 10); keep reading until entire bfr is full or -1 + int read = stream.read(bry, bgn, len); + if (read == Io_stream_rdr_.Read_done) break; + total_read += read; + if (total_read >= len) break; // entire bfr full; stop + bgn += read; // increase bgn by amount read + len -= read; // decrease len by amount read + } + return total_read == 0 ? Io_stream_rdr_.Read_done : total_read; // gzip seems to allow 0 bytes read (bz2 and zip return -1 instead); normalize return to -1; + } + catch (Exception e) { + throw Err_.new_exc(e, "io", "read failed", "bgn", bgn, "len", len); + } + } + } + @Override public java.io.InputStream Wrap_stream(java.io.InputStream stream) { + try {return new java.util.zip.GZIPInputStream(stream);} + catch (Exception exc) {throw Err_.new_wo_type("failed to open gz stream");} + } + } diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__noop.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__noop.java new file mode 100644 index 000000000..0efb8aa87 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__noop.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_rdr__noop implements Io_stream_rdr { + public Object Under() {return null;} + public byte Tid() {return Io_stream_tid_.Tid__null;} + public boolean Exists() {return false;} + public Io_url Url() {return Io_url_.Empty;} public Io_stream_rdr Url_(Io_url v) {return this;} + public long Len() {return Io_mgr.Len_null;} public Io_stream_rdr Len_(long v) {return this;} + public void Open_mem(byte[] v) {} + public Io_stream_rdr Open() {return this;} + public int Read(byte[] bry, int bgn, int len) {return Io_stream_rdr_.Read_done;} + public long Skip(long len) {return Io_stream_rdr_.Read_done;} + public void Rls() {} +} diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__raw.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__raw.java new file mode 100644 index 000000000..cc45965f5 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__raw.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_rdr__raw extends Io_stream_rdr__base { + public byte Tid() {return Io_stream_tid_.Tid__raw;} + public Io_stream_rdr Open() { + Io_url url = this.Url(); + try { + if (!Io_mgr.Instance.Exists(url)) + stream = Wrap_stream(new java.io.ByteArrayInputStream(Bry_.Empty)); + else { + if (url.Info().EngineKey() == IoEngine_.MemKey) + stream = Wrap_stream(new java.io.ByteArrayInputStream(Io_mgr.Instance.LoadFilBry(url.Xto_api()))); + else + stream = Wrap_stream(new java.io.FileInputStream(url.Xto_api())); + } + } + catch (Exception e) {throw Err_.new_exc(e, "io", "open failed", "url", url.Xto_api());} + return this; + } + @Override public java.io.InputStream Wrap_stream(java.io.InputStream stream) {return stream;} + } diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__xz.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__xz.java new file mode 100644 index 000000000..f29ebfe6e --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__xz.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_rdr__xz extends Io_stream_rdr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__xz;} + @Override public java.io.InputStream Wrap_stream(java.io.InputStream stream) { + try {return new org.tukaani.xz.XZInputStream(stream);} + catch (Exception exc) {throw Err_.new_wo_type("failed to open xz stream");} + } + } diff --git a/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__zip.java b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__zip.java new file mode 100644 index 000000000..98e84ef17 --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/rdrs/Io_stream_rdr__zip.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.rdrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_rdr__zip extends Io_stream_rdr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__gzip;} + public Object Under() {return zip_stream;} private java.util.zip.ZipInputStream zip_stream; + public void Src_bfr_(Bry_bfr v) {this.src_bfr = v;} Bry_bfr src_bfr; + public void Open_mem(byte[] v) { + this.zip_stream = (java.util.zip.ZipInputStream)Wrap_stream(new java.io.ByteArrayInputStream(v)); + } + public Io_stream_rdr Open() { + try {this.zip_stream = (java.util.zip.ZipInputStream)Wrap_stream(new java.io.FileInputStream(url.Xto_api()));} + catch (Exception e) {throw Err_.new_exc(e, "io", "open failed", "url", url.Xto_api());} + return this; + } + public int Read(byte[] bry, int bgn, int len) { + try { + while (true){ + int read = zip_stream.read(bry, bgn, len); + if (read == Io_stream_rdr_.Read_done) { + if (zip_stream.getNextEntry() == null) + return Io_stream_rdr_.Read_done; + } + else + return read; + } + } + catch (Exception e) {throw Err_.new_exc(e, "io", "read failed", "bgn", bgn, "len", len);} + } + public long Skip(long len) { + try {return zip_stream.skip(len);} + catch (Exception e) {throw Err_.new_exc(e, "io", "skip failed", "len", len);} + } + public void Rls() { + try {zip_stream.close();} + catch (Exception e) {throw Err_.new_exc(e, "io", "close failed", "url", url.Xto_api());} + } + @Override public java.io.InputStream Wrap_stream(java.io.InputStream input_stream) {return new java.util.zip.ZipInputStream(input_stream);} + } diff --git a/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__base.java b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__base.java new file mode 100644 index 000000000..736751b6d --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__base.java @@ -0,0 +1,60 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.wtrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public abstract class Io_stream_wtr__base implements Io_stream_wtr { + public abstract byte Tid(); + public Io_url Url() {return url;} private Io_url url; + public Io_stream_wtr Url_(Io_url v) {url = v; return this;} + public void Trg_bfr_(Bry_bfr v) {this.trg_bfr = v;} private Bry_bfr trg_bfr; + public byte[] To_ary_and_clear() {return trg_bfr.To_bry_and_clear();} + + private java.io.OutputStream zip_stream; + private java.io.ByteArrayOutputStream mem_stream; + @Virtual public Io_stream_wtr Open() { + java.io.OutputStream bry_stream = null; + if (trg_bfr == null) { + if (!Io_mgr.Instance.ExistsFil(url)) Io_mgr.Instance.SaveFilStr(url, ""); + try {bry_stream = new java.io.FileOutputStream(url.Raw());} + catch (Exception e) {throw Err_.new_exc(e, "io", "open failed", "url", url.Raw());} + } + else { + mem_stream = new java.io.ByteArrayOutputStream(); + bry_stream = mem_stream; + } + zip_stream = Wrap_stream(bry_stream); + return this; + } + public void Write(byte[] bry, int bgn, int len) { + try {zip_stream.write(bry, bgn, len);} + catch (Exception e) {Err_.new_exc(e, "io", "write failed", "bgn", bgn, "len", len);} + } + public void Flush() { + if (trg_bfr != null) { + try {zip_stream.close();} catch (Exception e) {throw Err_.new_exc(e, "io", "flush failed");} // must close zip_stream to flush all bytes + trg_bfr.Add(mem_stream.toByteArray()); + } + } + public void Rls() { + try { + if (zip_stream != null) zip_stream.close(); + if (mem_stream != null) mem_stream.close(); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "close failed", "url", url.Raw());} + } + @Virtual protected java.io.OutputStream Wrap_stream(java.io.OutputStream stream) {throw Err_.new_unimplemented();} + } diff --git a/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__bzip2.java b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__bzip2.java new file mode 100644 index 000000000..28659a25c --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__bzip2.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.wtrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_wtr__bzip2 extends Io_stream_wtr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__bzip2;} + @Override public java.io.OutputStream Wrap_stream(java.io.OutputStream stream) { + try {return new org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream(stream);} + catch (Exception e) {throw Err_.new_exc(e, "io", "failed to open bzip2 stream");} + } + } diff --git a/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__gzip.java b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__gzip.java new file mode 100644 index 000000000..e6a846a6f --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__gzip.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.wtrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_wtr__gzip extends Io_stream_wtr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__gzip;} + @Override public java.io.OutputStream Wrap_stream(java.io.OutputStream stream) { + try {return new java.util.zip.GZIPOutputStream(stream);} + catch (Exception e) {throw Err_.new_exc(e, "io", "failed to open gz stream");} + } + } diff --git a/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__raw.java b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__raw.java new file mode 100644 index 000000000..28e229e2b --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__raw.java @@ -0,0 +1,55 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.wtrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_wtr__raw implements Io_stream_wtr { + public byte Tid() {return Io_stream_tid_.Tid__raw;} + public Io_url Url() {return url;} public Io_stream_wtr Url_(Io_url v) {url = v; return this;} private Io_url url; + public void Trg_bfr_(Bry_bfr v) {trg_bfr = v;} private Bry_bfr trg_bfr; + + private IoStream bry_stream; + @Override public Io_stream_wtr Open() { + try { + if (trg_bfr == null) + bry_stream = Io_mgr.Instance.OpenStreamWrite(url); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "open failed", "url", url.Raw());} + return this; + } + public void Write(byte[] bry, int bgn, int len) { + if (trg_bfr == null) { + try {bry_stream.Write(bry, bgn, len);} + catch (Exception e) {throw Err_.new_exc(e, "io", "write failed", "url", url.Raw(), "bgn", bgn, "len", len);} + } + else + trg_bfr.Add_mid(bry, bgn, bgn + len); + } + public byte[] To_ary_and_clear() { + return trg_bfr == null ? Io_mgr.Instance.LoadFilBry(url) : trg_bfr.To_bry_and_clear(); + } + public void Flush() { + if (trg_bfr == null) + bry_stream.Flush(); + } + public void Rls() { + try { + if (trg_bfr == null) + bry_stream.Rls(); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "close failed", "url", url.Raw());} + } + } diff --git a/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__xz.java b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__xz.java new file mode 100644 index 000000000..f623040ac --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__xz.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.wtrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_wtr__xz extends Io_stream_wtr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__xz;} + @Override public java.io.OutputStream Wrap_stream(java.io.OutputStream stream) { + try {return new org.tukaani.xz.XZOutputStream(stream, new org.tukaani.xz.LZMA2Options(org.tukaani.xz.LZMA2Options.PRESET_DEFAULT));} + catch (Exception e) {throw Err_.new_exc(e, "io", "failed to open xz stream");} + } + } diff --git a/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__zip.java b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__zip.java new file mode 100644 index 000000000..cd6240a2d --- /dev/null +++ b/100_core/src/gplx/core/ios/streams/wtrs/Io_stream_wtr__zip.java @@ -0,0 +1,68 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.streams.wtrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*; import gplx.core.ios.streams.*; +public class Io_stream_wtr__zip extends Io_stream_wtr__base { + @Override public byte Tid() {return Io_stream_tid_.Tid__zip;} + private java.util.zip.ZipOutputStream zip_stream; + public Io_url Url() {return url;} public Io_stream_wtr Url_(Io_url v) {url = v; trg_bfr = null; return this;} private Io_url url = Io_url_.Empty; + public void Trg_bfr_(Bry_bfr v) {trg_bfr = v;} private Bry_bfr trg_bfr; private java.io.ByteArrayOutputStream mem_stream; + // rely on zip_stream to close bry_stream + @Override public Io_stream_wtr Open() { + java.io.OutputStream bry_stream; + if (trg_bfr == null) { + if (!Io_mgr.Instance.ExistsFil(url)) Io_mgr.Instance.SaveFilStr(url, ""); // create file if it doesn't exist + try {bry_stream = new java.io.FileOutputStream(url.Xto_api());} + catch (Exception e) {throw Err_.new_exc(e, "io", "open failed", "url", url.Raw());} + } + else { + mem_stream = new java.io.ByteArrayOutputStream(); + bry_stream = mem_stream; + } + zip_stream = new java.util.zip.ZipOutputStream(bry_stream); + java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry("file"); + try {zip_stream.putNextEntry(entry);} + catch (Exception e) {throw Err_.new_exc(e, "io", "open failed", "url", url.Raw());} + return this; + } + public void Write(byte[] bry, int bgn, int len) { + try {zip_stream.write(bry, bgn, len);} + catch (Exception e) {throw Err_.new_exc(e, "io", "write failed", "url", url.Raw(), "bgn", bgn, "len", len);} + } + public void Flush() {// fixed as of DATE:2014-04-15 + try { + zip_stream.closeEntry(); + zip_stream.close(); + if (trg_bfr != null) + trg_bfr.Add(mem_stream.toByteArray()); + zip_stream.flush(); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "flush failed", "url", url.Raw());} + } + public void Rls() { + try { + if (zip_stream != null) zip_stream.close(); + if (mem_stream != null) mem_stream.close(); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "close failed", "url", url.Raw());} + } + public byte[] To_ary_and_clear() { + byte[] rv = trg_bfr.To_bry_and_clear(); + this.Rls(); + return rv; + } + } diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_compress_cmd__jre.java b/100_core/src/gplx/core/ios/zips/Io_zip_compress_cmd__jre.java new file mode 100644 index 000000000..f29673ce7 --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_compress_cmd__jre.java @@ -0,0 +1,74 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import java.io.*; +import java.util.zip.*; +import gplx.core.envs.*; import gplx.core.threads.*; import gplx.core.progs.*; +public class Io_zip_compress_cmd__jre { + public Io_zip_compress_cmd__jre() {} + public Io_zip_compress_cmd__jre Make_new() {return new Io_zip_compress_cmd__jre();} + public byte Exec_hook(gplx.core.progs.Gfo_prog_ui prog_ui, Io_url[] src_urls, Io_url trg_url, String resume_name, long resume_file, long resume_item) { + OutputStream trg_out_stream; + Io_mgr.Instance.CreateDirIfAbsent(trg_url.OwnerDir()); + try {trg_out_stream = new FileOutputStream(trg_url.Xto_api());} + catch (Exception e) {throw Err_.new_exc(e, "io", "trg open failed", "url", trg_url.Raw());} + ZipOutputStream trg_stream = new ZipOutputStream(trg_out_stream); + int len = src_urls.length; + byte[] buffer = new byte[4096]; + for (int i = 0; i < len; ++i) { + Io_url src_url = src_urls[i]; + java.util.zip.ZipEntry trg_entry = new java.util.zip.ZipEntry(src_url.NameAndExt()); + try {trg_stream.putNextEntry(trg_entry);} + catch (Exception e) { + try {trg_stream.close();} + catch (IOException e1) {} + throw Err_.new_exc(e, "io", "zip entry failed", "url", src_url.Raw()); + } + FileInputStream src_stream = null; + try {src_stream = new FileInputStream(new File(src_url.Raw()));} + catch (Exception e) {throw Err_.new_exc(e, "io", "src open failed", "url", src_url.Raw());} + while (true) { // loop over bytes + int read_in_raw = -1; + try {read_in_raw = src_stream.read(buffer);} + catch (Exception e) { + try {src_stream.close();} + catch (IOException e1) {} + throw Err_.new_exc(e, "io", "src read failed", "url", src_url.Raw()); + } + if (read_in_raw < 1) break; + try {trg_stream.write(buffer, 0, read_in_raw);} + catch (Exception e) { + try {src_stream.close();} + catch (IOException e1) {} + throw Err_.new_exc(e, "io", "trg write failed", "url", trg_url.Raw()); + } + } + try { + trg_stream.closeEntry(); + src_stream.close(); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "trg close entry failed", "url", src_url.Raw());} + } + try { + trg_stream.close(); + trg_stream.flush(); + } + catch (Exception e) {throw Err_.new_exc(e, "io", "trg close failed", "url", trg_url.Raw());} + return Gfo_prog_ui_.Status__done; + } +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd.java b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd.java new file mode 100644 index 000000000..9f25e4c2b --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import gplx.core.progs.*; +public interface Io_zip_decompress_cmd { + String Fail_msg(); + Io_zip_decompress_cmd Make_new(); + long Checkpoint__load_by_src_fil(Io_url src_fil); + byte Exec(gplx.core.progs.Gfo_prog_ui prog_ui, Io_url src_fil, Io_url trg_dir, List_adp trg_fils); + void Exec_cleanup(); +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd_.java b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd_.java new file mode 100644 index 000000000..2e24a684e --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd_.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public class Io_zip_decompress_cmd_ { + public static Io_zip_decompress_cmd Proto = new Io_zip_decompress_cmd__jre(); +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__base.java b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__base.java new file mode 100644 index 000000000..50339ad90 --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__base.java @@ -0,0 +1,74 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import gplx.core.progs.*; +public abstract class Io_zip_decompress_cmd__base implements Io_zip_decompress_cmd { + private Io_url checkpoint_url; + private String resume_name; + private long resume_file, resume_item; + private long checkpoint_interval = 32 * Io_mgr.Len_mb, checkpoint_nxt = 0; + public String Fail_msg() {return fail_msg;} private String fail_msg; + public abstract Io_zip_decompress_cmd Make_new(); + private final Bry_bfr bfr = Bry_bfr_.New(); + public long Prog_data_cur() {return resume_file;} + public byte Exec(gplx.core.progs.Gfo_prog_ui prog_ui, Io_url src_fil, Io_url trg_dir, List_adp trg_fils) { + this.Checkpoint__load_by_src_fil(src_fil); + this.checkpoint_nxt = resume_file + checkpoint_interval; + this.fail_msg = null; + + byte status = this.Exec_hook(prog_ui, src_fil, trg_dir, trg_fils, resume_name, resume_file, resume_item); + switch (status) { + case Gfo_prog_ui_.Status__done: + case Gfo_prog_ui_.Status__fail: + this.Exec_cleanup(); + break; + case Gfo_prog_ui_.Status__suspended: + break; + } + return status; + } + protected abstract byte Exec_hook(gplx.core.progs.Gfo_prog_ui prog_ui, Io_url src_fil, Io_url trg_dir, List_adp trg_fils, String resume_name, long resume_file, long resume_item); + public void Exec_cleanup() { + if (checkpoint_url != null) Io_mgr.Instance.DeleteFil(checkpoint_url); + } + public long Checkpoint__load_by_src_fil(Io_url src_fil) { + this.checkpoint_url = src_fil.GenNewExt(".checkpoint"); + this.Checkpoint__load(); + return resume_file; + } + private void Checkpoint__load() { + this.resume_name = null; this.resume_file = resume_item = 0; + byte[] data = Io_mgr.Instance.LoadFilBryOrNull(checkpoint_url); if (data == null) return; + byte[][] lines = Bry_split_.Split_lines(data); if (lines.length != 3) return; + this.resume_name = String_.new_u8(lines[0]); + this.resume_file = Long_.parse_or(String_.new_a7(lines[1]), -1); if (resume_file == -1) return; + this.resume_item = Long_.parse_or(String_.new_a7(lines[2]), -1); + } + public boolean Checkpoint__save(String resume_name, long resume_file, long resume_item) { + if (resume_file < checkpoint_nxt) return false; + bfr.Add_str_u8(resume_name).Add_byte_nl(); + bfr.Add_long_variable(resume_file).Add_byte_nl(); + bfr.Add_long_variable(resume_item); + Io_mgr.Instance.SaveFilBry(checkpoint_url, bfr.To_bry_and_clear()); + this.resume_name = resume_name; + this.resume_file = resume_file; + this.resume_item = resume_item; + checkpoint_nxt += checkpoint_interval; + return true; + } +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__jre.java b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__jre.java new file mode 100644 index 000000000..e41e2ee8f --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_decompress_cmd__jre.java @@ -0,0 +1,106 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import java.io.*; +import java.util.zip.*; +import gplx.core.envs.*; import gplx.core.threads.*; import gplx.core.progs.*; +class Io_zip_decompress_cmd__jre extends Io_zip_decompress_cmd__base { + @Override public Io_zip_decompress_cmd Make_new() {return new Io_zip_decompress_cmd__jre();} + @Override protected byte Exec_hook(gplx.core.progs.Gfo_prog_ui prog_ui, Io_url src_fil, Io_url trg_dir, List_adp trg_fils, String resume_name, long resume_file, long resume_item) { + // open src_zip_stream + FileInputStream src_fil_stream = null; + try {src_fil_stream = new FileInputStream(src_fil.Raw());} + catch (FileNotFoundException e) {throw Err_.new_("ios.zip", "file not found", "path", src_fil.Raw());} + ZipInputStream src_zip_stream = new ZipInputStream(src_fil_stream); + + // init variables for entry loop + boolean resumed = resume_name != null; + long file_cur_in_raw = resumed ? resume_file : 0; + long file_max_in_raw = prog_ui.Prog_data_end(); + + // if no size provided, guess length as 5x orig + if (file_max_in_raw == -1) { + file_max_in_raw = 4 * Io_mgr.Instance.QueryFil(src_fil).Size(); + } + + ZipEntry entry = null; + byte[] buffer = new byte[4096]; + Io_mgr.Instance.CreateDirIfAbsent(trg_dir); // NOTE: assert that trg_dir exists + + try { + while (true) { // loop over entries + entry = src_zip_stream.getNextEntry(); + if (entry == null) break; // no more entries + if (resume_name != null) { // resume_entry_name will be null in most cases + if (String_.Eq(resume_name, entry.getName())) // if resume_entry_name is not null, keep reading until match + resume_name = null; + else + continue; + } + + // get entry name; also convert / to \ for wnt + String entry_name = entry.getName(); + if (Op_sys.Cur().Tid_is_wnt()) entry_name = String_.Replace(entry_name, "/", "\\"); + + // create file + Io_url trg_fil_url = Io_url_.new_any_(trg_dir.GenSubFil(entry_name).Raw()); + Io_url trg_tmp_url = trg_fil_url.GenNewNameAndExt(trg_fil_url.NameAndExt() + ".tmp"); + if (trg_fil_url.Type_fil()) { + // handle resume + long item_in_raw = 0; + if (resume_item > 0) { + src_zip_stream.skip(resume_item); + Io_mgr.Instance.Truncate_fil(trg_tmp_url, resume_item); + item_in_raw = resume_item; + resume_item = 0; + } + FileOutputStream trg_fil_stream = new FileOutputStream(new File(trg_tmp_url.Raw()), resumed); + if (resumed) resumed = false; + boolean loop = true; + while (loop) { // loop over bytes + int read_in_raw = src_zip_stream.read(buffer); if (read_in_raw < 1) break; + trg_fil_stream.write(buffer, 0, read_in_raw); + item_in_raw += read_in_raw; + file_cur_in_raw += read_in_raw; + Checkpoint__save(entry_name, file_cur_in_raw, item_in_raw); + if (prog_ui.Prog_notify_and_chk_if_suspended(file_cur_in_raw, file_max_in_raw)) { + loop = false; + break; + } + } + trg_fil_stream.close(); + if (!loop) return Gfo_prog_ui_.Status__suspended; // manually canceled + Io_mgr.Instance.MoveFil_args(trg_tmp_url, trg_fil_url, true).Exec(); + trg_fils.Add(trg_fil_url); + } + else { + Io_mgr.Instance.CreateDir(trg_fil_url); + } + } + Gfo_evt_mgr_.Pub_val(Io_mgr.Instance, Io_mgr.Evt__fil_created, trg_fils.To_ary(Io_url.class)); + } + catch(IOException e) {throw Err_.new_exc(e, "ios.zip", "error duing unzip", "src", src_fil.Raw(), "trg", trg_dir.Raw());} + finally { + try { + // src_zip_stream.closeEntry(); // TOMBSTONE: takes a long time to close; does not seem to be necessary + src_zip_stream.close(); + } catch (Exception e) {} + } + return Gfo_prog_ui_.Status__done; + } +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_mgr.java b/100_core/src/gplx/core/ios/zips/Io_zip_mgr.java new file mode 100644 index 000000000..8d1915037 --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_mgr.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public interface Io_zip_mgr { + void Zip_fil(Io_url src_fil, Io_url trg_fil); + byte[] Zip_bry(byte[] src, int bgn, int len); + byte[] Unzip_bry(byte[] src, int bgn, int len); + void Unzip_to_dir(Io_url src_fil, Io_url trg_dir); + void Zip_dir(Io_url src_dir, Io_url trg_fil); +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_mgr_base.java b/100_core/src/gplx/core/ios/zips/Io_zip_mgr_base.java new file mode 100644 index 000000000..d084ba9c2 --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_mgr_base.java @@ -0,0 +1,120 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import java.io.*; +import java.util.zip.*; +import gplx.core.envs.*; +public class Io_zip_mgr_base implements Io_zip_mgr { + public void Zip_fil(Io_url src_fil, Io_url trg_fil) { + byte[] src_bry = Io_mgr.Instance.LoadFilBry(src_fil); + byte[] trg_bry = Zip_bry(src_bry, 0, src_bry.length); + Io_mgr.Instance.SaveFilBry(trg_fil, trg_bry); + } + public void Zip_dir(Io_url src_dir, Io_url trg_fil) { + try { + byte[] bry = new byte[4096]; + FileOutputStream fil_strm = new FileOutputStream(trg_fil.Raw()); + ZipOutputStream zip_strm = new ZipOutputStream(fil_strm); + Zip_dir__add_dir(zip_strm, bry, "", src_dir, Zip_dir__get_subs(src_dir)); + zip_strm.flush(); + zip_strm.close(); + } catch(IOException e) {Err_.new_exc(e, "io", "error duing zip", "src", src_dir.Raw(), "trg", trg_fil.Raw());} + } + private void Zip_dir__add_dir(ZipOutputStream zip_strm, byte[] bry, String zip_path, Io_url owner_dir, Io_url[] subs) { + int len = subs.length; + for (int i = 0; i < len; i++) { + Io_url sub = subs[i]; + String sub_path = zip_path + sub.NameAndExt_noDirSpr(); + if (sub.Type_dir()) + Zip_dir__add_dir(zip_strm, bry, sub_path + "/", sub, Zip_dir__get_subs(sub)); + else + Zip_dir__add_fil(zip_strm, bry, sub_path, sub); + } + } + private void Zip_dir__add_fil(ZipOutputStream zip_strm, byte[] bry, String zip_path, Io_url fil_url) { + try { + int len; + FileInputStream fil_strm = new FileInputStream(fil_url.Raw()); + zip_strm.putNextEntry(new ZipEntry(zip_path)); + while ((len = fil_strm.read(bry)) > 0) + zip_strm.write(bry, 0, len); + fil_strm.close(); + } catch(IOException e) {throw Err_.new_exc(e, "io", "error duing zip", "src", zip_path);} + } + private Io_url[] Zip_dir__get_subs(Io_url url) { + return Io_mgr.Instance.QueryDir_args(url).DirInclude_().ExecAsUrlAry(); + } + public byte[] Zip_bry(byte[] src, int bgn, int len) { + ByteArrayInputStream src_stream = new ByteArrayInputStream(src, bgn, len); + ByteArrayOutputStream trg_stream = new ByteArrayOutputStream(len); + try { + ZipOutputStream trgZip = new ZipOutputStream(trg_stream); + ZipEntry entry = new ZipEntry("file"); + trgZip.putNextEntry(entry); + int count; + while((count = src_stream.read(tmp, 0, tmpLen)) != -1) { + trgZip.write(tmp, 0, count); + } + trgZip.close(); + } catch(Exception e) {throw Err_.new_wo_type("failed to zip", "err", e.getMessage());} + return trg_stream.toByteArray(); + } + public byte[] Unzip_bry(byte[] src, int bgn, int len) { + ByteArrayInputStream src_stream = new ByteArrayInputStream(src, bgn, len); + ByteArrayOutputStream trg_stream = new ByteArrayOutputStream(len); + try { + ZipInputStream srcZip = new ZipInputStream(src_stream); + int count; + while(srcZip.getNextEntry() != null) { + while ((count = srcZip.read(tmp, 0, tmpLen)) != -1) { + trg_stream.write(tmp, 0, count); + } + } + } catch(Exception e) {throw Err_.new_wo_type("failed to unzip", "err", e.getMessage());} + return trg_stream.toByteArray(); + } + public void Unzip_to_dir(Io_url src_fil, Io_url trg_dir) { + byte[] buffer = new byte[4096]; + try{ + Io_mgr.Instance.CreateDirIfAbsent(trg_dir); + + ZipInputStream zip_strm = new ZipInputStream(new FileInputStream(src_fil.Raw())); + ZipEntry zip_eny = zip_strm.getNextEntry(); + while (zip_eny != null) { + String itm_name = zip_eny.getName(); + if (Op_sys.Cur().Tid_is_wnt()) itm_name = String_.Replace(itm_name, "/", "\\"); + Io_url itm_url = Io_url_.new_any_(trg_dir.GenSubFil(itm_name).Raw()); + Io_mgr.Instance.CreateDirIfAbsent(itm_url.OwnerDir()); // make sure owner dir exists + if (itm_url.Type_fil()) { + Io_mgr.Instance.SaveFilStr_args(itm_url, "").Exec(); + File itm_file = new File(itm_url.Raw()); + FileOutputStream itm_strm = new FileOutputStream(itm_file); + int len; + while ((len = zip_strm.read(buffer)) > 0) + itm_strm.write(buffer, 0, len); + itm_strm.close(); + } + zip_eny = zip_strm.getNextEntry(); + } + zip_strm.closeEntry(); + zip_strm.close(); + } catch(IOException e) {throw Err_.new_exc(e, "io", "error duing unzip", "src", src_fil.Raw(), "trg", trg_dir.Raw());} + } + byte[] tmp = new byte[4096]; int tmpLen = 4096; + public static final Io_zip_mgr Instance = new Io_zip_mgr_base(); +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_mgr_mok.java b/100_core/src/gplx/core/ios/zips/Io_zip_mgr_mok.java new file mode 100644 index 000000000..579e0266b --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_mgr_mok.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +public class Io_zip_mgr_mok implements Io_zip_mgr { + public void Zip_fil(Io_url src_fil, Io_url trg_fil) { + byte[] src_bry = Io_mgr.Instance.LoadFilBry(src_fil); + byte[] zip_bry = Zip_bry(src_bry, 0, src_bry.length); + Io_mgr.Instance.SaveFilBry(trg_fil, zip_bry); + } + public void Zip_dir(Io_url src_dir, Io_url trg_fil) {} + public byte[] Zip_bry(byte[] src, int bgn, int len) {return Bry_.Add(Bry_zipped, Bry_.Mid(src, bgn, len));} + public byte[] Unzip_bry(byte[] src, int bgn, int len) { + if (src == Bry_.Empty) return src; + byte[] section = Bry_.Mid(src, bgn, bgn + len); + if (!Bry_.Has_at_bgn(section, Bry_zipped, 0, section.length)) throw Err_.new_wo_type("src not zipped", "section", String_.new_u8(section)); + return Bry_.Mid(section, Bry_zipped.length, section.length); + } + public void Unzip_to_dir(Io_url src_fil, Io_url trg_dir) {} + private static final byte[] Bry_zipped = Bry_.new_a7("zipped:"); + public static final Io_zip_mgr_mok Instance = new Io_zip_mgr_mok(); Io_zip_mgr_mok() {} +} diff --git a/100_core/src/gplx/core/ios/zips/Io_zip_mgr_tst.java b/100_core/src/gplx/core/ios/zips/Io_zip_mgr_tst.java new file mode 100644 index 000000000..589b556e9 --- /dev/null +++ b/100_core/src/gplx/core/ios/zips/Io_zip_mgr_tst.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios.zips; import gplx.*; import gplx.core.*; import gplx.core.ios.*; +import org.junit.*; +public class Io_zip_mgr_tst { + @Test public void Zip_unzip() { + Zip_unzip_tst("abcdefghijklmnopqrstuvwxyz"); + } + private void Zip_unzip_tst(String s) { + Io_zip_mgr zip_mgr = Io_zip_mgr_base.Instance; + byte[] src = Bry_.new_a7(s); + byte[] zip = zip_mgr.Zip_bry(src, 0, src.length); + byte[] unz = zip_mgr.Unzip_bry(zip, 0, zip.length); + Tfds.Eq_ary(src, unz); + } +} diff --git a/100_core/src/gplx/core/js/Js_wtr.java b/100_core/src/gplx/core/js/Js_wtr.java new file mode 100644 index 000000000..33bdd87d3 --- /dev/null +++ b/100_core/src/gplx/core/js/Js_wtr.java @@ -0,0 +1,107 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.js; import gplx.*; import gplx.core.*; +public class Js_wtr { + private final Bry_bfr bfr = Bry_bfr_.Reset(32); + private int arg_idx = 0, ary_idx = 0; + public byte Quote_char() {return quote_char;} public Js_wtr Quote_char_(byte v) {quote_char = v; return this;} private byte quote_char = Byte_ascii.Quote; + public void Clear() {bfr.Clear();} + public String To_str() {return bfr.To_str();} + public String To_str_and_clear() {return bfr.To_str_and_clear();} + public Js_wtr Func_init(String name) {return Func_init(Bry_.new_u8(name));} + public Js_wtr Func_init(byte[] name) { + bfr.Add(name).Add_byte(Byte_ascii.Paren_bgn); + arg_idx = 0; + return this; + } + public Js_wtr Func_term() { + bfr.Add_byte(Byte_ascii.Paren_end).Add_byte_semic(); + return this; + } + public Js_wtr Prm_bry(byte[] bry) { + Prm_spr(); + Write_val(bry); + return this; + } + public Js_wtr Prm_obj_ary(Object[] ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; ++i) { + Object itm = ary[i]; + if (i != 0) bfr.Add_byte(Byte_ascii.Comma); + boolean val_needs_quotes = true; + if ( Type_adp_.Eq_typeSafe(itm, Bool_.Cls_ref_type) + || Type_adp_.Eq_typeSafe(itm, Int_.Cls_ref_type) + || Type_adp_.Eq_typeSafe(itm, Long_.Cls_ref_type) + ) { + val_needs_quotes = false; + } + if (val_needs_quotes) + Write_val(Bry_.new_u8(Object_.Xto_str_strict_or_null_mark(itm))); + else + bfr.Add_obj_strict(itm); + } + return this; + } + public Js_wtr Ary_init() { + ary_idx = 0; + bfr.Add_byte(Byte_ascii.Brack_bgn); + return this; + } + public Js_wtr Ary_term() { + bfr.Add_byte(Byte_ascii.Brack_end); + return this; + } + public void Prm_spr() { + if (arg_idx != 0) bfr.Add_byte(Byte_ascii.Comma); + ++arg_idx; + } + private void Ary_spr() { + if (ary_idx != 0) bfr.Add_byte(Byte_ascii.Comma); + ++ary_idx; + } + public Js_wtr Ary_bry(byte[] bry) { + Ary_spr(); + Write_val(bry); + return this; + } + private Js_wtr Write_keyword_return() {bfr.Add(Keyword_return); return this;} + public Js_wtr Write_statement_return_func(String func, Object... args) { + this.Write_keyword_return(); + this.Func_init(func); + this.Prm_obj_ary(args); + this.Func_term(); + return this; + } + public void Write_val(byte[] bry) { + bfr.Add_byte(quote_char); + int len = bry.length; + for (int i = 0; i < len; i++) { + byte b = bry[i]; + switch (b) { + case Byte_ascii.Backslash: // "\" -> "\\"; needed else js will usurp \ as escape; EX: "\&" -> "&"; DATE:2014-06-24 + case Byte_ascii.Quote: + case Byte_ascii.Apos: bfr.Add_byte(Byte_ascii.Backslash).Add_byte(b); break; + case Byte_ascii.Nl: bfr.Add_byte(Byte_ascii.Backslash).Add_byte(Byte_ascii.Ltr_n); break; // "\n" -> "\\n" + case Byte_ascii.Cr: break;// skip + default: bfr.Add_byte(b); break; + } + } + bfr.Add_byte(quote_char); + } + private static final byte[] Keyword_return = Bry_.new_a7("return "); +} diff --git a/100_core/src/gplx/core/js/Js_wtr_tst.java b/100_core/src/gplx/core/js/Js_wtr_tst.java new file mode 100644 index 000000000..46a98d32d --- /dev/null +++ b/100_core/src/gplx/core/js/Js_wtr_tst.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.js; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Js_wtr_tst { + @Before public void Init() {fxt.Clear();} private Js_wtr_fxt fxt = new Js_wtr_fxt(); + @Test public void Basic() { + fxt.Test_write_val_html("abc" , "'abc'"); + fxt.Test_write_val_html("a'b" , "'a\\'b'"); + fxt.Test_write_val_html("a\"b" , "'a\\\"b'"); + fxt.Test_write_val_html("a\nb" , "'a\\nb'"); + fxt.Test_write_val_html("a\rb" , "'ab'"); + fxt.Test_write_val_html("a\\&b" , "'a\\\\&b'"); // PURPOSE: backslashes need to be escaped; need for MathJax and "\&"; PAGE:Electromagnetic_field_tensor; DATE:2014-06-24 + } +} +class Js_wtr_fxt { + private Js_wtr wtr = new Js_wtr(); + public void Clear() { + wtr.Clear(); + wtr.Quote_char_(Byte_ascii.Apos); + } + public void Test_write_val_html(String raw, String expd) { + wtr.Write_val(Bry_.new_u8(raw)); + Tfds.Eq(expd, wtr.To_str_and_clear()); + } +} diff --git a/100_core/src/gplx/core/lists/ComparerAble.java b/100_core/src/gplx/core/lists/ComparerAble.java new file mode 100644 index 000000000..e676f93c4 --- /dev/null +++ b/100_core/src/gplx/core/lists/ComparerAble.java @@ -0,0 +1,20 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public interface ComparerAble extends java.util.Comparator {} +// public int compare(Object lhsObj, Object rhsObj) {} diff --git a/100_core/src/gplx/core/lists/ComparerAble_.java b/100_core/src/gplx/core/lists/ComparerAble_.java new file mode 100644 index 000000000..100c2d1a8 --- /dev/null +++ b/100_core/src/gplx/core/lists/ComparerAble_.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public class ComparerAble_ { + public static int Compare(ComparerAble comparer, Object lhs, Object rhs) {return comparer.compare(lhs, rhs);} +} diff --git a/100_core/src/gplx/core/lists/EnumerAble.java b/100_core/src/gplx/core/lists/EnumerAble.java new file mode 100644 index 000000000..5554fd3a7 --- /dev/null +++ b/100_core/src/gplx/core/lists/EnumerAble.java @@ -0,0 +1,19 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public interface EnumerAble extends java.lang.Iterable {}//_20110320 diff --git a/100_core/src/gplx/core/lists/Hash_adp_base.java b/100_core/src/gplx/core/lists/Hash_adp_base.java new file mode 100644 index 000000000..0c0c87421 --- /dev/null +++ b/100_core/src/gplx/core/lists/Hash_adp_base.java @@ -0,0 +1,50 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public abstract class Hash_adp_base implements Hash_adp { + public boolean Has(Object key) {return Has_base(key);} + public Object Get_by(Object key) {return Fetch_base(key);} + public Object Get_by_or_fail(Object key) {return Get_by_or_fail_base(key);} + public void Add(Object key, Object val) {Add_base(key, val);} + public void Add_as_key_and_val(Object val) {Add_base(val, val);} + public void Add_if_dupe_use_nth(Object key, Object val) { + Object existing = Fetch_base(key); if (existing != null) Del(key); // overwrite if exists + Add(key, val); + } + public boolean Add_if_dupe_use_1st(Object key, Object val) { + if (Has(key)) return false; + Add(key, val); + return true; + } + @gplx.Virtual public void Del(Object key) {Del_base(key);} + protected Object Get_by_or_fail_base(Object key) { + if (key == null) throw Err_.new_wo_type("key cannot be null"); + if (!Has_base(key)) throw Err_.new_wo_type("key not found", "key", key); + return Fetch_base(key); + } + + // private final java.util.HashMap hash = new java.util.HashMap(); + private final java.util.Hashtable hash = new java.util.Hashtable(); + @gplx.Virtual public int Count() {return hash.size();} + @gplx.Virtual public void Clear() {hash.clear();} + @gplx.Virtual protected void Add_base(Object key, Object val) {hash.put(key, val);} + @gplx.Virtual protected void Del_base(Object key) {hash.remove(key);} + @gplx.Virtual protected boolean Has_base(Object key) {return hash.containsKey(key);} + @gplx.Virtual protected Object Fetch_base(Object key) {return hash.get(key);} + @gplx.Virtual public java.util.Iterator iterator() {return hash.values().iterator();} +} diff --git a/100_core/src/gplx/core/lists/Hash_adp_list.java b/100_core/src/gplx/core/lists/Hash_adp_list.java new file mode 100644 index 000000000..365c2d96a --- /dev/null +++ b/100_core/src/gplx/core/lists/Hash_adp_list.java @@ -0,0 +1,40 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public class Hash_adp_list extends Hash_adp_base { + @gplx.New public List_adp Get_by(Object key) {return ((List_adp)Fetch_base(key));} + public List_adp Get_by_or_new(Object key) { + List_adp rv = Get_by(key); + if (rv == null) { + rv = List_adp_.New(); + Add_base(key, rv); + } + return rv; + } + public void AddInList(Object key, Object val) { + List_adp list = Get_by_or_new(key); + list.Add(val); + } + public void DelInList(Object key, Object val) { + List_adp list = Get_by(key); + if (list == null) return; + list.Del(val); + if (list.Count() == 0) Del(key); + } + public static Hash_adp_list new_() {return new Hash_adp_list();} Hash_adp_list() {} +} diff --git a/100_core/src/gplx/core/lists/Iterator_null.java b/100_core/src/gplx/core/lists/Iterator_null.java new file mode 100644 index 000000000..cef453964 --- /dev/null +++ b/100_core/src/gplx/core/lists/Iterator_null.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public class Iterator_null implements java.util.Iterator { + public boolean hasNext() {return false;} + public Object next() {return null;} + public void remove() {} + public static final Iterator_null Instance = new Iterator_null(); +} diff --git a/100_core/src/gplx/core/lists/Iterator_objAry.java b/100_core/src/gplx/core/lists/Iterator_objAry.java new file mode 100644 index 000000000..f30df9cca --- /dev/null +++ b/100_core/src/gplx/core/lists/Iterator_objAry.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public class Iterator_objAry implements java.util.Iterator { + public boolean hasNext() {return ++pos < len;} + public Object next() {return ary[pos];} + public void remove() {pos = -1;} + Object[] ary; int pos = -1; int len = 0; + public Iterator_objAry(Object[] v, int count) {ary = v; len = count;} +} \ No newline at end of file diff --git a/100_core/src/gplx/core/lists/List_adp__getable.java b/100_core/src/gplx/core/lists/List_adp__getable.java new file mode 100644 index 000000000..a98b79241 --- /dev/null +++ b/100_core/src/gplx/core/lists/List_adp__getable.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public interface List_adp__getable { + int Len(); + Object Get_at(int i); +} diff --git a/100_core/src/gplx/core/lists/List_adp_sorter.java b/100_core/src/gplx/core/lists/List_adp_sorter.java new file mode 100644 index 000000000..a4edea36c --- /dev/null +++ b/100_core/src/gplx/core/lists/List_adp_sorter.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public class List_adp_sorter { + private ComparerAble comparer = null; + public void Sort(Object[] orig, int origLen) {Sort(orig, origLen, true, null);} + public void Sort(Object[] orig, int origLen, boolean asc, ComparerAble comparer) { + this.comparer = comparer; + Object[] temp = new Object[origLen]; + MergeSort(asc, orig, temp, 0, origLen - 1); + this.comparer = null; + } + void MergeSort(boolean asc, Object[] orig,Object[] temp, int lhs, int rhs) { + if (lhs < rhs) { + int mid = (lhs + rhs) / 2; + MergeSort(asc, orig, temp, lhs, mid); + MergeSort(asc, orig, temp, mid + 1, rhs); + Combine(asc, orig, temp, lhs, mid + 1, rhs); + } + } + private void Combine(boolean asc, Object[] orig, Object[] temp, int lhsPos, int rhsPos, int rhsEnd) { + int lhsEnd = rhsPos - 1; + int tmpPos = lhsPos; + int aryLen = rhsEnd - lhsPos + 1; + + while (lhsPos <= lhsEnd && rhsPos <= rhsEnd) { + int compareVal = 0; + if (comparer != null) + compareVal = ComparerAble_.Compare(comparer, orig[lhsPos], orig[rhsPos]); + else { + Comparable lhsComp = (Comparable)orig[lhsPos]; + compareVal = lhsComp == null ? CompareAble_.Less : lhsComp.compareTo(orig[rhsPos]); + } + if (!asc) compareVal *= -1; + if (compareVal <= CompareAble_.Same) // NOTE: (a) must be < 0; JAVA's String.compareTo returns -number based on position; (b) must be <= else sorting sorted list will change order; EX: sorting (a,1;a,2) on fld0 will switch to (a,2;a,1) + temp[tmpPos++] = orig[lhsPos++]; + else + temp[tmpPos++] = orig[rhsPos++]; + } + + while (lhsPos <= lhsEnd) // Copy rest of first half + temp[tmpPos++] = orig[lhsPos++]; + while (rhsPos <= rhsEnd) // Copy rest of right half + temp[tmpPos++] = orig[rhsPos++]; + for (int i = 0; i < aryLen; i++, rhsEnd--) + orig[rhsEnd] = temp[rhsEnd]; + } + + public static List_adp_sorter new_() {return new List_adp_sorter();} List_adp_sorter() {} +} diff --git a/100_core/src/gplx/core/lists/List_adp_sorter_tst.java b/100_core/src/gplx/core/lists/List_adp_sorter_tst.java new file mode 100644 index 000000000..c3be7d455 --- /dev/null +++ b/100_core/src/gplx/core/lists/List_adp_sorter_tst.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +import org.junit.*; +public class List_adp_sorter_tst { + @Test public void Basic() { + Object[] src = new Object[] {0,8,1,7,2,6,3,5,4}; + List_adp_sorter.new_().Sort(src, src.length); + Tfds.Eq_ary(src, Sequential(0, 8)); + } + @Test public void Basic2() { + Object[] src = new Object[] {"0","8","1","7","2","6","3","5","4"}; + List_adp_sorter.new_().Sort(src, src.length); + Tfds.Eq_ary(src, new Object[] {"0","1","2","3","4","5","6","7","8"}); + } + Object[] Sequential(int bgn, int end) { + Object[] rv = new Object[end - bgn + 1]; + for (int i = 0; i < Array_.Len(rv); i++) + rv[i] = i + bgn; + return rv; + } +} diff --git a/100_core/src/gplx/core/lists/StackAdp.java b/100_core/src/gplx/core/lists/StackAdp.java new file mode 100644 index 000000000..318ac6272 --- /dev/null +++ b/100_core/src/gplx/core/lists/StackAdp.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public interface StackAdp extends EnumerAble { + int Count(); + void Clear(); + void Push(Object obj); + Object Pop(); + Object Peek(); + List_adp XtoList(); +} diff --git a/100_core/src/gplx/core/lists/StackAdp_.java b/100_core/src/gplx/core/lists/StackAdp_.java new file mode 100644 index 000000000..eef2f7056 --- /dev/null +++ b/100_core/src/gplx/core/lists/StackAdp_.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +public class StackAdp_ { + public static StackAdp new_() {return new StackAdp_base();} +} +class StackAdp_base implements StackAdp { + public Object Peek() {return Peek_base();} + public Object Pop() {return Pop_base();} + public void Push(Object obj) {Push_base(obj);} + public List_adp XtoList() { + List_adp list = List_adp_.New(); + for (Object obj : stack) + list.Add(obj); + // NOTE: dotnet traverses last to first; java: first to last + return list; + } + final java.util.Stack stack = new java.util.Stack(); + public StackAdp_base() {} + public int Count() {return stack.size();} + public void Clear() {stack.clear();} + protected void Push_base(Object obj) {stack.push(obj);} + protected Object Pop_base() {return stack.pop();} + protected Object Peek_base() {return stack.peek();} + public java.util.Iterator iterator() {return stack.iterator();} +} diff --git a/100_core/src/gplx/core/lists/StackAdp_tst.java b/100_core/src/gplx/core/lists/StackAdp_tst.java new file mode 100644 index 000000000..065cf3c39 --- /dev/null +++ b/100_core/src/gplx/core/lists/StackAdp_tst.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +import org.junit.*; +public class StackAdp_tst { + @Test public void XtoList() { + tst_XtoList(1, 2, 3); + } + void tst_XtoList(int... ary) { + StackAdp stack = StackAdp_.new_(); + for (int i : ary) + stack.Push(i); + List_adp list = stack.XtoList(); + int[] actl = (int[])list.To_ary(int.class); + for (int i = 0; i < ary.length; i++) + Tfds.Eq(ary[i], actl[i]); + } +} diff --git a/100_core/src/gplx/core/lists/rings/Ring__long.java b/100_core/src/gplx/core/lists/rings/Ring__long.java new file mode 100644 index 000000000..733ba09b9 --- /dev/null +++ b/100_core/src/gplx/core/lists/rings/Ring__long.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists.rings; import gplx.*; import gplx.core.*; import gplx.core.lists.*; +public class Ring__long { + private final long[] ary; + private final int max; + private int nxt, idx_0; + public Ring__long(int max) { + this.max = max; + this.ary = new long[max]; + } + public int Len() {return len;} private int len; + public void Clear() { + for (int i = 0; i < max; ++i) + ary[i] = 0; + len = nxt = 0; + idx_0 = 0; + } + public long Get_at(int i) { + int idx = idx_0 + i; + if (idx >= max) idx -= max; + return ary[idx]; + } + public void Add(long val) { + ary[nxt] = val; // set ary idx + if (++nxt == max) // increment nxt; if max... + nxt = 0; // ...set to 0; + if (len == max) // set idx_0 + idx_0 = nxt == 0 ? 0 : nxt; + if (len < max) // increment len unless already at max + ++len; + } + public long[] To_ary() { + long[] rv = new long[len]; + for (int i = 0; i < len; ++i) + rv[i] = Get_at(i); + return rv; + } +} diff --git a/100_core/src/gplx/core/lists/rings/Ring__long__tst.java b/100_core/src/gplx/core/lists/rings/Ring__long__tst.java new file mode 100644 index 000000000..9c676a120 --- /dev/null +++ b/100_core/src/gplx/core/lists/rings/Ring__long__tst.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists.rings; import gplx.*; import gplx.core.*; import gplx.core.lists.*; +import org.junit.*; import gplx.core.tests.*; +public class Ring__long__tst { + private final Ring__long__fxt fxt = new Ring__long__fxt(); + @Test public void Basic__1() {fxt.Clear().Add(1) .Test__to_ary(1);} + @Test public void Basic__2() {fxt.Clear().Add(1, 2) .Test__to_ary(1, 2);} + @Test public void Basic__3() {fxt.Clear().Add(1, 2, 3) .Test__to_ary(1, 2, 3);} + @Test public void Wrap__1() {fxt.Clear().Add(1, 2, 3, 4) .Test__to_ary(2, 3, 4);} + @Test public void Wrap__2() {fxt.Clear().Add(1, 2, 3, 4, 5) .Test__to_ary(3, 4, 5);} + @Test public void Wrap__3() {fxt.Clear().Add(1, 2, 3, 4, 5, 6) .Test__to_ary(4, 5, 6);} +} +class Ring__long__fxt { + private Ring__long ring = new Ring__long(3); + public Ring__long__fxt Clear() {ring.Clear(); return this;} + public Ring__long__fxt Add(long... ary) { + for (long itm : ary) + ring.Add(itm); + return this; + } + public Ring__long__fxt Test__to_ary(long... expd) { + Gftest.Eq__ary(expd, ring.To_ary(), "to_ary"); + return this; + } +} diff --git a/100_core/src/gplx/core/lists/rings/Ring__string.java b/100_core/src/gplx/core/lists/rings/Ring__string.java new file mode 100644 index 000000000..dbe87cbb1 --- /dev/null +++ b/100_core/src/gplx/core/lists/rings/Ring__string.java @@ -0,0 +1,60 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists.rings; import gplx.*; import gplx.core.*; import gplx.core.lists.*; +public class Ring__string { + String[] ary = String_.Ary_empty; + public int Len() {return len;} int len; + public Ring__string Max_(int v) { + if (v != max) { + ary = new String[v]; + max = v; + } + return this; + } int max; + public void Clear() { + for (int i = 0; i < max; i++) { + ary[i] = null; + } + len = nxt = 0; + } + int nxt; + public void Push(String v) { + int idx = nxt++; + if (idx == max) { + idx = 0; + } + if (nxt == max) { + nxt = 0; + } + ary[idx] = v; + if (len < max) + ++len; + } + public String[] Xto_str_ary() { + String[] rv = new String[len]; + int ary_i = nxt - 1; + for (int rv_i = len - 1; rv_i > -1; rv_i--) { + if (ary_i == -1) { + ary_i = max - 1; + } + rv[rv_i] = ary[ary_i]; + --ary_i; + } + return rv; + } +} diff --git a/100_core/src/gplx/core/lists/rings/Ring__string__tst.java b/100_core/src/gplx/core/lists/rings/Ring__string__tst.java new file mode 100644 index 000000000..e573d0268 --- /dev/null +++ b/100_core/src/gplx/core/lists/rings/Ring__string__tst.java @@ -0,0 +1,46 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists.rings; import gplx.*; import gplx.core.*; import gplx.core.lists.*; +import org.junit.*; +public class Ring__string__tst { + private final Ring__string__fxt fxt = new Ring__string__fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Basic() { + fxt.Clear().Max_(3).Push_many("a") .Expd("a"); + fxt.Clear().Max_(3).Push_many("a", "b") .Expd("a", "b"); + fxt.Clear().Max_(3).Push_many("a", "b", "c") .Expd("a", "b", "c"); + fxt.Clear().Max_(3).Push_many("a", "b", "c", "d") .Expd("b", "c", "d"); + fxt.Clear().Max_(3).Push_many("a", "b", "c", "d", "e") .Expd("c", "d", "e"); + fxt.Clear().Max_(3).Push_many("a", "b", "c", "d", "e", "f") .Expd("d", "e", "f"); + } +} +class Ring__string__fxt { + Ring__string ring = new Ring__string(); + public Ring__string__fxt Clear() {ring.Clear(); return this;} + public Ring__string__fxt Max_(int v) {ring.Max_(v); return this;} + public Ring__string__fxt Push_many(String... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) + ring.Push(ary[i]); + return this; + } + public Ring__string__fxt Expd(String... expd) { + Tfds.Eq_ary_str(expd, ring.Xto_str_ary()); + return this; + } +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_data.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_data.java new file mode 100644 index 000000000..e76f0c124 --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_data.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +public class Gfo_msg_data { + public int Uid() {return uid;} int uid = uid_next++; + public Gfo_msg_itm Item() {return item;} Gfo_msg_itm item; + public Object[] Vals() {return vals;} Object[] vals; + public byte[] Src_bry() {return src_bry;} private byte[] src_bry; + public int Src_bgn() {return src_bgn;} int src_bgn; + public int Src_end() {return src_end;} int src_end; + public Gfo_msg_data Ctor_val_many(Gfo_msg_itm item, Object[] vals) {this.item = item; this.vals = vals; return this;} + public Gfo_msg_data Ctor_src_many(Gfo_msg_itm item, byte[] src_bry, int src_bgn, int src_end, Object[] vals) {this.item = item; this.src_bry = src_bry; this.src_bgn = src_bgn; this.src_end = src_end; this.vals = vals; return this;} + public void Clear() { + item = null; vals = null; src_bry = null; + } + public String Gen_str_ary() {return item.Gen_str_ary(vals);} + static int uid_next = 0; + public static final Gfo_msg_data[] Ary_empty = new Gfo_msg_data[0]; +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_grp.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_grp.java new file mode 100644 index 000000000..e72fc62f7 --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_grp.java @@ -0,0 +1,46 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +public class Gfo_msg_grp implements Gfo_msg_obj { + public Gfo_msg_grp(Gfo_msg_grp owner, int uid, byte[] key) { + this.owner = owner; this.uid = uid; this.key = key; this.key_str = String_.new_a7(key); + if (owner != null) { + owner.subs.Add(this); + path = Gfo_msg_grp_.Path(owner.path, key); + } + else + path = Bry_.Empty; + } + public void Subs_clear() {subs.Clear();} + public Gfo_msg_grp Owner() {return owner;} Gfo_msg_grp owner; + public int Uid() {return uid;} int uid; + public byte[] Key() {return key;} private byte[] key; + public String Key_str() {return key_str;} private String key_str; + public byte[] Path() {return path;} private byte[] path; + public String Path_str() {return String_.new_a7(path);} + public Gfo_msg_obj Subs_get_by_key(String sub_key) { + int subs_len = subs.Count(); + for (int i = 0; i < subs_len; i++) { + Gfo_msg_obj sub = (Gfo_msg_obj)subs.Get_at(i); + if (String_.Eq(sub_key, sub.Key_str())) return sub; + } + return null; + } + public void Subs_add(Gfo_msg_itm item) {subs.Add(item);} + List_adp subs = List_adp_.New(); +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_grp_.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_grp_.java new file mode 100644 index 000000000..c00f958dc --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_grp_.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +public class Gfo_msg_grp_ { + public static final Gfo_msg_grp Root_gplx = new Gfo_msg_grp(null, Gfo_msg_grp_.Uid_next(), Bry_.new_a7("gplx")); + public static final Gfo_msg_grp Root = new Gfo_msg_grp(null, Gfo_msg_grp_.Uid_next(), Bry_.Empty); + public static Gfo_msg_grp prj_(String key) {return new Gfo_msg_grp(Root , Gfo_msg_grp_.Uid_next(), Bry_.new_a7(key));} + public static Gfo_msg_grp new_(Gfo_msg_grp owner, String key) {return new Gfo_msg_grp(owner , Gfo_msg_grp_.Uid_next(), Bry_.new_a7(key));} + public static int Uid_next() {return uid_next++;} static int uid_next = 0; + public static byte[] Path(byte[] owner_path, byte[] key) { + if (owner_path != Bry_.Empty) tmp_bfr.Add(owner_path).Add_byte(Byte_ascii.Dot); // only add "." if owner_path is available; prevents creating ".gplx" + return tmp_bfr.Add(key).To_bry_and_clear(); + } + static Bry_bfr tmp_bfr = Bry_bfr_.Reset(256); +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_itm.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_itm.java new file mode 100644 index 000000000..b3a181af7 --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_itm.java @@ -0,0 +1,58 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +import gplx.core.brys.fmtrs.*; +public class Gfo_msg_itm implements Gfo_msg_obj { + public Gfo_msg_itm(Gfo_msg_grp owner, int uid, byte cmd, byte[] key_bry, byte[] fmt, boolean add_to_owner) { + this.owner = owner; this.uid = uid; this.cmd = cmd; this.key_bry = key_bry; this.fmt = fmt; + this.key_str = String_.new_a7(key_bry); + this.path_bry = Gfo_msg_grp_.Path(owner.Path(), key_bry); + if (add_to_owner) owner.Subs_add(this); + } + public Gfo_msg_grp Owner() {return owner;} Gfo_msg_grp owner; + public int Uid() {return uid;} int uid; + public byte[] Path_bry() {return path_bry;} private byte[] path_bry; + public String Path_str() {return String_.new_u8(path_bry);} + public byte[] Key_bry() {return key_bry;} private byte[] key_bry; + public String Key_str() {return key_str;} private String key_str; + public Gfo_msg_obj Subs_get_by_key(String sub_key) {return null;} + public byte Cmd() {return cmd;} private byte cmd; + public byte[] Fmt() {return fmt;} private byte[] fmt; + public Bry_fmtr Fmtr() {if (fmtr == null) fmtr = Bry_fmtr.new_bry_(fmt).Compile(); return fmtr;} Bry_fmtr fmtr; + public String Gen_str_many(Object... vals) {return Gen_str_ary(vals);} + public String Gen_str_ary(Object[] vals) { + if (fmtr == null) fmtr = Bry_fmtr.new_bry_(fmt).Compile(); + if (fmtr.Fmt_args_exist()) { + fmtr.Bld_bfr_many(tmp_bfr, vals); + return tmp_bfr.To_str_and_clear(); + } + else + return String_.new_u8(fmt); + } + public String Gen_str_one(Object val) { + if (fmtr == null) fmtr = Bry_fmtr.new_bry_(fmt).Compile(); + if (fmtr.Fmt_args_exist()) { + fmtr.Bld_bfr_one(tmp_bfr, val); + return tmp_bfr.To_str_and_clear(); + } + else + return String_.new_u8(fmt); + } + public String Gen_str_none() {return key_str;} + static Bry_bfr tmp_bfr = Bry_bfr_.Reset(255); +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_itm_.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_itm_.java new file mode 100644 index 000000000..29ffbbae6 --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_itm_.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +public class Gfo_msg_itm_ { + public static final byte Cmd_null = 0, Cmd_log = 1, Cmd_note = 2, Cmd_warn = 3, Cmd_stop = 4, Cmd_fail = 5; + public static final byte[][] CmdBry = new byte[][] {Object_.Bry__null, Bry_.new_a7("log"), Bry_.new_a7("note"), Bry_.new_a7("warn"), Bry_.new_a7("stop"), Bry_.new_a7("fail")}; + public static Gfo_msg_itm new_note_(Gfo_msg_grp owner, String key) {return new_(owner, Cmd_note, key, key);} + public static Gfo_msg_itm new_fail_(Gfo_msg_grp owner, String key, String fmt) {return new_(owner, Cmd_warn, key, fmt);} + public static Gfo_msg_itm new_warn_(Gfo_msg_grp owner, String key) {return new_(owner, Cmd_warn, key, key);} + public static Gfo_msg_itm new_warn_(Gfo_msg_grp owner, String key, String fmt) {return new_(owner, Cmd_warn, key, fmt);} + public static Gfo_msg_itm new_(Gfo_msg_grp owner, byte cmd, String key, String fmt) {return new Gfo_msg_itm(owner, Gfo_msg_grp_.Uid_next(), cmd, Bry_.new_a7(key), Bry_.new_a7(fmt), false);} +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_log.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_log.java new file mode 100644 index 000000000..143fbb5e7 --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_log.java @@ -0,0 +1,60 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +public class Gfo_msg_log { + public Gfo_msg_log(String root_key) {root = new Gfo_msg_root(root_key);} Gfo_msg_root root; + public int Ary_len() {return ary_idx;} + public Gfo_msg_data Ary_get(int i) {return ary[i];} + public Gfo_msg_log Clear() { + synchronized (this) { // TS: DATE:2016-07-06 + for (int i = 0; i < ary_idx; i++) + ary[i].Clear(); + ary_idx = 0; + return this; + } + } + public Gfo_msg_log Add_str_warn_key_none(String grp, String itm, byte[] src, int pos) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, null, src, pos, pos + 1, null);} + public Gfo_msg_log Add_str_warn_key_none(String grp, String itm, byte[] src, int bgn, int end) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, null, src, bgn, end, null);} + public Gfo_msg_log Add_str_warn_fmt_none(String grp, String itm, String fmt) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , Bry_.Empty, -1, -1, null);} + public Gfo_msg_log Add_str_warn_fmt_none(String grp, String itm, String fmt, byte[] src, int pos) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , src, pos, pos + 1, null);} + public Gfo_msg_log Add_str_warn_fmt_none(String grp, String itm, String fmt, byte[] src, int bgn, int end) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , src, bgn, end, null);} + public Gfo_msg_log Add_str_warn_fmt_many(String grp, String itm, String fmt, Object... vals) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , Bry_.Empty, -1, -1, vals);} + Gfo_msg_log Add_str(byte cmd, String owner_key, String itm, String fmt, byte[] src, int bgn, int end, Object[] vals) { + synchronized (this) { // TS: DATE:2016-07-06 + if (ary_idx >= ary_max) ary_expand(); + ary[ary_idx++] = root.Data_new_many(cmd, src, bgn, end, owner_key, itm, fmt, vals); + return this; + } + } + public Gfo_msg_log Add_itm_none(Gfo_msg_itm itm, byte[] src, int bgn, int end) {return Add_itm(itm, src, bgn, end, null);} + public Gfo_msg_log Add_itm_many(Gfo_msg_itm itm, byte[] src, int bgn, int end, Object... val_ary) {return Add_itm(itm, src, bgn, end, val_ary);} + Gfo_msg_log Add_itm(Gfo_msg_itm itm, byte[] src, int bgn, int end, Object[] vals) { + synchronized (this) { // TS: DATE:2016-07-06 + if (ary_idx >= ary_max) ary_expand(); + ary[ary_idx++] = root.Data_new_many(itm, src, bgn, end, vals); + return this; + } + } + void ary_expand() { + int new_max = ary_max == 0 ? 2 : ary_max * 2; + ary = (Gfo_msg_data[])Array_.Expand(ary, new Gfo_msg_data[new_max], ary_max); + ary_max = new_max; + } + Gfo_msg_data[] ary = Gfo_msg_data.Ary_empty; int ary_idx, ary_max; + public static Gfo_msg_log Test() {return new Gfo_msg_log("test");} +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_obj.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_obj.java new file mode 100644 index 000000000..d229de941 --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_obj.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +public interface Gfo_msg_obj { + String Key_str(); + Gfo_msg_obj Subs_get_by_key(String sub_key); +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_root.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_root.java new file mode 100644 index 000000000..3248fedea --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_root.java @@ -0,0 +1,80 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +public class Gfo_msg_root { + public Gfo_msg_root(String root_key) { + this.root_key = root_key; + this.root = Gfo_msg_grp_.new_(Gfo_msg_grp_.Root, root_key); + } String root_key; + public void Data_ary_clear() { + for (int i = 0; i < data_ary_idx; i++) + data_ary[i].Clear(); + data_ary_idx = 0; + } + public void Data_ary_len_(int v) { + data_ary_len = v; + data_ary = new Gfo_msg_data[data_ary_len]; + for (int i = 0; i < data_ary_len; i++) + data_ary[i] = new Gfo_msg_data(); + data_ary_idx = 0; + } int data_ary_len; int data_ary_idx; Gfo_msg_data[] data_ary; + public void Reset() { + root.Subs_clear(); + owners.Clear(); + uid_list_next = uid_item_next = 0; + Data_ary_clear(); + } + public Gfo_msg_data Data_new_note_many(String owner_key, String key, String fmt, Object... vals) {return Data_new_many(Gfo_msg_itm_.Cmd_note, Bry_.Empty, -1, -1, owner_key, key, fmt, vals);} + public Gfo_msg_data Data_new_many(byte cmd, String owner_key, String key, String fmt, Object[] vals) {return Data_new_many(cmd, Bry_.Empty, -1, -1, owner_key, key, fmt, vals);} + public Gfo_msg_data Data_new_many(byte cmd, byte[] src, int bgn, int end, String owner_key, String key, String fmt, Object[] vals) { + Object owner_obj = owners.Get_by(owner_key); + Gfo_msg_grp owner = null; + if (owner_obj == null) { + owner = New_list_by_key(owner_key); + owners.Add(owner_key, owner); + } + else + owner = (Gfo_msg_grp)owner_obj; + Gfo_msg_itm itm = (Gfo_msg_itm)owner.Subs_get_by_key(key); + if (itm == null) + itm = new Gfo_msg_itm(owner, uid_item_next++, cmd, Bry_.new_u8(key), fmt == null ? Bry_.Empty : Bry_.new_a7(fmt), false); + return Data_new_many(itm, src, bgn, end, vals); + } + public Gfo_msg_data Data_new_many(Gfo_msg_itm itm, byte[] src, int bgn, int end, Object... vals) {return Data_get().Ctor_src_many(itm, src, bgn, end, vals);} + public Gfo_msg_data Data_get() { + return data_ary_idx < data_ary_len ? data_ary[data_ary_idx++] : new Gfo_msg_data(); + } + Gfo_msg_grp New_list_by_key(String key) { + String[] segs = String_.Split(key, '.'); + int segs_len = segs.length; int segs_last = segs_len - 1; + Gfo_msg_grp cur_list = root; + for (int i = 0; i < segs_last; i++) { + String seg = segs[i]; + Gfo_msg_grp sub_list = (Gfo_msg_grp)cur_list.Subs_get_by_key(seg); + if (sub_list == null) + sub_list = new Gfo_msg_grp(cur_list, uid_list_next++, Bry_.new_a7(key)); + cur_list = sub_list; + } + return cur_list; + } + Gfo_msg_grp root; + Ordered_hash owners = Ordered_hash_.New(); + int uid_list_next = 0; + int uid_item_next = 0; + public static final Gfo_msg_root Instance = new Gfo_msg_root("gplx"); +} diff --git a/100_core/src/gplx/core/log_msgs/Gfo_msg_root_tst.java b/100_core/src/gplx/core/log_msgs/Gfo_msg_root_tst.java new file mode 100644 index 000000000..29833b6eb --- /dev/null +++ b/100_core/src/gplx/core/log_msgs/Gfo_msg_root_tst.java @@ -0,0 +1,62 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.log_msgs; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Gfo_msg_root_tst { + Gfo_msg_root_fxt fxt = new Gfo_msg_root_fxt(); + @Before public void setup() {fxt.Reset();} + @Test public void Str() { + fxt.Clear().Expd_data_str_("failed a0 b0").Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a0", "b0"); + fxt.Clear().Expd_data_str_("failed a1 b1").Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a1", "b1"); + } +// @Test public void Item() { // DISABLED: no longer registering items with owner; +// fxt.Clear().Expd_item_uid_(0).Expd_item_fmtr_arg_exists_(Bool_.Y).Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a0", "b0"); +// fxt.Clear().Expd_item_uid_(1).Expd_item_fmtr_arg_exists_(Bool_.N).Tst_data_new_many("proj.cls.proc", "err_1", "failed"); +// fxt.Clear().Expd_item_uid_(0).Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a0", "b0"); // make sure item_uid stays the same +// } + @Test public void Cache() { + fxt.Mgr().Data_ary_len_(2); + fxt.Clear().Expd_data_uid_(0).Tst_data_new_many("x", "err_0", "a"); + fxt.Clear().Expd_data_uid_(1).Tst_data_new_many("x", "err_0", "b"); + fxt.Clear().Expd_data_uid_(2).Tst_data_new_many("x", "err_0", "a"); + fxt.Mgr().Data_ary_clear(); + fxt.Clear().Expd_data_uid_(0).Tst_data_new_many("x", "err_0", "a"); + } +} +class Gfo_msg_root_fxt { + Gfo_msg_root root = new Gfo_msg_root("tst"); + public Gfo_msg_root_fxt Reset() {root.Reset(); this.Clear(); return this;} + public Gfo_msg_root_fxt Clear() { + expd_item_uid = -1; + expd_item_fmtr_arg_exists = Bool_.__byte; + expd_data_uid = -1; + expd_data_str = null; + return this; + } + public Gfo_msg_root Mgr() {return root;} + public Gfo_msg_root_fxt Expd_data_uid_(int v) {this.expd_data_uid = v; return this;} int expd_data_uid; + public Gfo_msg_root_fxt Expd_data_str_(String v) {this.expd_data_str = v; return this;} private String expd_data_str; + public Gfo_msg_root_fxt Expd_item_uid_(int v) {this.expd_item_uid = v; return this;} int expd_item_uid; + public Gfo_msg_root_fxt Expd_item_fmtr_arg_exists_(boolean v) {this.expd_item_fmtr_arg_exists = v ? Bool_.Y_byte : Bool_.N_byte; return this;} private byte expd_item_fmtr_arg_exists; + public void Tst_data_new_many(String path, String key, String fmt, Object... vals) { + Gfo_msg_data data = root.Data_new_many(Gfo_msg_itm_.Cmd_note, path, key, fmt, vals); + if (expd_item_uid != -1) Tfds.Eq(expd_item_uid, data.Item().Uid());; + if (expd_item_fmtr_arg_exists != Bool_.__byte) Tfds.Eq(Bool_.By_int(expd_item_fmtr_arg_exists), data.Item().Fmtr().Fmt_args_exist()); + if (expd_data_str != null) Tfds.Eq(expd_data_str, data.Item().Gen_str_many(data.Vals())); + } +} diff --git a/100_core/src/gplx/core/logs/Gfo_log__base.java b/100_core/src/gplx/core/logs/Gfo_log__base.java new file mode 100644 index 000000000..6f335e7b0 --- /dev/null +++ b/100_core/src/gplx/core/logs/Gfo_log__base.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.logs; import gplx.*; import gplx.core.*; +public abstract class Gfo_log__base implements Gfo_log { + private long time_prv = 0; + public void Warn(String msg, Object... args) { + long time = gplx.core.envs.System_.Ticks(); + long elapsed = time_prv == 0 ? 0 : time - time_prv; + Exec(Gfo_log_itm.Type__warn, time, elapsed, msg, args); + } + public void Note(String msg, Object... args) { + long time = gplx.core.envs.System_.Ticks(); + long elapsed = time_prv == 0 ? 0 : time - time_prv; + Exec(Gfo_log_itm.Type__note, time, elapsed, msg, args); + } + public void Info(String msg, Object... args) { + long time = gplx.core.envs.System_.Ticks(); + long elapsed = time_prv == 0 ? 0 : time - time_prv; + Exec(Gfo_log_itm.Type__info, time, elapsed, msg, args); + } + public void Prog(String msg, Object... args) { + long time = gplx.core.envs.System_.Ticks(); + long elapsed = time_prv == 0 ? 0 : time - time_prv; + Exec(Gfo_log_itm.Type__prog, time, elapsed, msg, args); + } + public abstract List_adp Itms(); public abstract Gfo_log Itms_(List_adp v); + public abstract void Exec(byte type, long time, long elapsed, String msg, Object[] args); + public abstract void Flush(); +} diff --git a/100_core/src/gplx/core/logs/Gfo_log__file.java b/100_core/src/gplx/core/logs/Gfo_log__file.java new file mode 100644 index 000000000..8720d89fd --- /dev/null +++ b/100_core/src/gplx/core/logs/Gfo_log__file.java @@ -0,0 +1,63 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.logs; import gplx.*; import gplx.core.*; +public class Gfo_log__file extends Gfo_log__base { + public final Gfo_log_itm_wtr fmtr; + private final Bry_bfr bfr = Bry_bfr_.New(); + public Gfo_log__file(Io_url url, Gfo_log_itm_wtr fmtr) { + this.url = url; this.fmtr = fmtr; + } + public Io_url Url() {return url;} private final Io_url url; + @Override public List_adp Itms() {return itms;} @Override public Gfo_log Itms_(List_adp v) {this.itms = v; return this;} private List_adp itms; + @Override public void Exec(byte type, long time, long elapsed, String msg, Object[] args) { + if (type == Gfo_log_itm.Type__prog) return; + + // add itm + Gfo_log_itm itm = new Gfo_log_itm(type, time, elapsed, msg, args); + itms.Add(itm); + + // flush if warning or failure; needed for download central + switch (type) { + case Gfo_log_itm.Type__note: + case Gfo_log_itm.Type__warn: + case Gfo_log_itm.Type__fail: this.Flush(); break; + } + } + @Override public void Flush() { + int len = itms.Len(); + for (int i = 0; i < len; ++i) { + Gfo_log_itm itm = (Gfo_log_itm)itms.Get_at(i); + fmtr.Write(bfr, itm); + } + byte[] bry = bfr.To_bry_and_clear(); if (bry.length == 0) return; // don't bother writing empty bfr; happens during Xolog.Delete + Io_mgr.Instance.AppendFilByt(url, bry); + itms.Clear(); + } + public static void Delete_old_files(Io_url dir, Gfo_log log) { + Io_url[] fils = Io_mgr.Instance.QueryDir_fils(dir); + int fils_len = fils.length; + if (fils_len < 9) return; // exit if less than 8 files + int cutoff = fils_len - 8; + Array_.Sort(fils); // sort by path + for (int i = 0; i < cutoff; ++i) { + Io_url fil = fils[i]; + log.Info("deleting old log file", "file", fil.Raw()); + Io_mgr.Instance.DeleteFil(fil); + } + } +} diff --git a/100_core/src/gplx/core/logs/Gfo_log__mem.java b/100_core/src/gplx/core/logs/Gfo_log__mem.java new file mode 100644 index 000000000..bd95db3c3 --- /dev/null +++ b/100_core/src/gplx/core/logs/Gfo_log__mem.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.logs; import gplx.*; import gplx.core.*; +public class Gfo_log__mem extends Gfo_log__base { + @Override public List_adp Itms() {return itms;} @Override public Gfo_log Itms_(List_adp v) {this.itms = v; return this;} private List_adp itms = List_adp_.New(); + @Override public void Exec(byte type, long time, long elapsed, String msg, Object[] args) { + Gfo_log_itm itm = new Gfo_log_itm(type, time, elapsed, msg, args); + itms.Add(itm); + } + @Override public void Flush() {} +} diff --git a/100_core/src/gplx/core/logs/Gfo_log_itm.java b/100_core/src/gplx/core/logs/Gfo_log_itm.java new file mode 100644 index 000000000..426db39c5 --- /dev/null +++ b/100_core/src/gplx/core/logs/Gfo_log_itm.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.logs; import gplx.*; import gplx.core.*; +public class Gfo_log_itm { + public Gfo_log_itm(byte type, long time, long elapsed, String msg, Object[] args) { + this.Type = type; + this.Time = time; + this.Elapsed = elapsed; + this.Msg = msg; + this.Args = args; + } + public final byte Type; + public final long Time; + public final long Elapsed; + public final String Msg; + public final Object[] Args; + + public static final byte Type__fail = 0, Type__warn = 1, Type__note = 2, Type__info = 3, Type__prog = 4; +} diff --git a/100_core/src/gplx/core/logs/Gfo_log_itm_wtr.java b/100_core/src/gplx/core/logs/Gfo_log_itm_wtr.java new file mode 100644 index 000000000..8b2818ddf --- /dev/null +++ b/100_core/src/gplx/core/logs/Gfo_log_itm_wtr.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.logs; import gplx.*; import gplx.core.*; +public interface Gfo_log_itm_wtr { + void Write(Bry_bfr bfr, Gfo_log_itm itm); +} diff --git a/100_core/src/gplx/core/logs/Gfo_log_itm_wtr__csv.java b/100_core/src/gplx/core/logs/Gfo_log_itm_wtr__csv.java new file mode 100644 index 000000000..98b969461 --- /dev/null +++ b/100_core/src/gplx/core/logs/Gfo_log_itm_wtr__csv.java @@ -0,0 +1,70 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.logs; import gplx.*; import gplx.core.*; +public class Gfo_log_itm_wtr__csv implements Gfo_log_itm_wtr { + private static final byte[] Type__info = Bry_.new_a7("INFO"), Type__note = Bry_.new_a7("NOTE"), Type__warn = Bry_.new_a7("WARN"); + private String time_fmt = "yyyyMMdd_HHmmss.fff"; + public void Write(Bry_bfr bfr, Gfo_log_itm itm) { + bfr.Add_str_a7(Int_.To_str_pad_bgn_space((int)itm.Elapsed, 6)).Add_byte_pipe(); + bfr.Add_str_a7(DateAdp_.unixtime_utc_ms_(itm.Time).XtoStr_fmt(time_fmt)).Add_byte_pipe(); + byte[] type = null; + switch (itm.Type) { + case Gfo_log_itm.Type__info: type = Type__info; break; + case Gfo_log_itm.Type__note: type = Type__note; break; + case Gfo_log_itm.Type__warn: type = Type__warn; break; + } + bfr.Add(type).Add_byte_pipe(); + Escape_str(bfr, itm.Msg); bfr.Add_byte_pipe(); + Object[] args = itm.Args; + int args_len = args.length; + for (int i = 0; i < args_len; i += 2) { + Object key = args[i]; + int val_idx = i + 1; + Object val = i < val_idx ? args[val_idx] : "<<>>"; + Escape_str(bfr, Object_.Xto_str_strict_or_null_mark(key)); bfr.Add_byte_eq(); + Escape_str(bfr, Object_.Xto_str_strict_or_null_mark(val)); bfr.Add_byte_pipe(); + } + bfr.Add_byte_nl(); + } + private void Escape_str(Bry_bfr bfr, String str) { + byte[] bry = Bry_.new_u8(str); + int len = bry.length; + boolean dirty = false; + for (int i = 0; i < len; ++i) { + byte b = bry[i]; + byte escape_byte = Byte_ascii.Null; + switch (b) { + case Byte_ascii.Pipe: escape_byte = Byte_ascii.Ltr_p; break; + case Byte_ascii.Nl: escape_byte = Byte_ascii.Ltr_n; break; + case Byte_ascii.Tick: escape_byte = Byte_ascii.Tick; break; + default: + if (dirty) + bfr.Add_byte(b); + break; + } + if (escape_byte != Byte_ascii.Null) { + if (!dirty) { + dirty = true; + bfr.Add_mid(bry, 0, i); + } + bfr.Add_byte(Byte_ascii.Tick).Add_byte(escape_byte); + } + } + if (!dirty) bfr.Add(bry); + } +} diff --git a/100_core/src/gplx/core/memorys/Gfo_memory_itm.java b/100_core/src/gplx/core/memorys/Gfo_memory_itm.java new file mode 100644 index 000000000..6d0b83df5 --- /dev/null +++ b/100_core/src/gplx/core/memorys/Gfo_memory_itm.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.memorys; import gplx.*; import gplx.core.*; +public interface Gfo_memory_itm { + void Rls_mem(); +} diff --git a/100_core/src/gplx/core/memorys/Gfo_memory_mgr.java b/100_core/src/gplx/core/memorys/Gfo_memory_mgr.java new file mode 100644 index 000000000..cd6ddda22 --- /dev/null +++ b/100_core/src/gplx/core/memorys/Gfo_memory_mgr.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.memorys; import gplx.*; import gplx.core.*; +public class Gfo_memory_mgr { + private final List_adp list = List_adp_.New(); + public void Reg_safe(Gfo_memory_itm itm) {synchronized (list) {Reg_fast(itm);}} + public void Reg_fast(Gfo_memory_itm itm) {list.Add(itm);} + public void Rls_safe() {synchronized (list) {Rls_fast();}} + public void Rls_fast() { + int len = list.Len(); + for (int i = 0; i < len; ++i) { + Gfo_memory_itm itm = (Gfo_memory_itm)list.Get_at(i); + itm.Rls_mem(); + } + list.Clear(); + } + public static final Gfo_memory_mgr Instance = new Gfo_memory_mgr(); Gfo_memory_mgr() {} +} diff --git a/100_core/src/gplx/core/primitives/Bool_obj_ref.java b/100_core/src/gplx/core/primitives/Bool_obj_ref.java new file mode 100644 index 000000000..8bdaecead --- /dev/null +++ b/100_core/src/gplx/core/primitives/Bool_obj_ref.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Bool_obj_ref { + public boolean Val() {return val;} private boolean val; + public boolean Val_y() {return val;} + public boolean Val_n() {return !val;} + public String Val_as_str_yn() {return Yn.To_str(val);} + public Bool_obj_ref Val_y_() {val = true; return this;} + public Bool_obj_ref Val_n_() {val = false; return this;} + public Bool_obj_ref Val_(boolean v) {val = v; return this;} + public Bool_obj_ref Val_toggle_() {val = !val; return this;} + @Override public String toString() {return Bool_.To_str_lower(val);} + public static Bool_obj_ref n_() {return new_(false);} + public static Bool_obj_ref y_() {return new_(true);} + public static Bool_obj_ref new_(boolean val) { + Bool_obj_ref rv = new Bool_obj_ref(); + rv.val = val; + return rv; + } Bool_obj_ref() {} +} diff --git a/100_core/src/gplx/core/primitives/Bool_obj_val.java b/100_core/src/gplx/core/primitives/Bool_obj_val.java new file mode 100644 index 000000000..648a36687 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Bool_obj_val.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Bool_obj_val { + Bool_obj_val(int v) {val = v;} private final int val; + public boolean Val() {return val == 1;} + public static final Bool_obj_val + Null = new Bool_obj_val(-1) + , False = new Bool_obj_val(0) + , True = new Bool_obj_val(1) + ; + public static Bool_obj_val read_(Object o) {String s = String_.as_(o); return s == null ? (Bool_obj_val)o : parse(s);} + public static Bool_obj_val parse(String raw) { + if (String_.Eq(raw, "y")) return Bool_obj_val.True; + else if (String_.Eq(raw, "n")) return Bool_obj_val.False; + else if (String_.Eq(raw, "")) return Bool_obj_val.Null; + else throw Err_.new_parse_type(Bool_obj_val.class, raw); + } +} diff --git a/100_core/src/gplx/core/primitives/Bry_obj_ref.java b/100_core/src/gplx/core/primitives/Bry_obj_ref.java new file mode 100644 index 000000000..82aa32963 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Bry_obj_ref.java @@ -0,0 +1,45 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +import gplx.core.brys.*; +public class Bry_obj_ref implements gplx.core.brys.Bfr_arg { + public byte[] Val() {return val;} private byte[] val; + public int Val_bgn() {return val_bgn;} private int val_bgn; + public int Val_end() {return val_end;} private int val_end; + public boolean Val_is_empty() {return val_bgn == val_end;} + public Bry_obj_ref Val_(byte[] val) {this.val = val; this.val_bgn = 0; this.val_end = val == null ? 0 : val.length; return this;} + public Bry_obj_ref Mid_(byte[] val, int val_bgn, int val_end) {this.val = val; this.val_bgn = val_bgn; this.val_end = val_end; return this;} + @Override public int hashCode() {return CalcHashCode(val, val_bgn, val_end);} + @Override public boolean equals(Object obj) { + if (obj == null) return false; // NOTE: strange, but null check needed; throws null error; EX.WP: File:Eug�ne Delacroix - La libert� guidant le peuple.jpg + Bry_obj_ref comp = (Bry_obj_ref)obj; + return Bry_.Match(val, val_bgn, val_end, comp.val, comp.val_bgn, comp.val_end); + } + public void Bfr_arg__add(Bry_bfr bfr) { + bfr.Add_mid(val, val_bgn, val_end); + } + public static int CalcHashCode(byte[] ary, int bgn, int end) { + int rv = 0; + for (int i = bgn; i < end; i++) + rv = (31 * rv) + ary[i]; + return rv; + } + public static Bry_obj_ref New_empty() {return New(Bry_.Empty);} + public static Bry_obj_ref New(byte[] val) {return new Bry_obj_ref().Val_(val);} + public static Bry_obj_ref New(String val) {return new Bry_obj_ref().Val_(Bry_.new_u8(val));} +} diff --git a/100_core/src/gplx/core/primitives/Byte_obj_ref.java b/100_core/src/gplx/core/primitives/Byte_obj_ref.java new file mode 100644 index 000000000..437feabad --- /dev/null +++ b/100_core/src/gplx/core/primitives/Byte_obj_ref.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Byte_obj_ref { + public byte Val() {return val;} private byte val; + public Byte_obj_ref Val_(byte v) {val = v; return this;} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return obj == null ? false : val == ((Byte_obj_ref)obj).Val();} + @Override public String toString() {return Int_.To_str(val);} + public static Byte_obj_ref zero_() {return new_(Byte_.Zero);} + public static Byte_obj_ref new_(byte val) { + Byte_obj_ref rv = new Byte_obj_ref(); + rv.val = val; + return rv; + } private Byte_obj_ref() {} +} diff --git a/100_core/src/gplx/core/primitives/Byte_obj_val.java b/100_core/src/gplx/core/primitives/Byte_obj_val.java new file mode 100644 index 000000000..939626073 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Byte_obj_val.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Byte_obj_val { + public byte Val() {return val;} private byte val; + @Override public String toString() {return Int_.To_str(val);} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return obj == null ? false : val == ((Byte_obj_val)obj).Val();} + public static Byte_obj_val new_(byte val) { + Byte_obj_val rv = new Byte_obj_val(); + rv.val = val; + return rv; + } private Byte_obj_val() {} +} diff --git a/100_core/src/gplx/core/primitives/Double_obj_val.java b/100_core/src/gplx/core/primitives/Double_obj_val.java new file mode 100644 index 000000000..6a8414e16 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Double_obj_val.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Double_obj_val implements CompareAble { + public double Val() {return val;} double val; + @Override public String toString() {return Double_.To_str(val);} + @Override public int hashCode() {return (int)val;} + @Override public boolean equals(Object obj) {return obj == null ? false : val == ((Double_obj_val)obj).Val();} + public int compareTo(Object obj) {Double_obj_val comp = (Double_obj_val)obj; return Double_.Compare(val, comp.val);} + public static Double_obj_val neg1_() {return new_(-1);} + public static Double_obj_val zero_() {return new_(0);} + public static Double_obj_val new_(double val) { + Double_obj_val rv = new Double_obj_val(); + rv.val = val; + return rv; + } Double_obj_val() {} +} diff --git a/100_core/src/gplx/core/primitives/EnmMgr.java b/100_core/src/gplx/core/primitives/EnmMgr.java new file mode 100644 index 000000000..17599e918 --- /dev/null +++ b/100_core/src/gplx/core/primitives/EnmMgr.java @@ -0,0 +1,72 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; +public class EnmMgr { + public String BitRngSpr() {return bitRngSpr;} public EnmMgr BitRngSpr_(String val) {bitRngSpr = val; return this;} private String bitRngSpr = "+"; + public String Prefix() {return prefix;} public EnmMgr Prefix_(String val) {prefix = val; return this;} private String prefix; + public int BitRngBgn() {return bitRngBgn;} public EnmMgr BitRngBgn_(int val) {bitRngBgn = val; return this;} int bitRngBgn = 1; + public int BitRngEnd() {return bitRngEnd;} public EnmMgr BitRngEnd_(int val) {bitRngEnd = val; return this;} int bitRngEnd = Int_.Max_value; + public void RegObj(int val, String raw, Object o) { + rawRegy.Add(raw, val); + valRegy.Add(val, raw); + objRegy.Add(val, o); + } + public Object Get(int val) {return objRegy.Get_by(val);} + public int GetVal(String raw) { + String[] ary = String_.Split(raw, bitRngSpr); + int rv = 0; + for (int i = 0; i < ary.length; i++) { + String term = String_.Trim(ary[i]); // ex: key.ctrl + key.a + if (prefix != null) term = String_.Replace(term, prefix, ""); + int cur = -1; + if (String_.Has_at_bgn(term, "#")) + cur = Int_.parse(String_.Mid(term, 1)); + else + cur = Int_.cast(rawRegy.Get_by(term)); + rv |= cur; + } + return rv; + } + public String GetStr(int v) { + String_bldr sb = String_bldr_.new_(); + int cur = v, curModifier = bitRngBgn; + while (true) { + if (cur == 0 + || curModifier > bitRngEnd // loop until all Modifers have been shifted out + ) break; + if ((cur & curModifier) == curModifier) { // cur has Modifier + AppendRaw(sb, curModifier); + cur ^= curModifier; // shift Modifier out + } + curModifier *= 2; // move to next Modifier; relies on Shift, Ctrl, Alt enum values + } + if (cur > 0 // cur is non-Modifier; NOTE: check needed for args that are just a Modifier; + || sb.Count() == 0) // cur is IptKey.None; cur == 0, but sb.length will also be 0 + AppendRaw(sb, cur); + return sb.To_str(); + } + void AppendRaw(String_bldr sb, int key) { + String raw = (String)valRegy.Get_by(key); + if (sb.Count() > 0) sb.Add(bitRngSpr); + if (prefix != null) sb.Add(prefix); + sb.Add(raw); + } + Hash_adp rawRegy = Hash_adp_.New(), valRegy = Hash_adp_.New(), objRegy = Hash_adp_.New(); + public static EnmMgr new_() {return new EnmMgr();} EnmMgr() {} +} diff --git a/100_core/src/gplx/core/primitives/EnmParser_tst.java b/100_core/src/gplx/core/primitives/EnmParser_tst.java new file mode 100644 index 000000000..b210402a2 --- /dev/null +++ b/100_core/src/gplx/core/primitives/EnmParser_tst.java @@ -0,0 +1,60 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +import org.junit.*; +public class EnmParser_tst { + @Before public void setup() { + parser = EnmMgr.new_(); + } + @Test public void Basic() { // 1,2,4,8 + parser.BitRngEnd_(8); + run_Reg(0, "zero"); + run_Reg(1, "one"); + run_Reg(2, "two"); + run_Reg(4, "four"); + run_Reg(8, "eight"); + + tst_Convert("zero", 0); + tst_Convert("one", 1); + tst_Convert("eight", 8); + tst_Convert("one+eight", 9); + } + @Test public void Keys() { + parser.BitRngBgn_(65536).BitRngEnd_(262144); + run_Reg( 65, "a"); + run_Reg( 65536, "shift"); + run_Reg(131072, "ctrl"); + run_Reg(262144, "alt"); + tst_Convert("a", 65); + tst_Convert("shift+a", 65 + 65536); + tst_Convert("ctrl+a", 65 + 131072); + tst_Convert("shift+ctrl+a", 65 + 65536 + 131072); + } + @Test public void Prefix() { + parser.Prefix_("key.").BitRngBgn_(128).BitRngEnd_(128); + run_Reg(65, "a"); + tst_Convert("key.a", 65); + } + void run_Reg(int i, String s) {parser.RegObj(i, s, "NULL");} + void tst_Convert(String raw, int val) { + int actlVal = parser.GetVal(raw); + Tfds.Eq(val, actlVal); + Tfds.Eq(raw, parser.GetStr(val)); + } + EnmMgr parser; +} diff --git a/100_core/src/gplx/core/primitives/Int_ary_.java b/100_core/src/gplx/core/primitives/Int_ary_.java new file mode 100644 index 000000000..0f3af5094 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Int_ary_.java @@ -0,0 +1,95 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Int_ary_ { + public static int[] Parse_list_or(byte[] src, int[] or) { + try { + if (Bry_.Len_eq_0(src)) return or; // null, "" should return [0] + int raw_len = src.length; + int[] rv = null; int rv_idx = 0, rv_len = 0; + int pos = 0; + int num_bgn = -1, num_end = -1; + boolean itm_done = false, itm_is_rng = false; + int rng_bgn = Int_.Min_value; + while (true) { + boolean pos_is_last = pos == raw_len; + if ( itm_done + || pos_is_last + ) { + if (num_bgn == -1) return or; // empty itm; EX: "1,"; "1,,2" + int num = Bry_.To_int_or(src, num_bgn, num_end, Int_.Min_value); + if (num == Int_.Min_value) return or; // not a number; parse failed + if (rv_len == 0) { // rv not init'd + rv_len = (raw_len / 2) + 1; // default rv_len to len of String / 2; + 1 to avoid fraction rounding down + rv = new int[rv_len]; + } + int add_len = 1; + if (itm_is_rng) { + add_len = num - rng_bgn + List_adp_.Base1; + if (add_len == 0) return or; // bgn >= end; + } + if (add_len + rv_idx > rv_len) { // ary out of space; resize + rv_len = (add_len + rv_idx) * 2; + rv = (int[])Array_.Resize(rv, rv_len); + } + if (itm_is_rng) { + for (int i = rng_bgn; i <= num; i++) + rv[rv_idx++] = i; + } + else { + rv[rv_idx++] = num; + } + num_bgn = num_end = -1; + itm_done = itm_is_rng = false; + rng_bgn = Int_.Min_value; + if (pos_is_last) break; + } + byte b = src[pos]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + if (num_bgn == -1) // num_bgn not set + num_bgn = pos; + num_end = pos + 1; // num_end is always after pos; EX: "9": num_end = 1; "98,7": num_end=2 + break; + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: // NOTE: parseNumList replaces ws with '', so "1 1" will become "11" + break; + case Byte_ascii.Comma: + if (pos == raw_len -1) return or; // eos; EX: "1," + if (num_bgn == -1) return or; // empty itm; EX: ","; "1,,2" + itm_done = true; + break; + case Byte_ascii.Dash: + if (pos == raw_len -1) return or; // eos; EX: "1-" + if (num_bgn == -1) return or; // no rng_bgn; EX: "-2" + rng_bgn = Bry_.To_int_or(src, num_bgn, pos, Int_.Min_value); + if (rng_bgn == Int_.Min_value) return or; + num_bgn = -1; + itm_is_rng = true; + break; + default: + return or; + } + ++pos; + } + return (rv_idx == rv_len) // on the off-chance that rv_len == rv_idx; EX: "1" + ? rv + : (int[])Array_.Resize(rv, rv_idx); + } catch (Exception e) {Err_.Noop(e); return or;} + } +} diff --git a/100_core/src/gplx/core/primitives/Int_ary__tst.java b/100_core/src/gplx/core/primitives/Int_ary__tst.java new file mode 100644 index 000000000..426824681 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Int_ary__tst.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Int_ary__tst { + private Int_ary__fxt fxt = new Int_ary__fxt(); + @Test public void Parse_list_or_() { + fxt.Test_Parse_list_or("1", 1); + fxt.Test_Parse_list_or("123", 123); + fxt.Test_Parse_list_or("1,2,123", 1, 2, 123); + fxt.Test_Parse_list_or("1,2,12,123", 1, 2, 12, 123); + fxt.Test_Parse_list_or("1-5", 1, 2, 3, 4, 5); + fxt.Test_Parse_list_or("1-1", 1); + fxt.Test_Parse_list_or("1-3,7,11-13,21", 1, 2, 3, 7, 11, 12, 13, 21); + + fxt.Test_Parse_list_empty("1 2"); // NOTE: MW would gen 12; treat as invalid + fxt.Test_Parse_list_empty("1,"); // eos + fxt.Test_Parse_list_empty("1,,2"); // empty comma + fxt.Test_Parse_list_empty("1-"); // eos + fxt.Test_Parse_list_empty("3-1"); // bgn > end + fxt.Test_Parse_list_empty("1,a,2"); + fxt.Test_Parse_list_empty("a-1,2"); + fxt.Test_Parse_list_empty("-1"); // no rng bgn + } +} +class Int_ary__fxt { + public void Test_Parse_list_empty(String raw) {Tfds.Eq_ary(Int_.Ary_empty, Int_ary_.Parse_list_or(Bry_.new_a7(raw), Int_.Ary_empty));} + public void Test_Parse_list_or(String raw, int... expd) {Tfds.Eq_ary(expd, Int_ary_.Parse_list_or(Bry_.new_a7(raw), Int_.Ary_empty));} +} diff --git a/100_core/src/gplx/core/primitives/Int_list.java b/100_core/src/gplx/core/primitives/Int_list.java new file mode 100644 index 000000000..90108ac19 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Int_list.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Int_list { + private int[] ary = Int_.Ary_empty; private int ary_len, ary_max; + public void Add(int uid) { + int new_len = ary_len + 1; + if (new_len > ary_max) { + ary_max += 16; + int[] new_ary = new int[ary_max]; + Int_.Ary_copy_to(ary, ary_len, new_ary); + ary = new_ary; + } + ary[ary_len] = uid; + ary_len = new_len; + } + public int Len() {return ary_len;} + public int Get_at(int i) {return ary[i];} + public void Clear() { + ary = Int_.Ary_empty; + ary_len = ary_max = 0; + } + public static Int_list new_(int... ary) { + Int_list rv = new Int_list(); + int len = ary.length; + rv.ary = ary; rv.ary_len = len; rv.ary_max = len; + return rv; + } +} diff --git a/100_core/src/gplx/core/primitives/Int_obj_ref.java b/100_core/src/gplx/core/primitives/Int_obj_ref.java new file mode 100644 index 000000000..779b200b0 --- /dev/null +++ b/100_core/src/gplx/core/primitives/Int_obj_ref.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Int_obj_ref { + Int_obj_ref(int val) {this.val = val;} + public int Val() {return val;} public Int_obj_ref Val_(int v) {val = v; return this;} int val; + public int Val_add() {val++; return val;} + public int Val_add_post() {return val++;} + public int Val_add_pre() {return ++val;} + public int Val_add(int v) {val += v; return val;} + public Int_obj_ref Val_zero_() {val = 0; return this;} + public Int_obj_ref Val_neg1_() {val = -1; return this;} + public String Val_as_str() {return Int_.To_str(val);} + + @Override public String toString() {return Int_.To_str(val);} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return val == ((Int_obj_ref)obj).Val();} + + public static Int_obj_ref New_neg1() {return new Int_obj_ref(-1);} + public static Int_obj_ref New_zero() {return new Int_obj_ref(0);} + public static Int_obj_ref New(int val) {return new Int_obj_ref(val);} +} diff --git a/100_core/src/gplx/core/primitives/Int_obj_val.java b/100_core/src/gplx/core/primitives/Int_obj_val.java new file mode 100644 index 000000000..31f05828e --- /dev/null +++ b/100_core/src/gplx/core/primitives/Int_obj_val.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class Int_obj_val implements CompareAble { + public Int_obj_val(int val) {this.val = val;} + public int Val() {return val;} private final int val; + @Override public String toString() {return Int_.To_str(val);} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return obj == null ? false : val == ((Int_obj_val)obj).Val();} + public int compareTo(Object obj) {Int_obj_val comp = (Int_obj_val)obj; return Int_.Compare(val, comp.val);} +} diff --git a/100_core/src/gplx/core/primitives/String_obj_ref.java b/100_core/src/gplx/core/primitives/String_obj_ref.java new file mode 100644 index 000000000..fd4dacf13 --- /dev/null +++ b/100_core/src/gplx/core/primitives/String_obj_ref.java @@ -0,0 +1,30 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class String_obj_ref { + public String Val() {return val;} public String_obj_ref Val_(String v) {val = v; return this;} private String val; + public String_obj_ref Val_null_() {return Val_(null);} + @Override public String toString() {return val;} + public static String_obj_ref empty_() {return new_("");} + public static String_obj_ref null_() {return new_(null);} + public static String_obj_ref new_(String val) { + String_obj_ref rv = new String_obj_ref(); + rv.val = val; + return rv; + } String_obj_ref() {} +} diff --git a/100_core/src/gplx/core/primitives/String_obj_val.java b/100_core/src/gplx/core/primitives/String_obj_val.java new file mode 100644 index 000000000..e4356c8ed --- /dev/null +++ b/100_core/src/gplx/core/primitives/String_obj_val.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.primitives; import gplx.*; import gplx.core.*; +public class String_obj_val implements CompareAble { + public String Val() {return val;} private final String val; + @Override public String toString() {return val;} + public int compareTo(Object obj) { + String_obj_val comp = (String_obj_val)obj; + return String_.Compare(val, comp.val); + } + public static String_obj_val new_(String val) {return new String_obj_val(val);} String_obj_val(String val) {this.val = val;} +} diff --git a/100_core/src/gplx/core/progs/Gfo_prog_ui.java b/100_core/src/gplx/core/progs/Gfo_prog_ui.java new file mode 100644 index 000000000..5371d099e --- /dev/null +++ b/100_core/src/gplx/core/progs/Gfo_prog_ui.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.progs; import gplx.*; import gplx.core.*; +public interface Gfo_prog_ui extends Cancelable { + byte Prog_status(); + void Prog_status_(byte v); + long Prog_data_cur(); + long Prog_data_end(); + boolean Prog_notify_and_chk_if_suspended(long cur, long max); + void Prog_notify_by_msg(String msg); +} diff --git a/100_core/src/gplx/core/progs/Gfo_prog_ui_.java b/100_core/src/gplx/core/progs/Gfo_prog_ui_.java new file mode 100644 index 000000000..5721951f5 --- /dev/null +++ b/100_core/src/gplx/core/progs/Gfo_prog_ui_.java @@ -0,0 +1,49 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.progs; import gplx.*; import gplx.core.*; +public class Gfo_prog_ui_ { + public static final Gfo_prog_ui Noop = new Gfo_prog_ui__noop(), Always = new Gfo_prog_ui__always(); + public static final byte + Status__init = 1 + , Status__working = 2 + , Status__done = 4 + , Status__fail = 8 + , Status__suspended = 16 + , Status__runnable = Status__init | Status__suspended | Status__fail + ; +} +class Gfo_prog_ui__noop implements Gfo_prog_ui { + public boolean Canceled() {return true;} + public void Cancel() {} + public byte Prog_status() {return Gfo_prog_ui_.Status__init;} + public void Prog_status_(byte v) {} + public long Prog_data_cur() {return 0;} + public long Prog_data_end() {return 0;} + public boolean Prog_notify_and_chk_if_suspended(long cur, long max) {return false;} + public void Prog_notify_by_msg(String msg) {} +} +class Gfo_prog_ui__always implements Gfo_prog_ui { + public boolean Canceled() {return false;} + public void Cancel() {} + public byte Prog_status() {return Gfo_prog_ui_.Status__init;} + public void Prog_status_(byte v) {} + public long Prog_data_cur() {return 0;} + public long Prog_data_end() {return 0;} + public boolean Prog_notify_and_chk_if_suspended(long cur, long max) {return false;} + public void Prog_notify_by_msg(String msg) {} +} diff --git a/100_core/src/gplx/core/progs/Gfo_resume_wkr.java b/100_core/src/gplx/core/progs/Gfo_resume_wkr.java new file mode 100644 index 000000000..a13fc74df --- /dev/null +++ b/100_core/src/gplx/core/progs/Gfo_resume_wkr.java @@ -0,0 +1,53 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.progs; import gplx.*; import gplx.core.*; +public interface Gfo_resume_wkr { + boolean Resuming(); + long Get_long(byte tid); + void Set_long(byte tid, long v); +} +class Gfo_resume_wkr__download { + public boolean Resuming() {return resuming;} private boolean resuming = true; + public long Get_long(byte tid) {return val;} private long val; + public void Set_long(byte tid, long v) {val = v;} +} +class Gfo_resume_wkr__unzip { + public boolean Resuming() {return resuming;} private boolean resuming = true; + public long Get_long(byte tid) {return val;} private long val; + public void Set_long(byte tid, long v) {val = v;} + public String Get_str(byte tid) {return name;} private String name; + public void Set_str(byte tid, String v) {this.name = v;} +} +/* +[ + { "job_uid": + , "subs": + [ + { "download" + , "done": 0 + , "resume_bytes":123 + } + , { "unzip" + , "done": 0 + , "resume_file":abc + , "resume_bytes":123 + } + ] + } +] +*/ diff --git a/100_core/src/gplx/core/security/Hash_algo.java b/100_core/src/gplx/core/security/Hash_algo.java new file mode 100644 index 000000000..0e125cf73 --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +public interface Hash_algo { + String Key(); + byte[] Hash_bry_as_bry(byte[] src); + String Hash_bry_as_str(byte[] src); + String Hash_stream_as_str(gplx.core.consoles.Console_adp console, gplx.core.ios.streams.IoStream src_stream); + byte[] Hash_stream_as_bry(gplx.core.progs.Gfo_prog_ui prog_ui, gplx.core.ios.streams.IoStream src_stream); +} diff --git a/100_core/src/gplx/core/security/Hash_algo_.java b/100_core/src/gplx/core/security/Hash_algo_.java new file mode 100644 index 000000000..6bd4e4901 --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo_.java @@ -0,0 +1,147 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import gplx.core.consoles.*; import gplx.core.ios.streams.*; /*IoStream*/ +import gplx.core.texts.*; /*Base32Converter*/ import gplx.core.progs.*; +public class Hash_algo_ { + public static Hash_algo New__md5() {return new Hash_algo__md5();} + public static Hash_algo New__sha1() {return new Hash_algo__sha1();} + public static Hash_algo New__sha2_256() {return new Hash_algo__sha2_256();} + public static Hash_algo New__tth_192() {return new Hash_algo__tth_192();} + public static Hash_algo New_by_tid(byte tid) { + switch (tid) { + case Tid__md5: return New__md5(); + case Tid__sha1: return New__sha1(); + case Tid__sha2_256: return New__sha2_256(); + case Tid__tth_192: return New__tth_192(); + default: throw Err_.new_unhandled_default(tid); + } + } + public static Hash_algo New(String key) { + if (key == Hash_algo__md5.KEY) return New__md5(); + else if (key == Hash_algo__sha1.KEY) return New__sha1(); + else if (key == Hash_algo__sha2_256.KEY) return New__sha2_256(); + else if (key == Hash_algo__tth_192.KEY) return New__tth_192(); + else throw Err_.new_unhandled(key); + } + public static final byte Tid__md5 = 0, Tid__sha1 = 1, Tid__sha2_256 = 2, Tid__tth_192 = 3; +} +abstract class Hash_algo_base implements Hash_algo { + private final MessageDigest md; + private final byte[] trg_bry; + private byte[] tmp_bfr; private final int tmp_bfr_len = 4096; + public Hash_algo_base(MessageDigest md, int trg_bry_len) { + this.md = md; this.trg_bry = new byte[trg_bry_len]; + } + public String Hash_bry_as_str(byte[] src) {return String_.new_a7(Hash_bry_as_bry(src));} + public byte[] Hash_bry_as_bry(byte[] src) { + Hash_algo_utl_.Hash_bry(md, src, src.length, trg_bry); + return Bry_.Copy(trg_bry); // NOTE: must copy to return different instances to callers; else callers may hash same instance with different values + } + public String Hash_stream_as_str(Console_adp console, IoStream stream) {return String_.new_a7(Hash_stream_as_bry(console, stream));} + public byte[] Hash_stream_as_bry(Console_adp console, IoStream stream) { + if (tmp_bfr == null) tmp_bfr = new byte[4096]; + Hash_algo_utl_.Hash_stream(console, stream, md, tmp_bfr, tmp_bfr_len, trg_bry); + return trg_bry; + } + public byte[] Hash_stream_as_bry(Gfo_prog_ui prog_ui, IoStream stream) { + if (tmp_bfr == null) tmp_bfr = new byte[4096]; + Hash_algo_utl_.Hash_stream(prog_ui, stream, md, tmp_bfr, tmp_bfr_len, trg_bry); + return trg_bry; + } + protected static MessageDigest Get_message_digest(String key) { + try {return MessageDigest.getInstance(key);} + catch (NoSuchAlgorithmException e) {throw Err_.new_missing_key(key);} + } +} +class Hash_algo__md5 extends Hash_algo_base { + public Hash_algo__md5() {super(Get_message_digest_instance(), 32);} + public String Key() {return KEY;} public static final String KEY = "md5"; + + private static MessageDigest Get_message_digest_instance() { + if (md__md5 == null) + md__md5 = Get_message_digest(KEY); + return md__md5; + } private static MessageDigest md__md5; +} +class Hash_algo__sha1 extends Hash_algo_base { + public Hash_algo__sha1() {super(Get_message_digest_instance(), 40);} + public String Key() {return KEY;} public static final String KEY = "sha1"; + + private static MessageDigest Get_message_digest_instance() { + if (md__sha1 == null) + md__sha1 = Get_message_digest(KEY); + return md__sha1; + } private static MessageDigest md__sha1; +} +class Hash_algo__sha2_256 extends Hash_algo_base { + public Hash_algo__sha2_256() {super(Get_message_digest_instance(), 64);} + public String Key() {return KEY;} public static final String KEY = "sha-256"; + + private static MessageDigest Get_message_digest_instance() { + if (md__sha2_256 == null) + md__sha2_256 = Get_message_digest(KEY); + return md__sha2_256; + } private static MessageDigest md__sha2_256; +} +class Hash_algo_utl_ { + public static void Hash_bry(MessageDigest md, byte[] src_bry, int src_len, byte[] trg_bry) { + int pos = 0; + while (true) { + if (pos == src_len) break; + int len = 4096; + if (pos + len > src_len) { + len = src_len - pos; + } + md.update(src_bry, pos, len); + pos += len; + } + byte[] md_bry = md.digest(); + gplx.core.encoders.Hex_utl_.Encode_bry(md_bry, trg_bry); + } + public static void Hash_stream(Console_adp dialog, IoStream stream, MessageDigest md, byte[] tmp_bfr, int tmp_bfr_len, byte[] trg_bry) { +// long pos = 0, len = stream.Len(); // pos and len must be long, else will not hash files > 2 GB + while (true) { + int read = stream.Read(tmp_bfr, 0, tmp_bfr_len); // read stream into tmp_bfr + if (read < 1) break; + md.update(tmp_bfr, 0, read); +// pos += read; + } + byte[] md_bry = md.digest(); + gplx.core.encoders.Hex_utl_.Encode_bry(md_bry , trg_bry); + } + public static void Hash_stream(Gfo_prog_ui prog_ui, IoStream stream, MessageDigest md, byte[] tmp_bfr, int tmp_bfr_len, byte[] trg_bry) { + long pos = prog_ui.Prog_data_cur(), len = prog_ui.Prog_data_end(); // pos and len must be long, else will not hash files > 2 GB + try { + while (true) { + int read = stream.Read(tmp_bfr, 0, tmp_bfr_len); // read stream into tmp_bfr + if (read < 1) break; + md.update(tmp_bfr, 0, read); + if (prog_ui.Prog_notify_and_chk_if_suspended(pos, len)) return; + pos += read; + } + } + finally {stream.Rls();} + byte[] md_bry = md.digest(); + gplx.core.encoders.Hex_utl_.Encode_bry(md_bry , trg_bry); + } + public static String To_base_32_str(byte[] ary) {return Base32Converter.Encode(ary);} +} diff --git a/100_core/src/gplx/core/security/Hash_algo__md5__tst.java b/100_core/src/gplx/core/security/Hash_algo__md5__tst.java new file mode 100644 index 000000000..ecd01dea2 --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo__md5__tst.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Hash_algo__md5__tst { // REF: https://www.cosic.esat.kuleuven.be/nessie/testvectors/hash/md5/Md5-128.unverified.test-vectors + private final Hash_algo__fxt fxt = new Hash_algo__fxt(Hash_algo_.New__md5()); + @Test public void Empty() {fxt.Test__hash("d41d8cd98f00b204e9800998ecf8427e", "");} + @Test public void a() {fxt.Test__hash("0cc175b9c0f1b6a831c399e269772661", "a");} + @Test public void abc() {fxt.Test__hash("900150983cd24fb0d6963f7d28e17f72", "abc");} + @Test public void message_digest() {fxt.Test__hash("f96b697d7cb7938d525a2f31aaf161d0", "message digest");} + @Test public void a_z() {fxt.Test__hash("c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz");} + @Test public void a_q__mixed() {fxt.Test__hash("8215ef0796a20bcaaae116d3876c664a", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");} + @Test public void A_Z__a_z__0_9() {fxt.Test__hash("d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");} + //@Test + public void Num__x_8() {fxt.Test__hash("57edf4a22be3c955ac49da2e2107b67a", String_.Repeat("1234567890", 8));} + //@Test + public void A__x_1million() {fxt.Test__hash("7707d6ae4e027c70eea2a935c2296f21", String_.Repeat("a", 1000000));} +} +class Hash_algo__fxt { + private final Hash_algo algo; + public Hash_algo__fxt(Hash_algo algo) {this.algo = algo;} + public void Test__hash(String expd, String raw) { + Tfds.Eq(expd, algo.Hash_bry_as_str(Bry_.new_u8(raw))); + Tfds.Eq(expd, algo.Hash_stream_as_str(gplx.core.consoles.Console_adp_.Noop, gplx.core.ios.streams.IoStream_.mem_txt_(Io_url_.Empty, raw))); + } +} diff --git a/100_core/src/gplx/core/security/Hash_algo__sha1__tst.java b/100_core/src/gplx/core/security/Hash_algo__sha1__tst.java new file mode 100644 index 000000000..531f9974e --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo__sha1__tst.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Hash_algo__sha1__tst { // REF: https://www.cosic.esat.kuleuven.be/nessie/testvectors/ + private final Hash_algo__fxt fxt = new Hash_algo__fxt(Hash_algo_.New__sha1()); + @Test public void Empty() {fxt.Test__hash("da39a3ee5e6b4b0d3255bfef95601890afd80709", "");} + @Test public void a() {fxt.Test__hash("86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a");} + @Test public void abc() {fxt.Test__hash("a9993e364706816aba3e25717850c26c9cd0d89d", "abc");} + @Test public void message_digest() {fxt.Test__hash("c12252ceda8be8994d5fa0290a47231c1d16aae3", "message digest");} + @Test public void a_z() {fxt.Test__hash("32d10c7b8cf96570ca04ce37f2a19d84240d3a89", "abcdefghijklmnopqrstuvwxyz");} + @Test public void a_q__mixed() {fxt.Test__hash("84983e441c3bd26ebaae4aa1f95129e5e54670f1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");} + @Test public void A_Z__a_z__0_9() {fxt.Test__hash("761c457bf73b14d27e9e9265c46f4b4dda11f940", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");} + // @Test + public void Num() {fxt.Test__hash("50abf5706a150990a08b2c5ea40fa0e585554732", String_.Repeat("1234567890", 8));} + //@Test + public void A_1Million() {fxt.Test__hash("34aa973cd4c4daa4f61eeb2bdbad27316534016f", String_.Repeat("a", 1000000));} +} diff --git a/100_core/src/gplx/core/security/Hash_algo__sha2_256__tst.java b/100_core/src/gplx/core/security/Hash_algo__sha2_256__tst.java new file mode 100644 index 000000000..eda17424d --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo__sha2_256__tst.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Hash_algo__sha2_256__tst { // REF: https://www.cosic.esat.kuleuven.be/nessie/testvectors/ + private final Hash_algo__fxt fxt = new Hash_algo__fxt(Hash_algo_.New__sha2_256()); + @Test public void Empty() {fxt.Test__hash("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "");} + @Test public void a() {fxt.Test__hash("ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "a");} + @Test public void abc() {fxt.Test__hash("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc");} + @Test public void message_digest() {fxt.Test__hash("f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650", "message digest");} + @Test public void a_z() {fxt.Test__hash("71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73", "abcdefghijklmnopqrstuvwxyz");} + @Test public void a_q__mixed() {fxt.Test__hash("248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");} + @Test public void A_Z__a_z__0_9() {fxt.Test__hash("db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");} + // @Test + public void Num() {fxt.Test__hash("f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e", String_.Repeat("1234567890", 8));} + //@Test + public void A_1Million() {fxt.Test__hash("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", String_.Repeat("a", 1000000));} +} diff --git a/100_core/src/gplx/core/security/Hash_algo__tth_192.java b/100_core/src/gplx/core/security/Hash_algo__tth_192.java new file mode 100644 index 000000000..041e43e56 --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo__tth_192.java @@ -0,0 +1,1007 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import gplx.core.consoles.*; import gplx.core.ios.streams.*; /*IoStream*/ +import gplx.core.progs.*; +public class Hash_algo__tth_192 implements Hash_algo { + public String Key() {return KEY;} public static final String KEY = "tth192"; + public int BlockSize() {return blockSize;} public void BlockSize_set(int v) {blockSize = v;} int blockSize = 1024; + public String Hash_bry_as_str(byte[] src) {return String_.new_a7(Hash_bry_as_bry(src));} + public byte[] Hash_bry_as_bry(byte[] v) {return Bry_.new_a7(Hash_stream_as_str(Console_adp_.Noop, gplx.core.ios.streams.IoStream_.ary_(v)));} + public byte[] Hash_stream_as_bry(Gfo_prog_ui prog_ui, IoStream stream) {return Bry_.new_a7(Hash_stream_as_str(Console_adp_.Noop, stream));} + public String Hash_stream_as_str(Console_adp dialog, IoStream stream) { + int leafCount = (int)(stream.Len() / blockSize); + HashDlgWtr dialogWtr = HashDlgWtr_.Current; + dialogWtr.Bgn(dialog, stream.Url(), CalcWorkUnits(stream.Len())); + if (stream.Len() % blockSize > 0) // if stream.length is not multiple of blockSize, add another leaf to hold leftover bytes + leafCount++; + else if (stream.Len() == 0) // if stream.length == 0, still allocate one leaf + leafCount = 1; + + hashMain = new byte[(leafCount / 2) + 1][]; // / 2 b/c HashAllBytes looks at pairs of slots; + 1 to avoid truncation + hashMainCount = 0; + HashAllBytes(dialogWtr, stream, leafCount); + byte[] rv = HashAllHashes(dialogWtr); + return Hash_algo_utl_.To_base_32_str(rv); + } + byte[] CalcHash_next(IoStream stream) { + if (blockA == null || blockA.length != blockSize) blockA = new byte[blockSize]; + if (blockB == null || blockB.length != blockSize) blockB = new byte[blockSize]; + + int dataSize = stream.Read(blockA, 0, blockSize); // if (dataSize < BlockSize) return (CalcHash_leaf(ShrinkArray(blockA, dataSize))); // TODO_OLD: reinstate? + dataSize = stream.Read(blockB, 0, blockSize); + if (dataSize < blockSize) blockB = ShrinkArray(blockB, dataSize); // shrink array to match data size (occurs at end of stream, when lastBytesCount < BlockSize) + return CalcHash_branch(CalcHash_leaf(blockA, 0), CalcHash_leaf(blockB, 1)); + } + void HashAllBytes(HashDlgWtr dialogWtr, IoStream stream, int leafCount) { + int total = leafCount / 2; + for (int i = 0; i < total; i++) { + if (dialogWtr.Canceled()) dialogWtr.Fail(stream); + hashMain[hashMainCount++] = CalcHash_next(stream); + dialogWtr.Do(2); + } + if (leafCount % 2 == 1) { // odd number of leaves => hash last leaf (since it was never in a pair) + byte[] block = new byte[blockSize]; + int dataSize = stream.Read(block, 0 , blockSize); + if (dataSize < blockSize) block = ShrinkArray(block,dataSize); // shrink array to match data size (occurs at end of stream, when lastBytesCount < BlockSize) + + hashMain[hashMainCount++] = CalcHash_leaf(block, 0); + dialogWtr.Do(1); + } + stream.Rls(); + } + byte[] HashAllHashes(HashDlgWtr dialogWtr) { + int currIdx = 0, lastIdx = hashMainCount - 1, reuseIdx = 0; + while (currIdx < lastIdx) { // calc till 1 is left; + while (currIdx < lastIdx) { // calc till 1 is left; EX: 8 slots; loop1 calcs 8; makes 4; loop0 restarts; loop1 calcs 4; makes 2; etc.. + byte[] hashA = hashMain[currIdx++]; + byte[] hashB = hashMain[currIdx++]; + hashMain[reuseIdx++] = CalcHash_branch(hashA, hashB); + dialogWtr.Do(2); + } + if (currIdx == lastIdx) // NOTE: currIdx == lastIdx only when odd number of leaves + hashMain[reuseIdx++] = hashMain[currIdx++]; +// for (int j = reuseIdx; j < lastIdx; j++) // PERF: free unused slots (may not be necessary) +// hashMain[j] = null; + lastIdx = reuseIdx - 1; + currIdx = 0; + reuseIdx = 0; + }; + dialogWtr.End(); + return (byte[])hashMain[lastIdx]; // return last hash as root hash + } + byte[] CalcHash_branch(byte[] blockA, byte[] blockB) { + // gen array: [0x01] + [blockA] + [blockB] + if (branchRv == null || branchRv.length != blockA.length + blockB.length + 1) + branchRv = new byte[blockA.length + blockB.length + 1]; + branchRv[0] = 0x01; // branch hash mark. + Array_.Copy_to(blockA, branchRv, 1); + Array_.Copy_to(blockB, branchRv, blockA.length + 1); + return CalcHash(branchRv); + } + byte[] CalcHash_leaf(byte[] raw, int i) { + // gen array: [0x00] + [raw] + byte[] rv = null; + if (i == 0) { + if (leaf0 == null || leaf0.length != raw.length + 1) { + leaf0 = new byte[raw.length + 1]; + } + rv = leaf0; + } + else { + if (leaf1 == null || leaf1.length != raw.length + 1) { + leaf1 = new byte[raw.length + 1]; + } + rv = leaf1; + } + + rv[0] = 0x00; // leaf hash mark. + Array_.Copy_to(raw, rv, 1); + return CalcHash(rv); + } + byte[] CalcHash(byte[] raw) { + algo.Initialize(); + return algo.ComputeHash(raw); + } + byte[] ShrinkArray(byte[] raw, int newLen) { + byte[] rv = new byte[newLen]; + for (int i = 0; i < newLen; i++) + rv[i] = raw[i]; + return rv; + } + public int CalcWorkUnits(long streamLength) { + int leafCount = (int)(streamLength / blockSize); + int phase1 = leafCount; + if (leafCount == 0) phase1 = 1; + else if (streamLength % blockSize != 0) phase1++; + int phase2 = phase1 <= 1 ? 0 : phase1 - 1; // see note + return phase1 + phase2; + } + Tiger192 algo = new Tiger192(); + byte[][] hashMain; int hashMainCount = 0; + byte[] blockA, blockB, branchRv, leaf0, leaf1; +} +interface HashDlgWtr { + boolean Canceled(); + void Bgn(Console_adp dialog, Io_url url, int total); + void Do(int increment); + void End(); + void Fail(IoStream stream); +} +class HashDlgWtr_ { + public static HashDlgWtr Current = HashDlgWtrDefault.new_(); +} +class HashDlgWtrDefault implements HashDlgWtr { + public int Total() {return total;} int total; + public int LastPercentage() {return lastPercentage;} int lastPercentage = 0; + public int Current() {return current;} int current = 0; + public boolean Canceled() {return dialog.Canceled_chk();} + String p; + public void Bgn(Console_adp dialog, Io_url url, int total) { + this.dialog = dialog; this.total = total; + p = url.Xto_api() + " - hash: "; + this.lastPercentage = 0; this.current = 0; + } + public void Do(int increment) { + current += increment; + int percentage = (current * 100) / total; + if (percentage <= lastPercentage) return; + dialog.Write_tmp(String_.LimitToFirst(p, dialog.Chars_per_line_max()) + Int_.To_str(percentage) + "%"); + lastPercentage = percentage; + } + public void End() {} + public void Fail(IoStream stream) { + // stream.Dispose(); + } + Console_adp dialog; + public static HashDlgWtrDefault new_() {return new HashDlgWtrDefault();} +} +class Tiger192 extends BaseHash { + public void Initialize() { + this.resetContext(); + } + public byte[] ComputeHash(byte[] input) { + this.update(input, 0, input.length); + return this.digest(); + } + protected byte[] padBuffer() { + int n = (int) (count % BLOCK_SIZE); + int padding = (n < 56) ? (56 - n) : (120 - n); + byte[] pad = new byte[padding + 8]; + + pad[0] = 1; + long bits = count << 3; + + pad[padding++] = (byte) bits; + pad[padding++] = (byte) (bits >>> 8); + pad[padding++] = (byte) (bits >>> 16); + pad[padding++] = (byte) (bits >>> 24); + pad[padding++] = (byte) (bits >>> 32); + pad[padding++] = (byte) (bits >>> 40); + pad[padding++] = (byte) (bits >>> 48); + pad[padding ] = (byte) (bits >>> 56); + + return pad; + } + + protected byte[] getResult() { + return new byte[] { + (byte) a , (byte)(a >>> 8), (byte)(a >>> 16), + (byte)(a >>> 24), (byte)(a >>> 32), (byte)(a >>> 40), + (byte)(a >>> 48), (byte)(a >>> 56), + (byte) b , (byte)(b >>> 8), (byte)(b >>> 16), + (byte)(b >>> 24), (byte)(b >>> 32), (byte)(b >>> 40), + (byte)(b >>> 48), (byte)(b >>> 56), + (byte) c , (byte)(c >>> 8), (byte)(c >>> 16), + (byte)(c >>> 24), (byte)(c >>> 32), (byte)(c >>> 40), + (byte)(c >>> 48), (byte)(c >>> 56) + }; + } + + protected void resetContext() { + a = A; + b = B; + c = C; + } + protected void transform(byte[] in, int offset) { + long x0, x1, x2, x3, x4, x5, x6, x7; + + x0 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x1 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x2 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x3 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x4 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x5 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x6 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x7 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset ] & 0xFF) << 56); + + // save_abc ::= + long aa = a, bb = b, cc = c; + + // pass(aa, bb, cc, 5) ::= + cc ^= x0; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 5; + aa ^= x1; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 5; + bb ^= x2; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 5; + cc ^= x3; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 5; + aa ^= x4; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 5; + bb ^= x5; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 5; + cc ^= x6; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 5; + aa ^= x7; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 5; + + // key_schedule ::= + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5L; + x1 ^= x0; + x2 += x1; + x3 -= x2 ^ ((~x1) << 19); + x4 ^= x3; + x5 += x4; + x6 -= x5 ^ ((~x4) >>> 23); + x7 ^= x6; + x0 += x7; + x1 -= x0 ^ ((~x7) << 19); + x2 ^= x1; + x3 += x2; + x4 -= x3 ^ ((~x2) >>> 23); + x5 ^= x4; + x6 += x5; + x7 -= x6 ^ 0x0123456789ABCDEFL; + + // pass(cc, aa, bb, 7) ::= + bb ^= x0; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 7; + cc ^= x1; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 7; + aa ^= x2; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 7; + bb ^= x3; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 7; + cc ^= x4; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 7; + aa ^= x5; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 7; + bb ^= x6; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 7; + cc ^= x7; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 7; + + // key_schedule ::= + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5L; + x1 ^= x0; + x2 += x1; + x3 -= x2 ^ ((~x1) << 19); + x4 ^= x3; + x5 += x4; + x6 -= x5 ^ ((~x4) >>> 23); + x7 ^= x6; + x0 += x7; + x1 -= x0 ^ ((~x7) << 19); + x2 ^= x1; + x3 += x2; + x4 -= x3 ^ ((~x2) >>> 23); + x5 ^= x4; + x6 += x5; + x7 -= x6 ^ 0x0123456789ABCDEFL; + + // pass(bb,cc,aa,9) ::= + aa ^= x0; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 9; + bb ^= x1; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 9; + cc ^= x2; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 9; + aa ^= x3; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 9; + bb ^= x4; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 9; + cc ^= x5; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 9; + aa ^= x6; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 9; + bb ^= x7; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 9; + + // feedforward ::= + a ^= aa; + b = bb - b; + c += cc; + } + protected static final int HASH_SIZE = 24; + + protected static final int BLOCK_SIZE = 64; + + /** Result when no data has been input. */ + + private static final long A = 0x0123456789ABCDEFL; + private static final long B = 0xFEDCBA9876543210L; + private static final long C = 0xF096A5B4C3B2E187L; + + /** S-Box T1. */ + private static final long[] T1 = { + 0x02AAB17CF7E90C5EL, 0xAC424B03E243A8ECL, 0x72CD5BE30DD5FCD3L, 0x6D019B93F6F97F3AL, + 0xCD9978FFD21F9193L, 0x7573A1C9708029E2L, 0xB164326B922A83C3L, 0x46883EEE04915870L, + 0xEAACE3057103ECE6L, 0xC54169B808A3535CL, 0x4CE754918DDEC47CL, 0x0AA2F4DFDC0DF40CL, + 0x10B76F18A74DBEFAL, 0xC6CCB6235AD1AB6AL, 0x13726121572FE2FFL, 0x1A488C6F199D921EL, + 0x4BC9F9F4DA0007CAL, 0x26F5E6F6E85241C7L, 0x859079DBEA5947B6L, 0x4F1885C5C99E8C92L, + 0xD78E761EA96F864BL, 0x8E36428C52B5C17DL, 0x69CF6827373063C1L, 0xB607C93D9BB4C56EL, + 0x7D820E760E76B5EAL, 0x645C9CC6F07FDC42L, 0xBF38A078243342E0L, 0x5F6B343C9D2E7D04L, + 0xF2C28AEB600B0EC6L, 0x6C0ED85F7254BCACL, 0x71592281A4DB4FE5L, 0x1967FA69CE0FED9FL, + 0xFD5293F8B96545DBL, 0xC879E9D7F2A7600BL, 0x860248920193194EL, 0xA4F9533B2D9CC0B3L, + 0x9053836C15957613L, 0xDB6DCF8AFC357BF1L, 0x18BEEA7A7A370F57L, 0x037117CA50B99066L, + 0x6AB30A9774424A35L, 0xF4E92F02E325249BL, 0x7739DB07061CCAE1L, 0xD8F3B49CECA42A05L, + 0xBD56BE3F51382F73L, 0x45FAED5843B0BB28L, 0x1C813D5C11BF1F83L, 0x8AF0E4B6D75FA169L, + 0x33EE18A487AD9999L, 0x3C26E8EAB1C94410L, 0xB510102BC0A822F9L, 0x141EEF310CE6123BL, + 0xFC65B90059DDB154L, 0xE0158640C5E0E607L, 0x884E079826C3A3CFL, 0x930D0D9523C535FDL, + 0x35638D754E9A2B00L, 0x4085FCCF40469DD5L, 0xC4B17AD28BE23A4CL, 0xCAB2F0FC6A3E6A2EL, + 0x2860971A6B943FCDL, 0x3DDE6EE212E30446L, 0x6222F32AE01765AEL, 0x5D550BB5478308FEL, + 0xA9EFA98DA0EDA22AL, 0xC351A71686C40DA7L, 0x1105586D9C867C84L, 0xDCFFEE85FDA22853L, + 0xCCFBD0262C5EEF76L, 0xBAF294CB8990D201L, 0xE69464F52AFAD975L, 0x94B013AFDF133E14L, + 0x06A7D1A32823C958L, 0x6F95FE5130F61119L, 0xD92AB34E462C06C0L, 0xED7BDE33887C71D2L, + 0x79746D6E6518393EL, 0x5BA419385D713329L, 0x7C1BA6B948A97564L, 0x31987C197BFDAC67L, + 0xDE6C23C44B053D02L, 0x581C49FED002D64DL, 0xDD474D6338261571L, 0xAA4546C3E473D062L, + 0x928FCE349455F860L, 0x48161BBACAAB94D9L, 0x63912430770E6F68L, 0x6EC8A5E602C6641CL, + 0x87282515337DDD2BL, 0x2CDA6B42034B701BL, 0xB03D37C181CB096DL, 0xE108438266C71C6FL, + 0x2B3180C7EB51B255L, 0xDF92B82F96C08BBCL, 0x5C68C8C0A632F3BAL, 0x5504CC861C3D0556L, + 0xABBFA4E55FB26B8FL, 0x41848B0AB3BACEB4L, 0xB334A273AA445D32L, 0xBCA696F0A85AD881L, + 0x24F6EC65B528D56CL, 0x0CE1512E90F4524AL, 0x4E9DD79D5506D35AL, 0x258905FAC6CE9779L, + 0x2019295B3E109B33L, 0xF8A9478B73A054CCL, 0x2924F2F934417EB0L, 0x3993357D536D1BC4L, + 0x38A81AC21DB6FF8BL, 0x47C4FBF17D6016BFL, 0x1E0FAADD7667E3F5L, 0x7ABCFF62938BEB96L, + 0xA78DAD948FC179C9L, 0x8F1F98B72911E50DL, 0x61E48EAE27121A91L, 0x4D62F7AD31859808L, + 0xECEBA345EF5CEAEBL, 0xF5CEB25EBC9684CEL, 0xF633E20CB7F76221L, 0xA32CDF06AB8293E4L, + 0x985A202CA5EE2CA4L, 0xCF0B8447CC8A8FB1L, 0x9F765244979859A3L, 0xA8D516B1A1240017L, + 0x0BD7BA3EBB5DC726L, 0xE54BCA55B86ADB39L, 0x1D7A3AFD6C478063L, 0x519EC608E7669EDDL, + 0x0E5715A2D149AA23L, 0x177D4571848FF194L, 0xEEB55F3241014C22L, 0x0F5E5CA13A6E2EC2L, + 0x8029927B75F5C361L, 0xAD139FABC3D6E436L, 0x0D5DF1A94CCF402FL, 0x3E8BD948BEA5DFC8L, + 0xA5A0D357BD3FF77EL, 0xA2D12E251F74F645L, 0x66FD9E525E81A082L, 0x2E0C90CE7F687A49L, + 0xC2E8BCBEBA973BC5L, 0x000001BCE509745FL, 0x423777BBE6DAB3D6L, 0xD1661C7EAEF06EB5L, + 0xA1781F354DAACFD8L, 0x2D11284A2B16AFFCL, 0xF1FC4F67FA891D1FL, 0x73ECC25DCB920ADAL, + 0xAE610C22C2A12651L, 0x96E0A810D356B78AL, 0x5A9A381F2FE7870FL, 0xD5AD62EDE94E5530L, + 0xD225E5E8368D1427L, 0x65977B70C7AF4631L, 0x99F889B2DE39D74FL, 0x233F30BF54E1D143L, + 0x9A9675D3D9A63C97L, 0x5470554FF334F9A8L, 0x166ACB744A4F5688L, 0x70C74CAAB2E4AEADL, + 0xF0D091646F294D12L, 0x57B82A89684031D1L, 0xEFD95A5A61BE0B6BL, 0x2FBD12E969F2F29AL, + 0x9BD37013FEFF9FE8L, 0x3F9B0404D6085A06L, 0x4940C1F3166CFE15L, 0x09542C4DCDF3DEFBL, + 0xB4C5218385CD5CE3L, 0xC935B7DC4462A641L, 0x3417F8A68ED3B63FL, 0xB80959295B215B40L, + 0xF99CDAEF3B8C8572L, 0x018C0614F8FCB95DL, 0x1B14ACCD1A3ACDF3L, 0x84D471F200BB732DL, + 0xC1A3110E95E8DA16L, 0x430A7220BF1A82B8L, 0xB77E090D39DF210EL, 0x5EF4BD9F3CD05E9DL, + 0x9D4FF6DA7E57A444L, 0xDA1D60E183D4A5F8L, 0xB287C38417998E47L, 0xFE3EDC121BB31886L, + 0xC7FE3CCC980CCBEFL, 0xE46FB590189BFD03L, 0x3732FD469A4C57DCL, 0x7EF700A07CF1AD65L, + 0x59C64468A31D8859L, 0x762FB0B4D45B61F6L, 0x155BAED099047718L, 0x68755E4C3D50BAA6L, + 0xE9214E7F22D8B4DFL, 0x2ADDBF532EAC95F4L, 0x32AE3909B4BD0109L, 0x834DF537B08E3450L, + 0xFA209DA84220728DL, 0x9E691D9B9EFE23F7L, 0x0446D288C4AE8D7FL, 0x7B4CC524E169785BL, + 0x21D87F0135CA1385L, 0xCEBB400F137B8AA5L, 0x272E2B66580796BEL, 0x3612264125C2B0DEL, + 0x057702BDAD1EFBB2L, 0xD4BABB8EACF84BE9L, 0x91583139641BC67BL, 0x8BDC2DE08036E024L, + 0x603C8156F49F68EDL, 0xF7D236F7DBEF5111L, 0x9727C4598AD21E80L, 0xA08A0896670A5FD7L, + 0xCB4A8F4309EBA9CBL, 0x81AF564B0F7036A1L, 0xC0B99AA778199ABDL, 0x959F1EC83FC8E952L, + 0x8C505077794A81B9L, 0x3ACAAF8F056338F0L, 0x07B43F50627A6778L, 0x4A44AB49F5ECCC77L, + 0x3BC3D6E4B679EE98L, 0x9CC0D4D1CF14108CL, 0x4406C00B206BC8A0L, 0x82A18854C8D72D89L, + 0x67E366B35C3C432CL, 0xB923DD61102B37F2L, 0x56AB2779D884271DL, 0xBE83E1B0FF1525AFL, + 0xFB7C65D4217E49A9L, 0x6BDBE0E76D48E7D4L, 0x08DF828745D9179EL, 0x22EA6A9ADD53BD34L, + 0xE36E141C5622200AL, 0x7F805D1B8CB750EEL, 0xAFE5C7A59F58E837L, 0xE27F996A4FB1C23CL, + 0xD3867DFB0775F0D0L, 0xD0E673DE6E88891AL, 0x123AEB9EAFB86C25L, 0x30F1D5D5C145B895L, + 0xBB434A2DEE7269E7L, 0x78CB67ECF931FA38L, 0xF33B0372323BBF9CL, 0x52D66336FB279C74L, + 0x505F33AC0AFB4EAAL, 0xE8A5CD99A2CCE187L, 0x534974801E2D30BBL, 0x8D2D5711D5876D90L, + 0x1F1A412891BC038EL, 0xD6E2E71D82E56648L, 0x74036C3A497732B7L, 0x89B67ED96361F5ABL, + 0xFFED95D8F1EA02A2L, 0xE72B3BD61464D43DL, 0xA6300F170BDC4820L, 0xEBC18760ED78A77AL + }; + + /** S-Box T2. */ + private static final long[] T2 = { + 0xE6A6BE5A05A12138L, 0xB5A122A5B4F87C98L, 0x563C6089140B6990L, 0x4C46CB2E391F5DD5L, + 0xD932ADDBC9B79434L, 0x08EA70E42015AFF5L, 0xD765A6673E478CF1L, 0xC4FB757EAB278D99L, + 0xDF11C6862D6E0692L, 0xDDEB84F10D7F3B16L, 0x6F2EF604A665EA04L, 0x4A8E0F0FF0E0DFB3L, + 0xA5EDEEF83DBCBA51L, 0xFC4F0A2A0EA4371EL, 0xE83E1DA85CB38429L, 0xDC8FF882BA1B1CE2L, + 0xCD45505E8353E80DL, 0x18D19A00D4DB0717L, 0x34A0CFEDA5F38101L, 0x0BE77E518887CAF2L, + 0x1E341438B3C45136L, 0xE05797F49089CCF9L, 0xFFD23F9DF2591D14L, 0x543DDA228595C5CDL, + 0x661F81FD99052A33L, 0x8736E641DB0F7B76L, 0x15227725418E5307L, 0xE25F7F46162EB2FAL, + 0x48A8B2126C13D9FEL, 0xAFDC541792E76EEAL, 0x03D912BFC6D1898FL, 0x31B1AAFA1B83F51BL, + 0xF1AC2796E42AB7D9L, 0x40A3A7D7FCD2EBACL, 0x1056136D0AFBBCC5L, 0x7889E1DD9A6D0C85L, + 0xD33525782A7974AAL, 0xA7E25D09078AC09BL, 0xBD4138B3EAC6EDD0L, 0x920ABFBE71EB9E70L, + 0xA2A5D0F54FC2625CL, 0xC054E36B0B1290A3L, 0xF6DD59FF62FE932BL, 0x3537354511A8AC7DL, + 0xCA845E9172FADCD4L, 0x84F82B60329D20DCL, 0x79C62CE1CD672F18L, 0x8B09A2ADD124642CL, + 0xD0C1E96A19D9E726L, 0x5A786A9B4BA9500CL, 0x0E020336634C43F3L, 0xC17B474AEB66D822L, + 0x6A731AE3EC9BAAC2L, 0x8226667AE0840258L, 0x67D4567691CAECA5L, 0x1D94155C4875ADB5L, + 0x6D00FD985B813FDFL, 0x51286EFCB774CD06L, 0x5E8834471FA744AFL, 0xF72CA0AEE761AE2EL, + 0xBE40E4CDAEE8E09AL, 0xE9970BBB5118F665L, 0x726E4BEB33DF1964L, 0x703B000729199762L, + 0x4631D816F5EF30A7L, 0xB880B5B51504A6BEL, 0x641793C37ED84B6CL, 0x7B21ED77F6E97D96L, + 0x776306312EF96B73L, 0xAE528948E86FF3F4L, 0x53DBD7F286A3F8F8L, 0x16CADCE74CFC1063L, + 0x005C19BDFA52C6DDL, 0x68868F5D64D46AD3L, 0x3A9D512CCF1E186AL, 0x367E62C2385660AEL, + 0xE359E7EA77DCB1D7L, 0x526C0773749ABE6EL, 0x735AE5F9D09F734BL, 0x493FC7CC8A558BA8L, + 0xB0B9C1533041AB45L, 0x321958BA470A59BDL, 0x852DB00B5F46C393L, 0x91209B2BD336B0E5L, + 0x6E604F7D659EF19FL, 0xB99A8AE2782CCB24L, 0xCCF52AB6C814C4C7L, 0x4727D9AFBE11727BL, + 0x7E950D0C0121B34DL, 0x756F435670AD471FL, 0xF5ADD442615A6849L, 0x4E87E09980B9957AL, + 0x2ACFA1DF50AEE355L, 0xD898263AFD2FD556L, 0xC8F4924DD80C8FD6L, 0xCF99CA3D754A173AL, + 0xFE477BACAF91BF3CL, 0xED5371F6D690C12DL, 0x831A5C285E687094L, 0xC5D3C90A3708A0A4L, + 0x0F7F903717D06580L, 0x19F9BB13B8FDF27FL, 0xB1BD6F1B4D502843L, 0x1C761BA38FFF4012L, + 0x0D1530C4E2E21F3BL, 0x8943CE69A7372C8AL, 0xE5184E11FEB5CE66L, 0x618BDB80BD736621L, + 0x7D29BAD68B574D0BL, 0x81BB613E25E6FE5BL, 0x071C9C10BC07913FL, 0xC7BEEB7909AC2D97L, + 0xC3E58D353BC5D757L, 0xEB017892F38F61E8L, 0xD4EFFB9C9B1CC21AL, 0x99727D26F494F7ABL, + 0xA3E063A2956B3E03L, 0x9D4A8B9A4AA09C30L, 0x3F6AB7D500090FB4L, 0x9CC0F2A057268AC0L, + 0x3DEE9D2DEDBF42D1L, 0x330F49C87960A972L, 0xC6B2720287421B41L, 0x0AC59EC07C00369CL, + 0xEF4EAC49CB353425L, 0xF450244EEF0129D8L, 0x8ACC46E5CAF4DEB6L, 0x2FFEAB63989263F7L, + 0x8F7CB9FE5D7A4578L, 0x5BD8F7644E634635L, 0x427A7315BF2DC900L, 0x17D0C4AA2125261CL, + 0x3992486C93518E50L, 0xB4CBFEE0A2D7D4C3L, 0x7C75D6202C5DDD8DL, 0xDBC295D8E35B6C61L, + 0x60B369D302032B19L, 0xCE42685FDCE44132L, 0x06F3DDB9DDF65610L, 0x8EA4D21DB5E148F0L, + 0x20B0FCE62FCD496FL, 0x2C1B912358B0EE31L, 0xB28317B818F5A308L, 0xA89C1E189CA6D2CFL, + 0x0C6B18576AAADBC8L, 0xB65DEAA91299FAE3L, 0xFB2B794B7F1027E7L, 0x04E4317F443B5BEBL, + 0x4B852D325939D0A6L, 0xD5AE6BEEFB207FFCL, 0x309682B281C7D374L, 0xBAE309A194C3B475L, + 0x8CC3F97B13B49F05L, 0x98A9422FF8293967L, 0x244B16B01076FF7CL, 0xF8BF571C663D67EEL, + 0x1F0D6758EEE30DA1L, 0xC9B611D97ADEB9B7L, 0xB7AFD5887B6C57A2L, 0x6290AE846B984FE1L, + 0x94DF4CDEACC1A5FDL, 0x058A5BD1C5483AFFL, 0x63166CC142BA3C37L, 0x8DB8526EB2F76F40L, + 0xE10880036F0D6D4EL, 0x9E0523C9971D311DL, 0x45EC2824CC7CD691L, 0x575B8359E62382C9L, + 0xFA9E400DC4889995L, 0xD1823ECB45721568L, 0xDAFD983B8206082FL, 0xAA7D29082386A8CBL, + 0x269FCD4403B87588L, 0x1B91F5F728BDD1E0L, 0xE4669F39040201F6L, 0x7A1D7C218CF04ADEL, + 0x65623C29D79CE5CEL, 0x2368449096C00BB1L, 0xAB9BF1879DA503BAL, 0xBC23ECB1A458058EL, + 0x9A58DF01BB401ECCL, 0xA070E868A85F143DL, 0x4FF188307DF2239EL, 0x14D565B41A641183L, + 0xEE13337452701602L, 0x950E3DCF3F285E09L, 0x59930254B9C80953L, 0x3BF299408930DA6DL, + 0xA955943F53691387L, 0xA15EDECAA9CB8784L, 0x29142127352BE9A0L, 0x76F0371FFF4E7AFBL, + 0x0239F450274F2228L, 0xBB073AF01D5E868BL, 0xBFC80571C10E96C1L, 0xD267088568222E23L, + 0x9671A3D48E80B5B0L, 0x55B5D38AE193BB81L, 0x693AE2D0A18B04B8L, 0x5C48B4ECADD5335FL, + 0xFD743B194916A1CAL, 0x2577018134BE98C4L, 0xE77987E83C54A4ADL, 0x28E11014DA33E1B9L, + 0x270CC59E226AA213L, 0x71495F756D1A5F60L, 0x9BE853FB60AFEF77L, 0xADC786A7F7443DBFL, + 0x0904456173B29A82L, 0x58BC7A66C232BD5EL, 0xF306558C673AC8B2L, 0x41F639C6B6C9772AL, + 0x216DEFE99FDA35DAL, 0x11640CC71C7BE615L, 0x93C43694565C5527L, 0xEA038E6246777839L, + 0xF9ABF3CE5A3E2469L, 0x741E768D0FD312D2L, 0x0144B883CED652C6L, 0xC20B5A5BA33F8552L, + 0x1AE69633C3435A9DL, 0x97A28CA4088CFDECL, 0x8824A43C1E96F420L, 0x37612FA66EEEA746L, + 0x6B4CB165F9CF0E5AL, 0x43AA1C06A0ABFB4AL, 0x7F4DC26FF162796BL, 0x6CBACC8E54ED9B0FL, + 0xA6B7FFEFD2BB253EL, 0x2E25BC95B0A29D4FL, 0x86D6A58BDEF1388CL, 0xDED74AC576B6F054L, + 0x8030BDBC2B45805DL, 0x3C81AF70E94D9289L, 0x3EFF6DDA9E3100DBL, 0xB38DC39FDFCC8847L, + 0x123885528D17B87EL, 0xF2DA0ED240B1B642L, 0x44CEFADCD54BF9A9L, 0x1312200E433C7EE6L, + 0x9FFCC84F3A78C748L, 0xF0CD1F72248576BBL, 0xEC6974053638CFE4L, 0x2BA7B67C0CEC4E4CL, + 0xAC2F4DF3E5CE32EDL, 0xCB33D14326EA4C11L, 0xA4E9044CC77E58BCL, 0x5F513293D934FCEFL, + 0x5DC9645506E55444L, 0x50DE418F317DE40AL, 0x388CB31A69DDE259L, 0x2DB4A83455820A86L, + 0x9010A91E84711AE9L, 0x4DF7F0B7B1498371L, 0xD62A2EABC0977179L, 0x22FAC097AA8D5C0EL + }; + + /** S-Box T3. */ + private static final long[] T3 = { + 0xF49FCC2FF1DAF39BL, 0x487FD5C66FF29281L, 0xE8A30667FCDCA83FL, 0x2C9B4BE3D2FCCE63L, + 0xDA3FF74B93FBBBC2L, 0x2FA165D2FE70BA66L, 0xA103E279970E93D4L, 0xBECDEC77B0E45E71L, + 0xCFB41E723985E497L, 0xB70AAA025EF75017L, 0xD42309F03840B8E0L, 0x8EFC1AD035898579L, + 0x96C6920BE2B2ABC5L, 0x66AF4163375A9172L, 0x2174ABDCCA7127FBL, 0xB33CCEA64A72FF41L, + 0xF04A4933083066A5L, 0x8D970ACDD7289AF5L, 0x8F96E8E031C8C25EL, 0xF3FEC02276875D47L, + 0xEC7BF310056190DDL, 0xF5ADB0AEBB0F1491L, 0x9B50F8850FD58892L, 0x4975488358B74DE8L, + 0xA3354FF691531C61L, 0x0702BBE481D2C6EEL, 0x89FB24057DEDED98L, 0xAC3075138596E902L, + 0x1D2D3580172772EDL, 0xEB738FC28E6BC30DL, 0x5854EF8F63044326L, 0x9E5C52325ADD3BBEL, + 0x90AA53CF325C4623L, 0xC1D24D51349DD067L, 0x2051CFEEA69EA624L, 0x13220F0A862E7E4FL, + 0xCE39399404E04864L, 0xD9C42CA47086FCB7L, 0x685AD2238A03E7CCL, 0x066484B2AB2FF1DBL, + 0xFE9D5D70EFBF79ECL, 0x5B13B9DD9C481854L, 0x15F0D475ED1509ADL, 0x0BEBCD060EC79851L, + 0xD58C6791183AB7F8L, 0xD1187C5052F3EEE4L, 0xC95D1192E54E82FFL, 0x86EEA14CB9AC6CA2L, + 0x3485BEB153677D5DL, 0xDD191D781F8C492AL, 0xF60866BAA784EBF9L, 0x518F643BA2D08C74L, + 0x8852E956E1087C22L, 0xA768CB8DC410AE8DL, 0x38047726BFEC8E1AL, 0xA67738B4CD3B45AAL, + 0xAD16691CEC0DDE19L, 0xC6D4319380462E07L, 0xC5A5876D0BA61938L, 0x16B9FA1FA58FD840L, + 0x188AB1173CA74F18L, 0xABDA2F98C99C021FL, 0x3E0580AB134AE816L, 0x5F3B05B773645ABBL, + 0x2501A2BE5575F2F6L, 0x1B2F74004E7E8BA9L, 0x1CD7580371E8D953L, 0x7F6ED89562764E30L, + 0xB15926FF596F003DL, 0x9F65293DA8C5D6B9L, 0x6ECEF04DD690F84CL, 0x4782275FFF33AF88L, + 0xE41433083F820801L, 0xFD0DFE409A1AF9B5L, 0x4325A3342CDB396BL, 0x8AE77E62B301B252L, + 0xC36F9E9F6655615AL, 0x85455A2D92D32C09L, 0xF2C7DEA949477485L, 0x63CFB4C133A39EBAL, + 0x83B040CC6EBC5462L, 0x3B9454C8FDB326B0L, 0x56F56A9E87FFD78CL, 0x2DC2940D99F42BC6L, + 0x98F7DF096B096E2DL, 0x19A6E01E3AD852BFL, 0x42A99CCBDBD4B40BL, 0xA59998AF45E9C559L, + 0x366295E807D93186L, 0x6B48181BFAA1F773L, 0x1FEC57E2157A0A1DL, 0x4667446AF6201AD5L, + 0xE615EBCACFB0F075L, 0xB8F31F4F68290778L, 0x22713ED6CE22D11EL, 0x3057C1A72EC3C93BL, + 0xCB46ACC37C3F1F2FL, 0xDBB893FD02AAF50EL, 0x331FD92E600B9FCFL, 0xA498F96148EA3AD6L, + 0xA8D8426E8B6A83EAL, 0xA089B274B7735CDCL, 0x87F6B3731E524A11L, 0x118808E5CBC96749L, + 0x9906E4C7B19BD394L, 0xAFED7F7E9B24A20CL, 0x6509EADEEB3644A7L, 0x6C1EF1D3E8EF0EDEL, + 0xB9C97D43E9798FB4L, 0xA2F2D784740C28A3L, 0x7B8496476197566FL, 0x7A5BE3E6B65F069DL, + 0xF96330ED78BE6F10L, 0xEEE60DE77A076A15L, 0x2B4BEE4AA08B9BD0L, 0x6A56A63EC7B8894EL, + 0x02121359BA34FEF4L, 0x4CBF99F8283703FCL, 0x398071350CAF30C8L, 0xD0A77A89F017687AL, + 0xF1C1A9EB9E423569L, 0x8C7976282DEE8199L, 0x5D1737A5DD1F7ABDL, 0x4F53433C09A9FA80L, + 0xFA8B0C53DF7CA1D9L, 0x3FD9DCBC886CCB77L, 0xC040917CA91B4720L, 0x7DD00142F9D1DCDFL, + 0x8476FC1D4F387B58L, 0x23F8E7C5F3316503L, 0x032A2244E7E37339L, 0x5C87A5D750F5A74BL, + 0x082B4CC43698992EL, 0xDF917BECB858F63CL, 0x3270B8FC5BF86DDAL, 0x10AE72BB29B5DD76L, + 0x576AC94E7700362BL, 0x1AD112DAC61EFB8FL, 0x691BC30EC5FAA427L, 0xFF246311CC327143L, + 0x3142368E30E53206L, 0x71380E31E02CA396L, 0x958D5C960AAD76F1L, 0xF8D6F430C16DA536L, + 0xC8FFD13F1BE7E1D2L, 0x7578AE66004DDBE1L, 0x05833F01067BE646L, 0xBB34B5AD3BFE586DL, + 0x095F34C9A12B97F0L, 0x247AB64525D60CA8L, 0xDCDBC6F3017477D1L, 0x4A2E14D4DECAD24DL, + 0xBDB5E6D9BE0A1EEBL, 0x2A7E70F7794301ABL, 0xDEF42D8A270540FDL, 0x01078EC0A34C22C1L, + 0xE5DE511AF4C16387L, 0x7EBB3A52BD9A330AL, 0x77697857AA7D6435L, 0x004E831603AE4C32L, + 0xE7A21020AD78E312L, 0x9D41A70C6AB420F2L, 0x28E06C18EA1141E6L, 0xD2B28CBD984F6B28L, + 0x26B75F6C446E9D83L, 0xBA47568C4D418D7FL, 0xD80BADBFE6183D8EL, 0x0E206D7F5F166044L, + 0xE258A43911CBCA3EL, 0x723A1746B21DC0BCL, 0xC7CAA854F5D7CDD3L, 0x7CAC32883D261D9CL, + 0x7690C26423BA942CL, 0x17E55524478042B8L, 0xE0BE477656A2389FL, 0x4D289B5E67AB2DA0L, + 0x44862B9C8FBBFD31L, 0xB47CC8049D141365L, 0x822C1B362B91C793L, 0x4EB14655FB13DFD8L, + 0x1ECBBA0714E2A97BL, 0x6143459D5CDE5F14L, 0x53A8FBF1D5F0AC89L, 0x97EA04D81C5E5B00L, + 0x622181A8D4FDB3F3L, 0xE9BCD341572A1208L, 0x1411258643CCE58AL, 0x9144C5FEA4C6E0A4L, + 0x0D33D06565CF620FL, 0x54A48D489F219CA1L, 0xC43E5EAC6D63C821L, 0xA9728B3A72770DAFL, + 0xD7934E7B20DF87EFL, 0xE35503B61A3E86E5L, 0xCAE321FBC819D504L, 0x129A50B3AC60BFA6L, + 0xCD5E68EA7E9FB6C3L, 0xB01C90199483B1C7L, 0x3DE93CD5C295376CL, 0xAED52EDF2AB9AD13L, + 0x2E60F512C0A07884L, 0xBC3D86A3E36210C9L, 0x35269D9B163951CEL, 0x0C7D6E2AD0CDB5FAL, + 0x59E86297D87F5733L, 0x298EF221898DB0E7L, 0x55000029D1A5AA7EL, 0x8BC08AE1B5061B45L, + 0xC2C31C2B6C92703AL, 0x94CC596BAF25EF42L, 0x0A1D73DB22540456L, 0x04B6A0F9D9C4179AL, + 0xEFFDAFA2AE3D3C60L, 0xF7C8075BB49496C4L, 0x9CC5C7141D1CD4E3L, 0x78BD1638218E5534L, + 0xB2F11568F850246AL, 0xEDFABCFA9502BC29L, 0x796CE5F2DA23051BL, 0xAAE128B0DC93537CL, + 0x3A493DA0EE4B29AEL, 0xB5DF6B2C416895D7L, 0xFCABBD25122D7F37L, 0x70810B58105DC4B1L, + 0xE10FDD37F7882A90L, 0x524DCAB5518A3F5CL, 0x3C9E85878451255BL, 0x4029828119BD34E2L, + 0x74A05B6F5D3CECCBL, 0xB610021542E13ECAL, 0x0FF979D12F59E2ACL, 0x6037DA27E4F9CC50L, + 0x5E92975A0DF1847DL, 0xD66DE190D3E623FEL, 0x5032D6B87B568048L, 0x9A36B7CE8235216EL, + 0x80272A7A24F64B4AL, 0x93EFED8B8C6916F7L, 0x37DDBFF44CCE1555L, 0x4B95DB5D4B99BD25L, + 0x92D3FDA169812FC0L, 0xFB1A4A9A90660BB6L, 0x730C196946A4B9B2L, 0x81E289AA7F49DA68L, + 0x64669A0F83B1A05FL, 0x27B3FF7D9644F48BL, 0xCC6B615C8DB675B3L, 0x674F20B9BCEBBE95L, + 0x6F31238275655982L, 0x5AE488713E45CF05L, 0xBF619F9954C21157L, 0xEABAC46040A8EAE9L, + 0x454C6FE9F2C0C1CDL, 0x419CF6496412691CL, 0xD3DC3BEF265B0F70L, 0x6D0E60F5C3578A9EL + }; + + /** S-Box T4. */ + private static final long[] T4 = { + 0x5B0E608526323C55L, 0x1A46C1A9FA1B59F5L, 0xA9E245A17C4C8FFAL, 0x65CA5159DB2955D7L, + 0x05DB0A76CE35AFC2L, 0x81EAC77EA9113D45L, 0x528EF88AB6AC0A0DL, 0xA09EA253597BE3FFL, + 0x430DDFB3AC48CD56L, 0xC4B3A67AF45CE46FL, 0x4ECECFD8FBE2D05EL, 0x3EF56F10B39935F0L, + 0x0B22D6829CD619C6L, 0x17FD460A74DF2069L, 0x6CF8CC8E8510ED40L, 0xD6C824BF3A6ECAA7L, + 0x61243D581A817049L, 0x048BACB6BBC163A2L, 0xD9A38AC27D44CC32L, 0x7FDDFF5BAAF410ABL, + 0xAD6D495AA804824BL, 0xE1A6A74F2D8C9F94L, 0xD4F7851235DEE8E3L, 0xFD4B7F886540D893L, + 0x247C20042AA4BFDAL, 0x096EA1C517D1327CL, 0xD56966B4361A6685L, 0x277DA5C31221057DL, + 0x94D59893A43ACFF7L, 0x64F0C51CCDC02281L, 0x3D33BCC4FF6189DBL, 0xE005CB184CE66AF1L, + 0xFF5CCD1D1DB99BEAL, 0xB0B854A7FE42980FL, 0x7BD46A6A718D4B9FL, 0xD10FA8CC22A5FD8CL, + 0xD31484952BE4BD31L, 0xC7FA975FCB243847L, 0x4886ED1E5846C407L, 0x28CDDB791EB70B04L, + 0xC2B00BE2F573417FL, 0x5C9590452180F877L, 0x7A6BDDFFF370EB00L, 0xCE509E38D6D9D6A4L, + 0xEBEB0F00647FA702L, 0x1DCC06CF76606F06L, 0xE4D9F28BA286FF0AL, 0xD85A305DC918C262L, + 0x475B1D8732225F54L, 0x2D4FB51668CCB5FEL, 0xA679B9D9D72BBA20L, 0x53841C0D912D43A5L, + 0x3B7EAA48BF12A4E8L, 0x781E0E47F22F1DDFL, 0xEFF20CE60AB50973L, 0x20D261D19DFFB742L, + 0x16A12B03062A2E39L, 0x1960EB2239650495L, 0x251C16FED50EB8B8L, 0x9AC0C330F826016EL, + 0xED152665953E7671L, 0x02D63194A6369570L, 0x5074F08394B1C987L, 0x70BA598C90B25CE1L, + 0x794A15810B9742F6L, 0x0D5925E9FCAF8C6CL, 0x3067716CD868744EL, 0x910AB077E8D7731BL, + 0x6A61BBDB5AC42F61L, 0x93513EFBF0851567L, 0xF494724B9E83E9D5L, 0xE887E1985C09648DL, + 0x34B1D3C675370CFDL, 0xDC35E433BC0D255DL, 0xD0AAB84234131BE0L, 0x08042A50B48B7EAFL, + 0x9997C4EE44A3AB35L, 0x829A7B49201799D0L, 0x263B8307B7C54441L, 0x752F95F4FD6A6CA6L, + 0x927217402C08C6E5L, 0x2A8AB754A795D9EEL, 0xA442F7552F72943DL, 0x2C31334E19781208L, + 0x4FA98D7CEAEE6291L, 0x55C3862F665DB309L, 0xBD0610175D53B1F3L, 0x46FE6CB840413F27L, + 0x3FE03792DF0CFA59L, 0xCFE700372EB85E8FL, 0xA7BE29E7ADBCE118L, 0xE544EE5CDE8431DDL, + 0x8A781B1B41F1873EL, 0xA5C94C78A0D2F0E7L, 0x39412E2877B60728L, 0xA1265EF3AFC9A62CL, + 0xBCC2770C6A2506C5L, 0x3AB66DD5DCE1CE12L, 0xE65499D04A675B37L, 0x7D8F523481BFD216L, + 0x0F6F64FCEC15F389L, 0x74EFBE618B5B13C8L, 0xACDC82B714273E1DL, 0xDD40BFE003199D17L, + 0x37E99257E7E061F8L, 0xFA52626904775AAAL, 0x8BBBF63A463D56F9L, 0xF0013F1543A26E64L, + 0xA8307E9F879EC898L, 0xCC4C27A4150177CCL, 0x1B432F2CCA1D3348L, 0xDE1D1F8F9F6FA013L, + 0x606602A047A7DDD6L, 0xD237AB64CC1CB2C7L, 0x9B938E7225FCD1D3L, 0xEC4E03708E0FF476L, + 0xFEB2FBDA3D03C12DL, 0xAE0BCED2EE43889AL, 0x22CB8923EBFB4F43L, 0x69360D013CF7396DL, + 0x855E3602D2D4E022L, 0x073805BAD01F784CL, 0x33E17A133852F546L, 0xDF4874058AC7B638L, + 0xBA92B29C678AA14AL, 0x0CE89FC76CFAADCDL, 0x5F9D4E0908339E34L, 0xF1AFE9291F5923B9L, + 0x6E3480F60F4A265FL, 0xEEBF3A2AB29B841CL, 0xE21938A88F91B4ADL, 0x57DFEFF845C6D3C3L, + 0x2F006B0BF62CAAF2L, 0x62F479EF6F75EE78L, 0x11A55AD41C8916A9L, 0xF229D29084FED453L, + 0x42F1C27B16B000E6L, 0x2B1F76749823C074L, 0x4B76ECA3C2745360L, 0x8C98F463B91691BDL, + 0x14BCC93CF1ADE66AL, 0x8885213E6D458397L, 0x8E177DF0274D4711L, 0xB49B73B5503F2951L, + 0x10168168C3F96B6BL, 0x0E3D963B63CAB0AEL, 0x8DFC4B5655A1DB14L, 0xF789F1356E14DE5CL, + 0x683E68AF4E51DAC1L, 0xC9A84F9D8D4B0FD9L, 0x3691E03F52A0F9D1L, 0x5ED86E46E1878E80L, + 0x3C711A0E99D07150L, 0x5A0865B20C4E9310L, 0x56FBFC1FE4F0682EL, 0xEA8D5DE3105EDF9BL, + 0x71ABFDB12379187AL, 0x2EB99DE1BEE77B9CL, 0x21ECC0EA33CF4523L, 0x59A4D7521805C7A1L, + 0x3896F5EB56AE7C72L, 0xAA638F3DB18F75DCL, 0x9F39358DABE9808EL, 0xB7DEFA91C00B72ACL, + 0x6B5541FD62492D92L, 0x6DC6DEE8F92E4D5BL, 0x353F57ABC4BEEA7EL, 0x735769D6DA5690CEL, + 0x0A234AA642391484L, 0xF6F9508028F80D9DL, 0xB8E319A27AB3F215L, 0x31AD9C1151341A4DL, + 0x773C22A57BEF5805L, 0x45C7561A07968633L, 0xF913DA9E249DBE36L, 0xDA652D9B78A64C68L, + 0x4C27A97F3BC334EFL, 0x76621220E66B17F4L, 0x967743899ACD7D0BL, 0xF3EE5BCAE0ED6782L, + 0x409F753600C879FCL, 0x06D09A39B5926DB6L, 0x6F83AEB0317AC588L, 0x01E6CA4A86381F21L, + 0x66FF3462D19F3025L, 0x72207C24DDFD3BFBL, 0x4AF6B6D3E2ECE2EBL, 0x9C994DBEC7EA08DEL, + 0x49ACE597B09A8BC4L, 0xB38C4766CF0797BAL, 0x131B9373C57C2A75L, 0xB1822CCE61931E58L, + 0x9D7555B909BA1C0CL, 0x127FAFDD937D11D2L, 0x29DA3BADC66D92E4L, 0xA2C1D57154C2ECBCL, + 0x58C5134D82F6FE24L, 0x1C3AE3515B62274FL, 0xE907C82E01CB8126L, 0xF8ED091913E37FCBL, + 0x3249D8F9C80046C9L, 0x80CF9BEDE388FB63L, 0x1881539A116CF19EL, 0x5103F3F76BD52457L, + 0x15B7E6F5AE47F7A8L, 0xDBD7C6DED47E9CCFL, 0x44E55C410228BB1AL, 0xB647D4255EDB4E99L, + 0x5D11882BB8AAFC30L, 0xF5098BBB29D3212AL, 0x8FB5EA14E90296B3L, 0x677B942157DD025AL, + 0xFB58E7C0A390ACB5L, 0x89D3674C83BD4A01L, 0x9E2DA4DF4BF3B93BL, 0xFCC41E328CAB4829L, + 0x03F38C96BA582C52L, 0xCAD1BDBD7FD85DB2L, 0xBBB442C16082AE83L, 0xB95FE86BA5DA9AB0L, + 0xB22E04673771A93FL, 0x845358C9493152D8L, 0xBE2A488697B4541EL, 0x95A2DC2DD38E6966L, + 0xC02C11AC923C852BL, 0x2388B1990DF2A87BL, 0x7C8008FA1B4F37BEL, 0x1F70D0C84D54E503L, + 0x5490ADEC7ECE57D4L, 0x002B3C27D9063A3AL, 0x7EAEA3848030A2BFL, 0xC602326DED2003C0L, + 0x83A7287D69A94086L, 0xC57A5FCB30F57A8AL, 0xB56844E479EBE779L, 0xA373B40F05DCBCE9L, + 0xD71A786E88570EE2L, 0x879CBACDBDE8F6A0L, 0x976AD1BCC164A32FL, 0xAB21E25E9666D78BL, + 0x901063AAE5E5C33CL, 0x9818B34448698D90L, 0xE36487AE3E1E8ABBL, 0xAFBDF931893BDCB4L, + 0x6345A0DC5FBBD519L, 0x8628FE269B9465CAL, 0x1E5D01603F9C51ECL, 0x4DE44006A15049B7L, + 0xBF6C70E5F776CBB1L, 0x411218F2EF552BEDL, 0xCB0C0708705A36A3L, 0xE74D14754F986044L, + 0xCD56D9430EA8280EL, 0xC12591D7535F5065L, 0xC83223F1720AEF96L, 0xC3A0396F7363A51FL + }; + protected static Boolean valid; // The cached self-test result. + + protected long a, b, c; // The context. + + public Tiger192() {super("tiger192", HASH_SIZE, BLOCK_SIZE);} + + /** + * Private copying constructor for cloning. + * + * @param that The instance being cloned. + */ + private Tiger192(Tiger192 that) { + this(); + this.a = that.a; + this.b = that.b; + this.c = that.c; + this.count = that.count; + this.buffer = (that.buffer != null) ? (byte[]) that.buffer.clone() : null; + } + + // Instance methods implementing BaseHash. + // ----------------------------------------------------------------------- + + public Object clone() { + return new Tiger192(this); + } +} + +//-------------------------------------------------------------------------- +//$Id: Tiger.java,v 1.1 2003/03/22 03:09:57 rsdio Exp $ +// +//Copyright (C) 2003, Free Software Foundation, Inc. +// +//This file is part of GNU Crypto. +// +//GNU Crypto 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, or (at your option) +//any later version. +// +//GNU Crypto 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; see the file COPYING. If not, write to the +// +// Free Software Foundation Inc., +// 59 Temple Place - Suite 330, +// Boston, MA 02111-1307 +// USA +// +//Linking this library statically or dynamically with other modules is +//making a combined work based on this library. Thus, the terms and +//conditions of the GNU General Public License cover the whole +//combination. +// +//As a special exception, the copyright holders of this library give +//you permission to link this library with independent modules to +//produce an executable, regardless of the license terms of these +//independent modules, and to copy and distribute the resulting +//executable under terms of your choice, provided that you also meet, +//for each linked independent module, the terms and conditions of the +//license of that module. An independent module is a module which is +//not derived from or based on this library. If you modify this +//library, you may extend this exception to your version of the +//library, but you are not obligated to do so. If you do not wish to +//do so, delete this exception statement from your version. +// +//-------------------------------------------------------------------------- + +/** +* The Tiger message digest. Tiger was designed by Ross Anderson and Eli +* Biham, with the goal of producing a secure, fast hash function that +* performs especially well on next-generation 64-bit architectures, but +* is still efficient on 32- and 16-bit architectures. +* +*

Tiger processes data in 512-bit blocks and produces a 192-bit +* digest.

+* +*

References:

+*
    +*
  1. Tiger: A +* Fast New Hash Function, Ross Anderson and Eli Biham.
  2. +*
+* +* @version $Revision: 1.1 $ +*/ +abstract class BaseHash { + /** The canonical name prefix of the hash. */ + protected String name; + + /** The hash (output) size in bytes. */ + protected int hashSize; + + /** The hash (inner) block size in bytes. */ + protected int blockSize; + + /** Number of bytes processed so far. */ + protected long count; + + /** Temporary input buffer. */ + protected byte[] buffer; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** + *

Trivial constructor for use by concrete subclasses.

+ * + * @param name the canonical name prefix of this instance. + * @param hashSize the block size of the output in bytes. + * @param blockSize the block size of the internal transform. + */ + protected BaseHash(String name, int hashSize, int blockSize) { + super(); + + this.name = name; + this.hashSize = hashSize; + this.blockSize = blockSize; + this.buffer = new byte[blockSize]; + + resetContext(); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // IMessageDigest interface implementation --------------------------------- + + public String name() { + return name; + } + + public int hashSize() { + return hashSize; + } + + public int blockSize() { + return blockSize; + } + + public void update(byte b) { + // compute number of bytes still unhashed; ie. present in buffer + int i = (int)(count % blockSize); + count++; + buffer[i] = b; + if (i == (blockSize - 1)) { + transform(buffer, 0); + } + } + + public void update(byte[] b, int offset, int len) { + int n = (int)(count % blockSize); + count += len; + int partLen = blockSize - n; + int i = 0; + + if (len >= partLen) { + System.arraycopy(b, offset, buffer, n, partLen); + transform(buffer, 0); + for (i = partLen; i + blockSize - 1 < len; i+= blockSize) { + transform(b, offset + i); + } + n = 0; + } + + if (i < len) { + System.arraycopy(b, offset + i, buffer, n, len - i); + } + } + + public byte[] digest() { + byte[] tail = padBuffer(); // pad remaining bytes in buffer + update(tail, 0, tail.length); // last transform of a message + byte[] result = getResult(); // make a result out of context + + reset(); // reset this instance for future re-use + + return result; + } + + public void reset() { // reset this instance for future re-use + count = 0L; + for (int i = 0; i < blockSize; ) { + buffer[i++] = 0; + } + + resetContext(); + } + + // methods to be implemented by concrete subclasses ------------------------ + + public abstract Object clone(); + + /** + *

Returns the byte array to use as padding before completing a hash + * operation.

+ * + * @return the bytes to pad the remaining bytes in the buffer before + * completing a hash operation. + */ + protected abstract byte[] padBuffer(); + + /** + *

Constructs the result from the contents of the current context.

+ * + * @return the output of the completed hash operation. + */ + protected abstract byte[] getResult(); + + /** Resets the instance for future re-use. */ + protected abstract void resetContext(); + + /** + *

The block digest transformation per se.

+ * + * @param in the blockSize long block, as an array of bytes to digest. + * @param offset the index where the data to digest is located within the + * input buffer. + */ + protected abstract void transform(byte[] in, int offset); +} + +//---------------------------------------------------------------------------- +//$Id: BaseHash.java,v 1.8 2002/11/07 17:17:45 raif Exp $ +// +//Copyright (C) 2001, 2002, Free Software Foundation, Inc. +// +//This file is part of GNU Crypto. +// +//GNU Crypto 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, or (at your option) +//any later version. +// +//GNU Crypto 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; see the file COPYING. If not, write to the +// +// Free Software Foundation Inc., +// 59 Temple Place - Suite 330, +// Boston, MA 02111-1307 +// USA +// +//Linking this library statically or dynamically with other modules is +//making a combined work based on this library. Thus, the terms and +//conditions of the GNU General Public License cover the whole +//combination. +// +//As a special exception, the copyright holders of this library give +//you permission to link this library with independent modules to +//produce an executable, regardless of the license terms of these +//independent modules, and to copy and distribute the resulting +//executable under terms of your choice, provided that you also meet, +//for each linked independent module, the terms and conditions of the +//license of that module. An independent module is a module which is +//not derived from or based on this library. If you modify this +//library, you may extend this exception to your version of the +//library, but you are not obligated to do so. If you do not wish to +//do so, delete this exception statement from your version. +//---------------------------------------------------------------------------- +/** + *

A base abstract class to facilitate hash implementations.

+ * + * @version $Revision: 1.8 $ + */ diff --git a/100_core/src/gplx/core/security/Hash_algo__tth_192__tst.java b/100_core/src/gplx/core/security/Hash_algo__tth_192__tst.java new file mode 100644 index 000000000..b7fea8c3e --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo__tth_192__tst.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.core.consoles.*; import gplx.core.ios.*; /*IoStream*/ +public class Hash_algo__tth_192__tst { // REF: http://open-content.net/specs/draft-jchapweske-thex-02.html; DC++ 0.698 + private final Hash_algo__fxt fxt = new Hash_algo__fxt(Hash_algo_.New__tth_192()); + @Test public void Empty() {fxt.Test__hash("LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ", "");} + @Test public void Null__1() {fxt.Test__hash("VK54ZIEEVTWNAUI5D5RDFIL37LX2IQNSTAXFKSA", "\0");} + @Test public void ab() {fxt.Test__hash("XQXRSGMB3PSN2VGZYJMNJG6SOOQ3JIGQHD2I6PQ", "ab");} + @Test public void abc() {fxt.Test__hash("ASD4UJSEH5M47PDYB46KBTSQTSGDKLBHYXOMUIA", "abc");} + @Test public void abde() {fxt.Test__hash("SQF2PFTVIFRR5KJSI45IDENXMB43NI7EIXYGHGI", "abcd");} + @Test public void abcde() {fxt.Test__hash("SKGLNP5WV7ZUMF6IUK5CYXBE3PI4C6PHWNVM2YQ", "abcde");} + @Test public void abcdefghi() {fxt.Test__hash("RUIKHZFO4NIY6NNUHJMAC2I26U3U65FZWCO3UFY", "abcdefghi");} + // @Test + public void a__x_1024() {fxt.Test__hash("L66Q4YVNAFWVS23X2HJIRA5ZJ7WXR3F26RSASFA", String_.Repeat("A", 1024));} + // @Test + public void a__x_1025() {fxt.Test__hash("PZMRYHGY6LTBEH63ZWAHDORHSYTLO4LEFUIKHWY", String_.Repeat("A", 1025));} + // @Test + public void A__Pow27() {fxt.Test__hash("QNIJO36QDIQREUT3HWK4MDVKD2T6OENAEKYADTQ", String_.Repeat("A", (int)Math_.Pow(2, 27))); + } +} diff --git a/100_core/src/gplx/core/security/Hash_algo__tth_192_tree_tst.java b/100_core/src/gplx/core/security/Hash_algo__tth_192_tree_tst.java new file mode 100644 index 000000000..53c0cb076 --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_algo__tth_192_tree_tst.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Hash_algo__tth_192_tree_tst { + @Test public void CalcRecursiveHalves() { + tst_CalcRecursiveHalves(129, 128); + tst_CalcRecursiveHalves(128, 127); + tst_CalcRecursiveHalves(100, 99); + tst_CalcRecursiveHalves(20, 19); + tst_CalcRecursiveHalves(6, 5); + tst_CalcRecursiveHalves(5, 4); + tst_CalcRecursiveHalves(4, 3); + tst_CalcRecursiveHalves(3, 2); + tst_CalcRecursiveHalves(2, 1); + tst_CalcRecursiveHalves(1, 0); + tst_CalcRecursiveHalves(0, 0); + } + @Test public void CalcWorkUnits() { + tst_CalcWorkUnits(101, 21); // leafs; 10 full, 1 part (+11) -> reduce 11 to 5+1 (+5) -> reduce 6 to 3 (+3) -> reduce 3 to 1+1 (+1) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(100, 19); // leafs; 10 full (+10) -> reduce 10 to 5 (+5) -> reduce 5 to 2+1 (+2) -> reduce 3 to 1+1 (+1) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(30, 5); // leafs; 3 full (+3) -> reduce 3 to 1+1 (+1) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(11, 3); // leafs: 1 full, 1 part (+2) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(10, 1); + tst_CalcWorkUnits(9, 1); + tst_CalcWorkUnits(1, 1); + tst_CalcWorkUnits(0, 1); + } + void tst_CalcWorkUnits(int length, int expd) { + Hash_algo__tth_192 algo = new Hash_algo__tth_192(); algo.BlockSize_set(10); + int actl = algo.CalcWorkUnits(length); + Tfds.Eq(expd, actl); + } + void tst_CalcRecursiveHalves(int val, int expd) { + int actl = CalcRecursiveHalvesMock(val); + Tfds.Eq(expd, actl); + } + int CalcRecursiveHalvesMock(int val) { + if (val <= 1) return 0; + int rv = 0; + while (true) { + int multiple = val / 2; + int remainder = val % 2; + rv += multiple; + val = multiple + remainder; + if (val == 1) + return remainder == 0 ? rv : ++rv; + } + } +} diff --git a/100_core/src/gplx/core/security/Hash_console_wtr_tst.java b/100_core/src/gplx/core/security/Hash_console_wtr_tst.java new file mode 100644 index 000000000..65c82a120 --- /dev/null +++ b/100_core/src/gplx/core/security/Hash_console_wtr_tst.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.security; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.core.consoles.*; import gplx.core.ios.streams.*; /*IoStream*/ +public class Hash_console_wtr_tst { + @Before public void setup() { + Hash_algo__tth_192 algo = new Hash_algo__tth_192(); + algo.BlockSize_set(10); + calc = algo; + } + @Test public void Basic() { + tst_Status(10, stringAry_(" - hash: 100%")); + tst_Status(11, stringAry_(" - hash: 66%")); + tst_Status(30, stringAry_(" - hash: 40%", " - hash: 60%", " - hash: 100%")); + } + void tst_Status(int count, String[] expdWritten) { + Console_adp__mem dialog = Console_adp_.Dev(); + String data = String_.Repeat("A", count); + IoStream stream = IoStream_.mem_txt_(Io_url_.Empty, data); + calc.Hash_stream_as_str(dialog, stream); + String[] actlWritten = dialog.Written().To_str_ary(); + Tfds.Eq_ary(actlWritten, expdWritten); + } + String[] stringAry_(String... ary) {return ary;} + Hash_algo calc; +} diff --git a/100_core/src/gplx/core/stores/DataRdr.java b/100_core/src/gplx/core/stores/DataRdr.java new file mode 100644 index 000000000..b9e900b89 --- /dev/null +++ b/100_core/src/gplx/core/stores/DataRdr.java @@ -0,0 +1,51 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; +public interface DataRdr extends SrlMgr, Rls_able { + String NameOfNode(); String To_str(); + Io_url Uri(); void Uri_set(Io_url s); + Hash_adp EnvVars(); + boolean Parse(); void Parse_set(boolean v); + + int FieldCount(); + String KeyAt(int i); + Object ReadAt(int i); + Keyval KeyValAt(int i); + + Object Read(String key); + String ReadStr(String key); String ReadStrOr(String key, String or); + byte[] ReadBryByStr(String key); byte[] ReadBryByStrOr(String key, byte[] or); + byte[] ReadBry(String key); byte[] ReadBryOr(String key, byte[] or); + char ReadChar(String key); char ReadCharOr(String key, char or); + int ReadInt(String key); int ReadIntOr(String key, int or); + boolean ReadBool(String key); boolean ReadBoolOr(String key, boolean or); + long ReadLong(String key); long ReadLongOr(String key, long or); + double ReadDouble(String key); double ReadDoubleOr(String key, double or); + float ReadFloat(String key); float ReadFloatOr(String key, float or); + byte ReadByte(String key); byte ReadByteOr(String key, byte or); + Decimal_adp ReadDecimal(String key); Decimal_adp ReadDecimalOr(String key, Decimal_adp or); + DateAdp ReadDate(String key); DateAdp ReadDateOr(String key, DateAdp or); + gplx.core.ios.streams.Io_stream_rdr ReadRdr(String key); + + boolean MoveNextPeer(); + DataRdr Subs(); + DataRdr Subs_byName(String name); + DataRdr Subs_byName_moveFirst(String name); + void XtoStr_gfml(String_bldr sb); +} diff --git a/100_core/src/gplx/core/stores/DataRdr_.java b/100_core/src/gplx/core/stores/DataRdr_.java new file mode 100644 index 000000000..558b24595 --- /dev/null +++ b/100_core/src/gplx/core/stores/DataRdr_.java @@ -0,0 +1,74 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; +public class DataRdr_ { + public static final DataRdr Null = new DataRdr_null(); + public static DataRdr as_(Object obj) {return obj instanceof DataRdr ? (DataRdr)obj : null;} + public static DataRdr cast(Object obj) {try {return (DataRdr)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, DataRdr.class, obj);}} + + public static Object Read_1st_row_and_1st_fld(DataRdr rdr) { + try {return rdr.MoveNextPeer() ? rdr.ReadAt(0) : null;} + finally {rdr.Rls();} + } + +} +class DataRdr_null implements DataRdr { + public String NameOfNode() {return To_str();} public String To_str() {return "<< NULL READER >>";} + public boolean Type_rdr() {return true;} + public Hash_adp EnvVars() {return Hash_adp_.Noop;} + public Io_url Uri() {return Io_url_.Empty;} public void Uri_set(Io_url s) {} + public boolean Parse() {return parse;} public void Parse_set(boolean v) {parse = v;} private boolean parse; + public int FieldCount() {return 0;} + public String KeyAt(int i) {return To_str();} + public Object ReadAt(int i) {return null;} + public Keyval KeyValAt(int i) {return Keyval_.new_(this.KeyAt(i), this.ReadAt(i));} + public Object Read(String name) {return null;} + public String ReadStr(String key) {return String_.Empty;} public String ReadStrOr(String key, String or) {return or;} + public byte[] ReadBryByStr(String key) {return Bry_.Empty;} public byte[] ReadBryByStrOr(String key, byte[] or) {return or;} + public byte[] ReadBry(String key) {return Bry_.Empty;} public byte[] ReadBryOr(String key, byte[] or) {return or;} + public char ReadChar(String key) {return Char_.Null;} public char ReadCharOr(String key, char or) {return or;} + public int ReadInt(String key) {return Int_.Min_value;} public int ReadIntOr(String key, int or) {return or;} + public boolean ReadBool(String key) {return false;} public boolean ReadBoolOr(String key, boolean or) {return or;} + public long ReadLong(String key) {return Long_.Min_value;} public long ReadLongOr(String key, long or) {return or;} + public double ReadDouble(String key) {return Double_.NaN;} public double ReadDoubleOr(String key, double or) {return or;} + public float ReadFloat(String key) {return Float_.NaN;} public float ReadFloatOr(String key, float or) {return or;} + public byte ReadByte(String key) {return Byte_.Min_value;} public byte ReadByteOr(String key, byte or) {return or;} + public Decimal_adp ReadDecimal(String key) {return Decimal_adp_.Zero;}public Decimal_adp ReadDecimalOr(String key, Decimal_adp or) {return or;} + public DateAdp ReadDate(String key) {return DateAdp_.MinValue;} public DateAdp ReadDateOr(String key, DateAdp or) {return or;} + public gplx.core.ios.streams.Io_stream_rdr ReadRdr(String key) {return gplx.core.ios.streams.Io_stream_rdr_.Noop;} + public boolean MoveNextPeer() {return false;} + public DataRdr Subs() {return this;} + public DataRdr Subs_byName(String name) {return this;} + public DataRdr Subs_byName_moveFirst(String name) {return this;} + public Object StoreRoot(SrlObj root, String key) {return null;} + public boolean SrlBoolOr(String key, boolean v) {return v;} + public byte SrlByteOr(String key, byte v) {return v;} + public int SrlIntOr(String key, int or) {return or;} + public long SrlLongOr(String key, long or) {return or;} + public String SrlStrOr(String key, String or) {return or;} + public DateAdp SrlDateOr(String key, DateAdp or) {return or;} + public Decimal_adp SrlDecimalOr(String key, Decimal_adp or) {return or;} + public double SrlDoubleOr(String key, double or) {return or;} + public Object SrlObjOr(String key, Object or) {return or;} + public void SrlList(String key, List_adp list, SrlObj proto, String itmKey) {} + public void TypeKey_(String v) {} + public void XtoStr_gfml(String_bldr sb) {sb.Add_str_w_crlf("NULL:;");} + public SrlMgr SrlMgr_new(Object o) {return this;} + public void Rls() {} +} diff --git a/100_core/src/gplx/core/stores/DataRdr_base.java b/100_core/src/gplx/core/stores/DataRdr_base.java new file mode 100644 index 000000000..8b6427cd1 --- /dev/null +++ b/100_core/src/gplx/core/stores/DataRdr_base.java @@ -0,0 +1,215 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; import gplx.core.type_xtns.*; +import gplx.core.ios.streams.*; +public abstract class DataRdr_base implements SrlMgr { + public boolean Parse() {return parse;} public void Parse_set(boolean v) {parse = v;} private boolean parse; + public Io_url Uri() {return uri;} public void Uri_set(Io_url s) {uri = s;} Io_url uri = Io_url_.Empty; + public abstract String NameOfNode(); + public boolean Type_rdr() {return true;} + public Hash_adp EnvVars() {return envVars;} Hash_adp envVars = Hash_adp_.New(); + public abstract Object Read(String key); + public abstract int FieldCount(); + public abstract String KeyAt(int i); + public abstract Object ReadAt(int i); + @gplx.Virtual public Keyval KeyValAt(int idx) {return Keyval_.new_(this.KeyAt(idx), ReadAt(idx));} + public String ReadStr(String key) { + Object val = Read(key); + try {return (String)val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(String.class, key, val, exc);} + } + public String ReadStrOr(String key, String or) { + Object val = Read(key); if (val == null) return or; + try {return (String)val;} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, String.class, key, val, or); return or;} + } + public byte[] ReadBryByStr(String key) {return Bry_.new_u8(ReadStr(key));} + public byte[] ReadBryByStrOr(String key, byte[] or) { + Object val = Read(key); if (val == null) return or; + try {return Bry_.new_u8((String)val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, byte[].class, key, val, or); return or;} + } + @gplx.Virtual public void SrlList(String key, List_adp list, SrlObj proto, String itmKey) { + list.Clear(); + DataRdr subRdr = this.Subs_byName_moveFirst(key); // collection node + subRdr = subRdr.Subs(); + while (subRdr.MoveNextPeer()) { + SrlObj itm = proto.SrlObj_New(null); + itm.SrlObj_Srl(subRdr); + list.Add(itm); + } + } + @gplx.Virtual public Object StoreRoot(SrlObj root, String key) { + SrlObj clone = root.SrlObj_New(null); + clone.SrlObj_Srl(this); + return clone; + } + public abstract DataRdr Subs_byName_moveFirst(String name); + + public int ReadInt(String key) { + Object val = Read(key); + try {return (parse) ? Int_.parse(String_.as_(val)) : Int_.cast(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(int.class, key, val, exc);} + } + public int ReadIntOr(String key, int or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Int_.parse(String_.as_(val)) : Int_.cast(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, int.class, key, val, or); return or;} + } + public long ReadLongOr(String key, long or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Long_.parse(String_.as_(val)) : Long_.cast(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, long.class, key, val, or); return or;} + } + @gplx.Virtual public boolean ReadBool(String key) { + Object val = Read(key); + try {return (parse) ? Bool_.cast(BoolClassXtn.Instance.ParseOrNull(String_.as_(val))) : Bool_.cast(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(boolean.class, key, val, exc);} + } + @gplx.Virtual public boolean ReadBoolOr(String key, boolean or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Bool_.parse(String_.as_(val)) : Bool_.cast(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, boolean.class, key, val, or); return or;} + } + public long ReadLong(String key) { + Object val = Read(key); + try {return (parse) ? Long_.parse(String_.as_(val)) : Long_.cast(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(long.class, key, val, exc);} + } + public float ReadFloat(String key) { + Object val = Read(key); + try {return (parse) ? Float_.parse(String_.as_(val)) : Float_.cast(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(float.class, key, val, exc);} + } + public float ReadFloatOr(String key, float or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Float_.parse(String_.as_(val)) : Float_.cast(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, float.class, key, val, or); return or;} + } + public double ReadDouble(String key) { + Object val = Read(key); + try {return (parse) ? Double_.parse(String_.as_(val)) : Double_.cast(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(double.class, key, val, exc);} + } + public double ReadDoubleOr(String key, double or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Double_.parse(String_.as_(val)) : Double_.cast(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, double.class, key, val, or); return or;} + } + @gplx.Virtual public byte ReadByte(String key) { + Object val = Read(key); + try {return (parse) ? Byte_.parse(String_.as_(val)) : Byte_.cast(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(byte.class, key, val, exc);} + } + @gplx.Virtual public byte ReadByteOr(String key, byte or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Byte_.parse(String_.as_(val)) : Byte_.cast(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, byte.class, key, val, or); return or;} + } + @gplx.Virtual public DateAdp ReadDate(String key) { + Object val = Read(key); + try {return (parse) ? DateAdp_.parse_gplx(String_.as_(val)) : (DateAdp)val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(DateAdp.class, key, val, exc);} + } + @gplx.Virtual public DateAdp ReadDateOr(String key, DateAdp or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? DateAdp_.parse_gplx(String_.as_(val)) : (DateAdp)val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(DateAdp.class, key, val, exc);} + } + @gplx.Virtual public Decimal_adp ReadDecimal(String key) { + Object val = Read(key); + try { + if (parse) return Decimal_adp_.parse(String_.as_(val)); + Decimal_adp rv = Decimal_adp_.as_(val); + return (rv == null) + ? Decimal_adp_.db_(val) // HACK: GfoNde_.rdr_ will call ReadAt(int i) on Db_data_rdr; since no Db_data_rdr knows about Decimal_adp, it will always return decimalType + : rv; + } + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(Decimal_adp.class, key, val, exc);} + } + @gplx.Virtual public Decimal_adp ReadDecimalOr(String key, Decimal_adp or) { + Object val = Read(key); if (val == null) return or; + try { + if (parse) return Decimal_adp_.parse(String_.as_(val)); + Decimal_adp rv = Decimal_adp_.as_(val); + return (rv == null) + ? Decimal_adp_.db_(val) // HACK: GfoNde_.rdr_ will call ReadAt(int i) on Db_data_rdr; since no Db_data_rdr knows about Decimal_adp, it will always return decimalType + : rv; + } + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(Decimal_adp.class, key, val, exc);} + } + public char ReadChar(String key) { + Object val = Read(key); + try { + if (parse) return Char_.parse(String_.as_(val)); + return Char_.cast(val); + } + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(char.class, key, val, exc);} + } + public char ReadCharOr(String key, char or) { + Object val = Read(key); if (val == null) return or; + try { + if (parse) return Char_.parse(String_.as_(val)); + return Char_.cast(val); + } + catch (Exception exc) {Err_.Noop(exc); return or;} + } + public byte[] ReadBry(String key) { + Object val = Read(key); + try {return (byte[])val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(byte[].class, key, val, exc);} + } + public byte[] ReadBryOr(String key, byte[] or) { + Object val = Read(key); if (val == null) return or; + try {return (byte[])val;} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, byte[].class, key, val, or); return or;} + } + public gplx.core.ios.streams.Io_stream_rdr ReadRdr(String key) {return gplx.core.ios.streams.Io_stream_rdr_.Noop;} + public boolean SrlBoolOr(String key, boolean or) {return ReadBoolOr(key, or);} + public byte SrlByteOr(String key, byte or) {return ReadByteOr(key, or);} + public int SrlIntOr(String key, int or) {return ReadIntOr(key, or);} + public long SrlLongOr(String key, long or) {return ReadLongOr(key, or);} + public String SrlStrOr(String key, String or) {return ReadStrOr(key, or);} + public DateAdp SrlDateOr(String key, DateAdp or) {return ReadDateOr(key, or);} + public Decimal_adp SrlDecimalOr(String key, Decimal_adp or) {return ReadDecimalOr(key, or);} + public double SrlDoubleOr(String key, double or) {return ReadDoubleOr(key, or);} + public Object SrlObjOr(String key, Object or) {throw Err_.new_unimplemented();} + public void XtoStr_gfml(String_bldr sb) { + sb.Add(this.NameOfNode()).Add(":"); + for (int i = 0; i < this.FieldCount(); i++) { + Keyval kv = this.KeyValAt(i); + if (i != 0) sb.Add(" "); + sb.Add_fmt("{0}='{1}'", kv.Key(), String_.Replace(kv.Val_to_str_or_empty(), "'", "\"")); + } + sb.Add(";"); + } + public abstract DataRdr Subs(); + public void TypeKey_(String v) {} + public abstract SrlMgr SrlMgr_new(Object o); + static Err Err_dataRdr_ReadFailed_err(Class type, String key, Object val, Exception inner) { + String innerMsg = inner == null ? "" : Err_.Message_lang(inner); + return Err_.new_("DataRdr_ReadFailed", "failed to read data", "key", key, "val", val, "type", type, "innerMsg", innerMsg).Trace_ignore_add_1_(); + } + static void Err_dataRdr_ReadFailed_useOr(Class type, String key, Object val, Object or) { + UsrDlg_.Instance.Warn(UsrMsg.new_("failed to read data; substituting default").Add("key", key).Add("val", val).Add("default", or).Add("type", type)); + } + static void Err_dataRdr_ReadFailed_useOr(Exception exc, Class type, String key, Object val, Object or) { + UsrDlg_.Instance.Warn(UsrMsg.new_("failed to read data; substituting default").Add("key", key).Add("val", val).Add("default", or).Add("type", type)); + } +} diff --git a/100_core/src/gplx/core/stores/DataRdr_mem.java b/100_core/src/gplx/core/stores/DataRdr_mem.java new file mode 100644 index 000000000..7ce83ccc8 --- /dev/null +++ b/100_core/src/gplx/core/stores/DataRdr_mem.java @@ -0,0 +1,79 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.gfo_ndes.*; +public class DataRdr_mem extends DataRdr_base implements GfoNdeRdr { + @Override public String NameOfNode() {return cur.Name();} + public GfoNde UnderNde() {return cur;} + @Override public int FieldCount() {return flds.Count();} + @Override public String KeyAt(int i) {return flds.Get_at(i).Key();} + @Override public Object ReadAt(int i) {return cur.ReadAt(i);} + @Override public Object Read(String key) { + int i = flds.Idx_of(key); if (i == List_adp_.Not_found) return null; + return cur.ReadAt(i); + } + public boolean MoveNextPeer() { + if (++peerPos >= peerList.Count()) { + cur = null; + return false; + } + cur = peerList.FetchAt_asGfoNde(peerPos); + return true; + } + @Override public DataRdr Subs() { + if (cur == null && peerList.Count() == 0) return DataRdr_.Null; + return GfoNdeRdr_.peers_(Peers_get(), this.Parse()); + } + public DataRdr Subs_byName(String name) { + if (cur == null && peerList.Count() == 0) return DataRdr_.Null; + String[] names = String_.Split(name, "/"); + GfoNdeList list = GfoNdeList_.new_(); + Subs_byName(list, names, 0, Peers_get()); + return GfoNdeRdr_.peers_(list, this.Parse()); + } + @Override public DataRdr Subs_byName_moveFirst(String name) { + DataRdr subRdr = Subs_byName(name); + boolean hasFirst = subRdr.MoveNextPeer(); + return (hasFirst) ? subRdr : DataRdr_.Null; + } + public String To_str() {return cur.To_str();} + public void Rls() {this.cur = null; this.peerList = null;} + @Override public SrlMgr SrlMgr_new(Object o) {return new DataRdr_mem();} + GfoNdeList Peers_get() { + boolean initialized = cur == null && peerPos == -1 && peerList.Count() > 0; // initialized = no current, at bof, subs available + return initialized ? peerList.FetchAt_asGfoNde(0).Subs() : cur.Subs(); + } + void Subs_byName(GfoNdeList list, String[] names, int depth, GfoNdeList peers) { + String name = names[depth]; + for (int i = 0; i < peers.Count(); i++) { + GfoNde sub = peers.FetchAt_asGfoNde(i); if (sub == null) continue; + if (!String_.Eq(name, sub.Name())) continue; + if (depth == names.length - 1) + list.Add(sub); + else + Subs_byName(list, names, depth + 1, sub.Subs()); + } + } + + GfoNde cur; GfoNdeList peerList; int peerPos = -1; GfoFldList flds; + public static DataRdr_mem new_(GfoNde cur, GfoFldList flds, GfoNdeList peerList) { + DataRdr_mem rv = new DataRdr_mem(); + rv.cur = cur; rv.peerList = peerList; rv.flds = flds; + return rv; + } @gplx.Internal protected DataRdr_mem() {} +} diff --git a/100_core/src/gplx/core/stores/DataWtr.java b/100_core/src/gplx/core/stores/DataWtr.java new file mode 100644 index 000000000..b8a39bf41 --- /dev/null +++ b/100_core/src/gplx/core/stores/DataWtr.java @@ -0,0 +1,33 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.gfo_ndes.*; +public interface DataWtr extends SrlMgr { + Hash_adp EnvVars(); + + void InitWtr(String key, Object val); + void WriteTableBgn(String name, GfoFldList fields); + void WriteNodeBgn(String nodeName); + void WriteLeafBgn(String leafName); + void WriteData(String name, Object val); + void WriteNodeEnd(); + void WriteLeafEnd(); + + void Clear(); + String To_str(); +} diff --git a/100_core/src/gplx/core/stores/DataWtr_.java b/100_core/src/gplx/core/stores/DataWtr_.java new file mode 100644 index 000000000..69c1ef12d --- /dev/null +++ b/100_core/src/gplx/core/stores/DataWtr_.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.gfo_ndes.*; +public class DataWtr_ { + public static final DataWtr Null = new DataWtr_null(); +} +class DataWtr_null implements DataWtr { + public boolean Type_rdr() {return false;} + public Hash_adp EnvVars() {return envVars;} Hash_adp envVars = Hash_adp_.New(); + public void InitWtr(String key, Object val) {} + public void WriteTableBgn(String name, GfoFldList fields) {} + public void WriteNodeBgn(String nodeName) {} + public void WriteLeafBgn(String leafName) {} + public void WriteData(String name, Object val) {} + public void WriteNodeEnd() {} + public void WriteLeafEnd() {} + public void Clear() {} + public String To_str() {return "";} + public Object StoreRoot(SrlObj root, String key) {return null;} + public boolean SrlBoolOr(String key, boolean v) {return v;} + public byte SrlByteOr(String key, byte v) {return v;} + public int SrlIntOr(String key, int or) {return or;} + public long SrlLongOr(String key, long or) {return or;} + public String SrlStrOr(String key, String or) {return or;} + public DateAdp SrlDateOr(String key, DateAdp or) {return or;} + public Decimal_adp SrlDecimalOr(String key, Decimal_adp or) {return or;} + public double SrlDoubleOr(String key, double or) {return or;} + public Object SrlObjOr(String key, Object or) {return or;} + public void SrlList(String key, List_adp list, SrlObj proto, String itmKey) {} + public void TypeKey_(String v) {} + public SrlMgr SrlMgr_new(Object o) {return this;} +} diff --git a/100_core/src/gplx/core/stores/DataWtr_base.java b/100_core/src/gplx/core/stores/DataWtr_base.java new file mode 100644 index 000000000..395deae41 --- /dev/null +++ b/100_core/src/gplx/core/stores/DataWtr_base.java @@ -0,0 +1,52 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +public abstract class DataWtr_base implements SrlMgr { + @gplx.Virtual public Hash_adp EnvVars() {return envVars;} Hash_adp envVars = Hash_adp_.New(); + public boolean Type_rdr() {return false;} + public abstract void WriteData(String key, Object o); + public abstract void WriteNodeBgn(String nodeName); + public abstract void WriteNodeEnd(); + @gplx.Virtual public void SrlList(String key, List_adp list, SrlObj proto, String itmKey) { + this.WriteNodeBgn(key); + for (Object itmObj : list) { + SrlObj itm = (SrlObj)itmObj; + this.WriteNodeBgn(itmKey); + itm.SrlObj_Srl(this); + this.WriteNodeEnd(); + } + this.WriteNodeEnd(); + } + @gplx.Virtual public Object StoreRoot(SrlObj root, String key) { + this.WriteNodeBgn(key); + root.SrlObj_Srl(this); + this.WriteNodeEnd(); + return root; + } + public boolean SrlBoolOr(String key, boolean v) {WriteData(key, v); return v;} + public byte SrlByteOr(String key, byte v) {WriteData(key, v); return v;} + public int SrlIntOr(String key, int or) {WriteData(key, or); return or;} + public long SrlLongOr(String key, long or) {WriteData(key, or); return or;} + public String SrlStrOr(String key, String or) {WriteData(key, or); return or;} + public DateAdp SrlDateOr(String key, DateAdp or) {WriteData(key, or.XtoStr_gplx()); return or;} + public Decimal_adp SrlDecimalOr(String key, Decimal_adp or) {WriteData(key, or.Under()); return or;} + public double SrlDoubleOr(String key, double or) {WriteData(key, or); return or;} + public Object SrlObjOr(String key, Object or) {throw Err_.new_unimplemented();} + public void TypeKey_(String v) {} + public abstract SrlMgr SrlMgr_new(Object o); +} diff --git a/100_core/src/gplx/core/stores/GfoNdeRdr.java b/100_core/src/gplx/core/stores/GfoNdeRdr.java new file mode 100644 index 000000000..bca5d712c --- /dev/null +++ b/100_core/src/gplx/core/stores/GfoNdeRdr.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.gfo_ndes.*; +public interface GfoNdeRdr extends DataRdr { + GfoNde UnderNde(); +} diff --git a/100_core/src/gplx/core/stores/GfoNdeRdr_.java b/100_core/src/gplx/core/stores/GfoNdeRdr_.java new file mode 100644 index 000000000..48f89f21a --- /dev/null +++ b/100_core/src/gplx/core/stores/GfoNdeRdr_.java @@ -0,0 +1,49 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import gplx.core.gfo_ndes.*; import gplx.core.type_xtns.*; +public class GfoNdeRdr_ { + public static GfoNdeRdr kvs_(Keyval_list kvList) { + GfoFldList flds = GfoFldList_.new_(); + int pairsLen = kvList.Count(); + Object[] vals = new Object[pairsLen]; + for (int i = 0; i < pairsLen; i++) { + Keyval pair = kvList.Get_at(i); + flds.Add(pair.Key(), StringClassXtn.Instance); + vals[i] = pair.Val_to_str_or_empty(); + } + GfoNde nde = GfoNde_.vals_(flds, vals); + return root_(nde, true); + } + public static GfoNdeRdr root_parseNot_(GfoNde root) {return root_(root, true);} + public static GfoNdeRdr root_(GfoNde root, boolean parse) { + DataRdr_mem rv = DataRdr_mem.new_(root, root.Flds(), root.Subs()); rv.Parse_set(parse); + return rv; + } + public static GfoNdeRdr leaf_(GfoNde cur, boolean parse) { + DataRdr_mem rv = DataRdr_mem.new_(cur, cur.Flds(), GfoNdeList_.Null); rv.Parse_set(parse); + return rv; + } + public static GfoNdeRdr peers_(GfoNdeList peers, boolean parse) { + GfoFldList flds = peers.Count() == 0 ? GfoFldList_.Null : peers.FetchAt_asGfoNde(0).Flds(); + DataRdr_mem rv = DataRdr_mem.new_(null, flds, peers); rv.Parse_set(parse); + return rv; + } + public static GfoNdeRdr as_(Object obj) {return obj instanceof GfoNdeRdr ? (GfoNdeRdr)obj : null;} + public static GfoNdeRdr cast(Object obj) {try {return (GfoNdeRdr)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, GfoNdeRdr.class, obj);}} +} diff --git a/100_core/src/gplx/core/stores/SrlMgr.java b/100_core/src/gplx/core/stores/SrlMgr.java new file mode 100644 index 000000000..684406e62 --- /dev/null +++ b/100_core/src/gplx/core/stores/SrlMgr.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +public interface SrlMgr { + boolean Type_rdr(); + Object StoreRoot(SrlObj root, String key); + + boolean SrlBoolOr(String key, boolean v); + byte SrlByteOr(String key, byte v); + int SrlIntOr(String key, int v); + long SrlLongOr(String key, long v); + String SrlStrOr(String key, String v); + double SrlDoubleOr(String key, double v); + Decimal_adp SrlDecimalOr(String key, Decimal_adp v); + DateAdp SrlDateOr(String key, DateAdp v); + Object SrlObjOr(String key, Object v); + void SrlList(String key, List_adp list, SrlObj proto, String itmKey); + void TypeKey_(String v); + SrlMgr SrlMgr_new(Object o); +} diff --git a/100_core/src/gplx/core/stores/SrlObj.java b/100_core/src/gplx/core/stores/SrlObj.java new file mode 100644 index 000000000..f0f6bbf9d --- /dev/null +++ b/100_core/src/gplx/core/stores/SrlObj.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +public interface SrlObj { + SrlObj SrlObj_New(Object o); + void SrlObj_Srl(SrlMgr mgr); +} diff --git a/100_core/src/gplx/core/stores/xmls/XmlDataRdr.java b/100_core/src/gplx/core/stores/xmls/XmlDataRdr.java new file mode 100644 index 000000000..3ad80b676 --- /dev/null +++ b/100_core/src/gplx/core/stores/xmls/XmlDataRdr.java @@ -0,0 +1,77 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores.xmls; import gplx.*; import gplx.core.*; import gplx.core.stores.*; +import gplx.langs.xmls.*; /*Xpath_*/ +public class XmlDataRdr extends DataRdr_base implements DataRdr { + @Override public String NameOfNode() {return nde.Name();} public String To_str() {return nde.Xml_outer();} + @Override public int FieldCount() {return nde.Atrs() == null ? 0 : nde.Atrs().Count();} // nde.Attributes == null when nde is XmlText; ex: val + @Override public String KeyAt(int i) {return nde.Atrs().Get_at(i).Name();} + @Override public Object ReadAt(int i) { + XmlAtr attrib = nde.Atrs().Get_at(i); + return (attrib == null) ? null : attrib.Value(); + } + @Override public Object Read(String key) { + return nde.Atrs().FetchValOr(key, null); + } + public boolean MoveNextPeer() { + if (++pos >= peerList.Count()){ // moved out Of range + nde = null; + return false; + } + nde = peerList.Get_at(pos); + return true; + } + @Override public DataRdr Subs() { + XmlNdeList list = Xpath_.SelectElements(nde); + XmlDataRdr rv = new XmlDataRdr(); + rv.ctor_(list, null); + return rv; + } + @Override public DataRdr Subs_byName_moveFirst(String name) { + DataRdr subRdr = Subs_byName(name); + boolean hasFirst = subRdr.MoveNextPeer(); + return (hasFirst) ? subRdr : DataRdr_.Null; + } + public DataRdr Subs_byName(String name) { + XmlNdeList list = Xpath_.SelectAll(nde, name); + XmlDataRdr rv = new XmlDataRdr(); + rv.ctor_(list, null); + return rv; + } + public void Rls() {nde = null; peerList = null;} + public String NodeValue_get() { + if (nde.SubNdes().Count() != 1) return ""; + XmlNde sub = nde.SubNdes().Get_at(0); + return (sub.NdeType_textOrEntityReference()) ? sub.Text_inner() : ""; + } + public String Node_OuterXml() {return nde.Xml_outer();} + @Override public SrlMgr SrlMgr_new(Object o) {return new XmlDataRdr();} + void LoadString(String raw) { + XmlDoc xdoc = XmlDoc_.parse(raw); + XmlNdeList list = Xpath_.SelectElements(xdoc.Root()); + ctor_(list, xdoc.Root()); + } + void ctor_(XmlNdeList peerList, XmlNde nde) { + this.peerList = peerList; this.nde = nde; pos = -1; + } + + XmlNde nde = null; + XmlNdeList peerList = null; int pos = -1; + @gplx.Internal protected XmlDataRdr(String raw) {this.LoadString(raw); this.Parse_set(true);} + XmlDataRdr() {this.Parse_set(true);} +} diff --git a/100_core/src/gplx/core/stores/xmls/XmlDataRdr_.java b/100_core/src/gplx/core/stores/xmls/XmlDataRdr_.java new file mode 100644 index 000000000..f0ee30374 --- /dev/null +++ b/100_core/src/gplx/core/stores/xmls/XmlDataRdr_.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores.xmls; import gplx.*; import gplx.core.*; import gplx.core.stores.*; +public class XmlDataRdr_ { + public static XmlDataRdr file_(Io_url url) { + String text = Io_mgr.Instance.LoadFilStr(url); + return new XmlDataRdr(text); + } + public static XmlDataRdr text_(String text) {return new XmlDataRdr(text);} +} diff --git a/100_core/src/gplx/core/stores/xmls/XmlDataWtr_.java b/100_core/src/gplx/core/stores/xmls/XmlDataWtr_.java new file mode 100644 index 000000000..b3ec2e11c --- /dev/null +++ b/100_core/src/gplx/core/stores/xmls/XmlDataWtr_.java @@ -0,0 +1,113 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores.xmls; import gplx.*; import gplx.core.*; import gplx.core.stores.*; +import gplx.core.strings.*; import gplx.core.gfo_ndes.*; +public class XmlDataWtr_ { + public static DataWtr new_() {return XmlDataWtr.new_();} +} +class XmlDataWtr extends DataWtr_base implements DataWtr { + public void InitWtr(String key, Object val) {} + @Override public void WriteData(String name, Object val) { +// if (val == null) return; + String valString = Object_.Xto_str_strict_or_empty(val); + int valStringLen = String_.Len(valString); + sb.Add(" ").Add(name).Add("=\""); + for (int i = 0; i < valStringLen; i++) { + char c = String_.CharAt(valString, i); + if (c == '<') sb.Add("<"); + else if (c == '>') sb.Add(">"); + else if (c == '&') sb.Add("&"); + else if (c == '\"') sb.Add(""e;"); + else sb.Add(c); + } + sb.Add("\""); +// XmlAttribute atr = doc.CreateAttribute(name); +// atr.Value = (val == null) ? String_.Empty : val.toString(); +// nde.Attributes.Append(atr); + } + public void WriteLeafBgn(String leafName) {this.WriteXmlNodeBegin(leafName);} + public void WriteLeafEnd() {} + public void WriteTableBgn(String name, GfoFldList fields) {this.WriteXmlNodeBegin(name);} + @Override public void WriteNodeBgn(String nodeName) {this.WriteXmlNodeBegin(nodeName);} + @Override public void WriteNodeEnd() {this.WriteXmlNodeEnd();} + public String To_str() { + while (names.Count() > 0) { + WriteXmlNodeEnd(); + } + return sb.To_str(); +// while (nde.ParentNode != null) +// WriteXmlNodeEnd(); // close all open ndes automatically +// return doc.OuterXml; + } + public void WriteComment(String comment) { + sb.Add(""); +// XmlComment xmlComment = doc.CreateComment(comment); +// nde.AppendChild(xmlComment); + } + public void Clear() { + sb.Clear(); +// doc = new XmlDocument(); + } + void WriteXmlNodeBegin(String name) { + if (ndeOpened) { + sb.Add(">" + String_.CrLf); + } + ndeOpened = true; + names.Add(name); + sb.Add("<" + name); +// XmlNode owner = nde; +// nde = doc.CreateElement(name); +// if (owner == null) // first call to WriteXmlNodeBegin(); append child to doc +// doc.AppendChild(nde); +// else { +// WriteLineFeedIfNeeded(doc, owner); +// owner.AppendChild(nde); +// } + } + void WriteXmlNodeEnd() { + if (ndeOpened) { + sb.Add(" />" + String_.CrLf); + ndeOpened = false; + } + else { + String name = (String)names.Get_at_last(); + sb.Add("" + String_.CrLf); + } + names.Del_at(names.Count() - 1); + // if (nde.ParentNode == null) throw Err_.new_wo_type("WriteXmlNodeEnd() called on root node"); +// nde = nde.ParentNode; +// WriteLineFeed(doc, nde); + } +// void WriteLineFeed(XmlDocument doc, XmlNode owner) { +// XmlSignificantWhitespace crlf = doc.CreateSignificantWhitespace(String_.CrLf); +// owner.AppendChild(crlf); +// } +// void WriteLineFeedIfNeeded(XmlDocument doc, XmlNode owner) { +// XmlSignificantWhitespace lastSubNode = owner.ChildNodes[owner.ChildNodes.Count - 1] as XmlSignificantWhitespace; +// if (lastSubNode == null) +// WriteLineFeed(doc, owner); // write LineFeed for consecutive WriteXmlNodeBegin calls; ex: +// } + @Override public SrlMgr SrlMgr_new(Object o) {return new XmlDataWtr();} + boolean ndeOpened = false; +// int atrCount = 0; +// int ndeState = -1; static final int NdeState0_Opened = 0, NdeState0_H = 1; +// XmlDocument doc = new XmlDocument(); XmlNode nde; + List_adp names = List_adp_.New(); + String_bldr sb = String_bldr_.new_(); + public static XmlDataWtr new_() {return new XmlDataWtr();} XmlDataWtr() {} +} diff --git a/100_core/src/gplx/core/strings/String_bldr.java b/100_core/src/gplx/core/strings/String_bldr.java new file mode 100644 index 000000000..f5280156a --- /dev/null +++ b/100_core/src/gplx/core/strings/String_bldr.java @@ -0,0 +1,116 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.strings; import gplx.*; import gplx.core.*; +import gplx.core.envs.*; +public interface String_bldr { + boolean Has_none(); + boolean Has_some(); + String_bldr Add_many(String... array); + String_bldr Add_fmt(String format, Object... args); + String_bldr Add_fmt_line(String format, Object... args); + String_bldr Add_kv(String hdr, String val); + String_bldr Add_kv_obj(String k, Object v); + String_bldr Add_char_pipe(); + String_bldr Add_char_nl(); + String_bldr Add_char_crlf(); + String_bldr Add_str_w_crlf(String v); + String_bldr Add_spr_unless_first(String s, String spr, int i); + String_bldr Clear(); + String To_str_and_clear(); + String To_str(); + int Count(); + String_bldr Add(byte[] v); + String_bldr Add(String s); + String_bldr Add(char c); + String_bldr Add(int i); + String_bldr Add_obj(Object o); + String_bldr Add_mid(char[] ary, int bgn, int count); + String_bldr Add_at(int idx, String s); + String_bldr Del(int bgn, int len); +} +abstract class String_bldr_base implements String_bldr { + public boolean Has_none() {return this.Count() == 0;} + public boolean Has_some() {return this.Count() > 0;} + public String_bldr Add_many(String... array) {for (String s : array) Add(s); return this;} + public String_bldr Add_fmt(String format, Object... args) {Add(String_.Format(format, args)); return this;} + public String_bldr Add_fmt_line(String format, Object... args) {Add_str_w_crlf(String_.Format(format, args)); return this;} + public String_bldr Add_kv_obj(String k, Object v) { + if (this.Count() != 0) this.Add(" "); + this.Add_fmt("{0}={1}", k, Object_.Xto_str_strict_or_null_mark(v)); + return this; + } + public String_bldr Add_char_pipe() {return Add("|");} + public String_bldr Add_char_nl() {Add(Op_sys.Lnx.Nl_str()); return this;} + public String_bldr Add_char_crlf() {Add(Op_sys.Wnt.Nl_str()); return this;} + public String_bldr Add_str_w_crlf(String line) {Add(line); Add(String_.CrLf); return this;} + public String_bldr Add_spr_unless_first(String s, String spr, int i) { + if (i != 0) Add(spr); + Add(s); + return this; + } + public String_bldr Add_kv(String hdr, String val) { + if (String_.Len_eq_0(val)) return this; + if (this.Count() != 0) this.Add(' '); + this.Add(hdr); + this.Add(val); + return this; + } + public String_bldr Clear() {Del(0, Count()); return this;} + public String To_str_and_clear() { + String rv = To_str(); + Clear(); + return rv; + } + @Override public String toString() {return To_str();} + public abstract String To_str(); + public abstract int Count(); + public abstract String_bldr Add_at(int idx, String s); + public abstract String_bldr Add(byte[] v); + public abstract String_bldr Add(String s); + public abstract String_bldr Add(char c); + public abstract String_bldr Add(int i); + public abstract String_bldr Add_mid(char[] ary, int bgn, int count); + public abstract String_bldr Add_obj(Object o); + public abstract String_bldr Del(int bgn, int len); +} +class String_bldr_thread_single extends String_bldr_base { + private java.lang.StringBuilder sb = new java.lang.StringBuilder(); + @Override public String To_str() {return sb.toString();} + @Override public int Count() {return sb.length();} + @Override public String_bldr Add_at(int idx, String s) {sb.insert(idx, s); return this;} + @Override public String_bldr Add(byte[] v) {sb.append(String_.new_u8(v)); return this;} + @Override public String_bldr Add(String s) {sb.append(s); return this;} + @Override public String_bldr Add(char c) {sb.append(c); return this;} + @Override public String_bldr Add(int i) {sb.append(i); return this;} + @Override public String_bldr Add_mid(char[] ary, int bgn, int count) {sb.append(ary, bgn, count); return this;} + @Override public String_bldr Add_obj(Object o) {sb.append(o); return this;} + @Override public String_bldr Del(int bgn, int len) {sb.delete(bgn, len); return this;} +} +class String_bldr_thread_multiple extends String_bldr_base { + private java.lang.StringBuffer sb = new java.lang.StringBuffer(); + @Override public String To_str() {return sb.toString();} + @Override public int Count() {return sb.length();} + @Override public String_bldr Add_at(int idx, String s) {sb.insert(idx, s); return this;} + @Override public String_bldr Add(byte[] v) {sb.append(String_.new_u8(v)); return this;} + @Override public String_bldr Add(String s) {sb.append(s); return this;} + @Override public String_bldr Add(char c) {sb.append(c); return this;} + @Override public String_bldr Add(int i) {sb.append(i); return this;} + @Override public String_bldr Add_mid(char[] ary, int bgn, int count) {sb.append(ary, bgn, count); return this;} + @Override public String_bldr Add_obj(Object o) {sb.append(o); return this;} + @Override public String_bldr Del(int bgn, int len) {sb.delete(bgn, len); return this;} +} diff --git a/100_core/src/gplx/core/strings/String_bldr_.java b/100_core/src/gplx/core/strings/String_bldr_.java new file mode 100644 index 000000000..545e11bae --- /dev/null +++ b/100_core/src/gplx/core/strings/String_bldr_.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.strings; import gplx.*; import gplx.core.*; +public class String_bldr_ { + public static String_bldr new_() {return new String_bldr_thread_single();} + public static String_bldr new_thread() {return new String_bldr_thread_multiple();} +} diff --git a/100_core/src/gplx/core/tests/Gftest.java b/100_core/src/gplx/core/tests/Gftest.java new file mode 100644 index 000000000..0b59bc388 --- /dev/null +++ b/100_core/src/gplx/core/tests/Gftest.java @@ -0,0 +1,212 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.tests; import gplx.*; import gplx.core.*; +import gplx.core.brys.*; +public class Gftest { + private static final Bry_bfr bfr = Bry_bfr_.New(); + public static void Eq__ary(boolean[] expd, boolean[] actl, String msg_fmt, Object... msg_args) {Eq__array(Type_adp_.Tid__bool, expd, actl, msg_fmt, msg_args);} + public static void Eq__ary(int[] expd, int[] actl, String msg_fmt, Object... msg_args) {Eq__array(Type_adp_.Tid__int, expd, actl, msg_fmt, msg_args);} + public static void Eq__ary(long[] expd, long[] actl, String msg_fmt, Object... msg_args) {Eq__array(Type_adp_.Tid__long, expd, actl, msg_fmt, msg_args);} + public static void Eq__ary__lines(String expd, byte[] actl, String msg_fmt, Object... msg_args) {Eq__ary__lines(expd, String_.new_u8(actl), msg_fmt, msg_args);} + public static void Eq__ary__lines(String expd, String actl, String msg_fmt, Object... msg_args) {Eq__array(Type_adp_.Tid__bry, Bry_split_.Split_lines(Bry_.new_u8(expd)), Bry_split_.Split_lines(Bry_.new_u8(actl)), msg_fmt, msg_args);} + public static void Eq__ary(String[] expd, String[] actl) {Eq__array(Type_adp_.Tid__bry, Bry_.Ary(expd), Bry_.Ary(actl), "no_msg");} + public static void Eq__ary(String[] expd, String[] actl, String msg_fmt, Object... msg_args) {Eq__array(Type_adp_.Tid__bry, Bry_.Ary(expd), Bry_.Ary(actl), msg_fmt, msg_args);} + public static void Eq__ary(String[] expd, byte[][] actl, String msg_fmt, Object... msg_args) {Eq__array(Type_adp_.Tid__bry, Bry_.Ary(expd), actl, msg_fmt, msg_args);} + public static void Eq__ary(byte[][] expd, byte[][] actl, String msg_fmt, Object... msg_args) {Eq__array(Type_adp_.Tid__bry, expd, actl, msg_fmt, msg_args);} + public static void Eq__ary(Bry_bfr_able[] expd_ary, Bry_bfr_able[] actl_ary) {Eq__ary(expd_ary, actl_ary, null);} + public static void Eq__ary(Bry_bfr_able[] expd_ary, Bry_bfr_able[] actl_ary, String msg_fmt, Object... msg_args) { + Eq__array(Type_adp_.Tid__bry, Bry_bfr_able_.To_bry_ary(bfr, expd_ary), Bry_bfr_able_.To_bry_ary(bfr, actl_ary), msg_fmt, msg_args); + } + private static void Eq__array(int type_tid, Object expd_ary, Object actl_ary, String msg_fmt, Object... msg_args) { + boolean[] failures = Calc__failures(type_tid, expd_ary, actl_ary); + if (failures != null) { + Write_fail_head(bfr, msg_fmt, msg_args); + Write_fail_ary(bfr, failures, type_tid, expd_ary, actl_ary); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + } + public static void Eq__null(boolean expd, Object actl) {Eq__null(expd, actl, null);} + public static void Eq__null(boolean expd, Object actl, String msg_fmt, Object... msg_args) { + if ( expd && actl == null + || !expd && actl != null + ) return; + Write_fail_head(bfr, msg_fmt, msg_args); + String expd_str = expd ? "null" : "not null"; + String actl_str = actl == null ? "null" : "not null"; + bfr.Add_str_a7("expd: ").Add_str_a7(expd_str).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add_str_a7(actl_str).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + public static void Eq__obj_or_null(Object expd, Object actl) { + if (expd == null) expd = Str__null; + if (actl == null) actl = Str__null; + Eq__str(Object_.Xto_str_or(expd, Str__null), Object_.Xto_str_or(actl, null), Str__null); + } + public static void Eq__str(String expd, byte[] actl, String msg_fmt, Object... msg_args) {Eq__str(expd, String_.new_u8(actl), msg_fmt, msg_args);} + public static void Eq__str(String expd, byte[] actl) {Eq__str(expd, String_.new_u8(actl), null);} + public static void Eq__str(String expd, String actl) {Eq__str(expd, actl, null);} + public static void Eq__str(String expd, String actl, String msg_fmt, Object... msg_args) { + if (String_.Eq(expd, actl)) return; + Write_fail_head(bfr, msg_fmt, msg_args); + bfr.Add_str_a7("expd: ").Add_str_u8_null(expd).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add_str_u8_null(actl).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + public static void Eq__bry(byte[] expd, byte[] actl) {Eq__bry(expd, actl, null);} + public static void Eq__bry(byte[] expd, byte[] actl, String msg_fmt, Object... msg_args) { + if (Bry_.Eq(expd, actl)) return; + Write_fail_head(bfr, msg_fmt, msg_args); + bfr.Add_str_a7("expd: ").Add(expd).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add(actl).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + public static void Eq__long(long expd, long actl) {Eq__long(expd, actl, null);} + public static void Eq__long(long expd, long actl, String msg_fmt, Object... msg_args) { + if (expd == actl) return; + Write_fail_head(bfr, msg_fmt, msg_args); + bfr.Add_str_a7("expd: ").Add_long_variable(expd).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add_long_variable(actl).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + public static void Eq__byte(byte expd, byte actl) {Eq__byte(expd, actl, null);} + public static void Eq__byte(byte expd, byte actl, String msg_fmt, Object... msg_args) { + if (expd == actl) return; + Write_fail_head(bfr, msg_fmt, msg_args); + bfr.Add_str_a7("expd: ").Add_byte_as_a7(expd).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add_byte_as_a7(actl).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + public static void Eq__int(int expd, int actl) {Eq__int(expd, actl, null);} + public static void Eq__int(int expd, int actl, String msg_fmt, Object... msg_args) { + if (expd == actl) return; + Write_fail_head(bfr, msg_fmt, msg_args); + bfr.Add_str_a7("expd: ").Add_int_variable(expd).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add_int_variable(actl).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + public static void Eq__bool_y(boolean actl) {Eq__bool(Bool_.Y, actl, null);} + public static void Eq__bool_y(boolean actl, String msg_fmt, Object... msg_args) {Eq__bool(Bool_.Y, actl, msg_fmt, msg_args);} + public static void Eq__bool(boolean expd, boolean actl) {Eq__bool(expd, actl, null);} + public static void Eq__bool(boolean expd, boolean actl, String msg_fmt, Object... msg_args) { + if (expd == actl) return; + Write_fail_head(bfr, msg_fmt, msg_args); + bfr.Add_str_a7("expd: ").Add_bool(expd).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add_bool(actl).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + public static void Eq__double(double expd, double actl) {Eq__double(expd, actl, null);} + public static void Eq__double(double expd, double actl, String msg_fmt, Object... msg_args) { + if (expd == actl) return; + Write_fail_head(bfr, msg_fmt, msg_args); + bfr.Add_str_a7("expd: ").Add_double(expd).Add_byte_nl(); + bfr.Add_str_a7("actl: ").Add_double(actl).Add_byte_nl(); + bfr.Add(Bry__line_end); + throw Err_.new_wo_type(bfr.To_str_and_clear()); + } + private static void Write_fail_head(Bry_bfr bfr, String msg_fmt, Object[] msg_args) { + bfr.Add(Bry__line_bgn); + if (msg_fmt != null) { + bfr.Add_str_u8(String_.Format(msg_fmt, msg_args)); + bfr.Add(Bry__line_mid); + } + } + private static void Write_fail_ary(Bry_bfr bfr, boolean[] failures, int type_id, Object expd_ary, Object actl_ary) { + int len = failures.length; + int expd_len = Array_.Len(expd_ary); + int actl_len = Array_.Len(actl_ary); + for (int i = 0; i < len; ++i) { + boolean failure = failures[i]; + int pad_len = 5 - Int_.DigitCount(i); + bfr.Add_int_pad_bgn(Byte_ascii.Num_0, pad_len, i).Add_byte_colon().Add_byte_space(); + Write__itm(bfr, type_id, expd_ary, expd_len, i); + if (failure) { + bfr.Add(Bry__item__eq_n).Add_byte_repeat(Byte_ascii.Space, pad_len - 1); + Write__itm(bfr, type_id, actl_ary, actl_len, i); + } + } + bfr.Add(Bry__line_end); + } + private static void Write__itm(Bry_bfr bfr, int type_id, Object ary, int len, int idx) { + if (idx < len) { + switch (type_id) { + case Type_adp_.Tid__bool: bfr.Add_yn(Bool_.cast(Array_.Get_at(ary, idx))); break; + case Type_adp_.Tid__bry: bfr.Add((byte[])Array_.Get_at(ary, idx)); break; + case Type_adp_.Tid__long: bfr.Add_long_variable(Long_.cast(Array_.Get_at(ary, idx))); break; + case Type_adp_.Tid__int: bfr.Add_int_variable(Int_.cast(Array_.Get_at(ary, idx))); break; + default: throw Err_.new_unhandled_default(type_id); + } + } + else + bfr.Add(Bry__null); + bfr.Add_byte_nl(); + } + private static boolean[] Calc__failures(int tid, Object expd_ary, Object actl_ary) { + int expd_len = Array_.Len(expd_ary); + int actl_len = Array_.Len(actl_ary); + int max_len = expd_len > actl_len ? expd_len : actl_len; if (max_len == 0) return null; + boolean[] rv = null; + for (int i = 0; i < max_len; ++i) { + Object expd_obj = i < expd_len ? Array_.Get_at(expd_ary, i) : null; + Object actl_obj = i < actl_len ? Array_.Get_at(actl_ary, i) : null; + boolean eq = false; + if (expd_obj == null && actl_obj == null) eq = true; + else if (expd_obj == null || actl_obj == null) eq = false; + else { + switch (tid) { + case Type_adp_.Tid__bool: eq = Bool_.cast(expd_obj) == Bool_.cast(actl_obj); break; + case Type_adp_.Tid__bry: eq = Bry_.Eq((byte[])expd_obj, (byte[])actl_obj); break; + case Type_adp_.Tid__long: eq = Long_.cast(expd_obj) == Long_.cast(actl_obj); break; + case Type_adp_.Tid__int: eq = Int_.cast(expd_obj) == Int_.cast(actl_obj); break; + } + } + if (!eq) { + if (rv == null) { + rv = new boolean[max_len]; + } + rv[i] = true; + } + } + return rv; + } + private static final String Str__null = "<>"; + private static final byte[] Bry__item__eq_n = Bry_.new_a7("!= ") // Bry__item__eq_y = Bry_.new_a7("== "), + , Bry__null = Bry_.new_a7(Str__null) + , Bry__line_bgn = Bry_.new_a7("\n************************************************************************************************\n") + , Bry__line_mid = Bry_.new_a7("\n------------------------------------------------------------------------------------------------\n") + , Bry__line_end = Bry_.new_a7( "________________________________________________________________________________________________") + ; +} +/* +package ns; +import org.junit.*; import gplx.core.tests.*; +public class Cls1_tst { + private final Cls1_fxt fxt = new Cls1_fxt(); + @Test public void Basic() {} +} +class Cls1_fxt { + private final Cls1 mgr = new Cls1(); + public Cls1_fxt Test() {return this;} +} +*/ \ No newline at end of file diff --git a/100_core/src/gplx/core/tests/PerfLogMgr_fxt.java b/100_core/src/gplx/core/tests/PerfLogMgr_fxt.java new file mode 100644 index 000000000..32f038574 --- /dev/null +++ b/100_core/src/gplx/core/tests/PerfLogMgr_fxt.java @@ -0,0 +1,66 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.tests; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; import gplx.core.envs.*; +public class PerfLogMgr_fxt { + public void Init(Io_url url, String text) { + this.url = url; + entries.Resize_bounds(1000); + entries.Add(new PerfLogItm(0, text + "|" + Datetime_now.Get().XtoStr_gplx())); + tmr.Bgn(); + } + public void Write(String text) { + long milliseconds = tmr.ElapsedMilliseconds(); + entries.Add(new PerfLogItm(milliseconds, text)); + tmr.Bgn(); + } + public void WriteFormat(String fmt, Object... ary) { + long milliseconds = tmr.ElapsedMilliseconds(); + String text = String_.Format(fmt, ary); + entries.Add(new PerfLogItm(milliseconds, text)); + tmr.Bgn(); + } + public void Flush() { + String_bldr sb = String_bldr_.new_(); + for (Object itmObj : entries) { + PerfLogItm itm = (PerfLogItm)itmObj; + sb.Add(itm.To_str()).Add_char_crlf(); + } + Io_mgr.Instance.AppendFilStr(url, sb.To_str()); + entries.Clear(); + } + List_adp entries = List_adp_.New(); PerfLogTmr tmr = PerfLogTmr.new_(); Io_url url = Io_url_.Empty; + public static final PerfLogMgr_fxt Instance = new PerfLogMgr_fxt(); PerfLogMgr_fxt() {} + class PerfLogItm { + public String To_str() { + String secondsStr = Time_span_.To_str(milliseconds, Time_span_.Fmt_Default); + secondsStr = String_.PadBgn(secondsStr, 7, "0"); // 7=000.000; left-aligns all times + return String_.Concat(secondsStr, "|", text); + } + long milliseconds; String text; + @gplx.Internal protected PerfLogItm(long milliseconds, String text) { + this.milliseconds = milliseconds; this.text = text; + } + } + +} +class PerfLogTmr { + public void Bgn() {bgn = System_.Ticks();} long bgn; + public long ElapsedMilliseconds() {return System_.Ticks() - bgn; } + public static PerfLogTmr new_() {return new PerfLogTmr();} PerfLogTmr() {} +} diff --git a/100_core/src/gplx/core/tests/TfdsEqListItmStr.java b/100_core/src/gplx/core/tests/TfdsEqListItmStr.java new file mode 100644 index 000000000..774a87c33 --- /dev/null +++ b/100_core/src/gplx/core/tests/TfdsEqListItmStr.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.tests; import gplx.*; import gplx.core.*; +public interface TfdsEqListItmStr { + String To_str(Object cur, Object actl); +} diff --git a/100_core/src/gplx/core/texts/Base32Converter.java b/100_core/src/gplx/core/texts/Base32Converter.java new file mode 100644 index 000000000..c64c76d0e --- /dev/null +++ b/100_core/src/gplx/core/texts/Base32Converter.java @@ -0,0 +1,99 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +public class Base32Converter { + public static String EncodeString(String orig) {return Encode(Bry_.new_u8(orig));} + public static String Encode(byte[] raw) { + int i = 0, index = 0, digit = 0; int currByte, nextByte; + int rawLen = Array_.Len(raw); + char[] ary = new char[(rawLen + 7) * 8 / 5]; int aryPos = 0; + while (i < rawLen) { + currByte = (raw[i] >= 0) ? raw[i] : raw[i] + 256; // unsign; java converts char 128+ -> byte -128 + if (index > 3) { /* Is the curPath digit going to span a byte boundary? */ + if ((i+1) < rawLen) + nextByte = (raw[i+1] >= 0) ? raw[i+1] : (raw[i+1] + 256); + else + nextByte = 0; + + digit = currByte & (0xFF >> index); + index = (index + 5) % 8; + digit <<= index; + digit |= nextByte >> (8 - index); + i++; + } + else { + digit = (currByte >> (8 - (index + 5))) & 0x1F; + index = (index + 5) % 8; + if (index == 0) i++; + } + ary[aryPos++] = String_.CharAt(chars, digit); + } + return new String(ary, 0, aryPos); + } + public static String DecodeString(String orig) {return String_.new_u8(Decode(orig));} + public static byte[] Decode(String raw) { + int i, index, lookup, offset; byte digit; + int rawLen = String_.Len(raw); + int rvLen = rawLen * 5 / 8; + byte[] rv = new byte[rvLen]; + for (i = 0, index = 0, offset = 0; i < rawLen; i++) { + lookup = String_.CharAt(raw, i) - '0'; + if (lookup < 0 || lookup >= valsLen) continue; /* Skip any char outside the lookup table */ + digit = vals[lookup]; + if (digit == 0x7F) continue; /* If this digit is not in the table, ignore it */ + + if (index <= 3) { + index = (index + 5) % 8; + if (index == 0) { + rv[offset] |= digit; + offset++; + if(offset >= rvLen) break; + } + else + rv[offset] |= (byte)(digit << (8 - index)); + } + else { + index = (index + 5) % 8; + rv[offset] |= (byte)(digit >> index); + offset++; + + if(offset >= rvLen) break; + rv[offset] |= (byte)(digit << (8 - index)); + } + } + return rv; + } + static String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + static int valsLen = 80; + static byte[] vals = { + 0x7F,0x7F,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, // '0', '1', '2', '3', '4', '5', '6', '7' + 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F, // '8', '9', ':', ';', '<', '=', '>', '?' + 0x7F,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G' + 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' + 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' + 0x17,0x18,0x19,0x7F,0x7F,0x7F,0x7F,0x7F, // 'X', 'Y', 'Z', '[', '\', ']', '^', '_' + 0x7F,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g' + 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' + 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' + 0x17,0x18,0x19,0x7F,0x7F,0x7F,0x7F,0x7F // 'x', 'y', 'z', '{', '|', '}', '~', 'DEL' + }; +} +/* +source: Azraeus excerpt in java + http://www.koders.com/java/fidA4D6F0DF43E6E9A6B518762366AB7232C14E9DB7.aspx +*/ diff --git a/100_core/src/gplx/core/texts/Base64Converter.java b/100_core/src/gplx/core/texts/Base64Converter.java new file mode 100644 index 000000000..036453836 --- /dev/null +++ b/100_core/src/gplx/core/texts/Base64Converter.java @@ -0,0 +1,79 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +public class Base64Converter { + private final static char[] ALPHABET = String_.XtoCharAry("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + private static int[] toInt = null;//new int[128]; + static void Init() { + toInt = new int[128]; + for(int i=0; i< ALPHABET.length; i++){ + toInt[ALPHABET[i]]= i; + } + } + public static String EncodeString(String orig) {return Encode(Bry_.new_u8(orig));} + public static String Encode(byte[] buf){ + if (toInt == null) Init(); + int size = buf.length; + char[] ar = new char[((size + 2) / 3) * 4]; + int a = 0; + int i=0; + while(i < size){ + byte b0 = buf[i++]; + byte b1 = (i < size) ? buf[i++] : (byte)0; + byte b2 = (i < size) ? buf[i++] : (byte)0; + + int mask = 0x3F; + ar[a++] = ALPHABET[(b0 >> 2) & mask]; + ar[a++] = ALPHABET[((b0 << 4) | ((b1 & 0xFF) >> 4)) & mask]; + ar[a++] = ALPHABET[((b1 << 2) | ((b2 & 0xFF) >> 6)) & mask]; + ar[a++] = ALPHABET[b2 & mask]; + } + switch(size % 3){ + case 1: ar[--a] = '='; + ar[--a] = '='; + break; + case 2: ar[--a] = '='; break; + } + return new String(ar); + } + public static String DecodeString(String orig) {return String_.new_u8(Decode(orig));} + public static byte[] Decode(String s){ + if (toInt == null) Init(); + int sLen = String_.Len(s); + int delta = String_.Has_at_end(s, "==") ? 2 : String_.Has_at_end(s, "=") ? 1 : 0; + byte[] buffer = new byte[sLen *3/4 - delta]; + int mask = 0xFF; + int index = 0; + for(int i=0; i< sLen; i+=4){ + int c0 = toInt[String_.CharAt(s, i)]; + int c1 = toInt[String_.CharAt(s, i + 1)]; + buffer[index++]= (byte)(((c0 << 2) | (c1 >> 4)) & mask); + if(index >= buffer.length){ + return buffer; + } + int c2 = toInt[String_.CharAt(s, i + 2)]; + buffer[index++]= (byte)(((c1 << 4) | (c2 >> 2)) & mask); + if(index >= buffer.length){ + return buffer; + } + int c3 = toInt[String_.CharAt(s, i + 3)]; + buffer[index++]= (byte)(((c2 << 6) | c3) & mask); + } + return buffer; + } +} diff --git a/100_core/src/gplx/core/texts/BaseXXConverter_tst.java b/100_core/src/gplx/core/texts/BaseXXConverter_tst.java new file mode 100644 index 000000000..ac6d8d2ec --- /dev/null +++ b/100_core/src/gplx/core/texts/BaseXXConverter_tst.java @@ -0,0 +1,58 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import org.junit.*; +public class BaseXXConverter_tst { + @Test public void Base32() { + tst_Base32("", ""); + tst_Base32("f", "MY"); + tst_Base32("fo", "MZXQ"); + tst_Base32("foo", "MZXW6"); + tst_Base32("foob", "MZXW6YQ"); + tst_Base32("fooba", "MZXW6YTB"); + tst_Base32("foobar", "MZXW6YTBOI"); + tst_Base32("A", "IE"); + tst_Base32("a", "ME"); + tst_Base32("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLIZDGNBVGY3Q"); + } + @Test public void Base64() { + tst_Base64("", ""); + tst_Base64("f", "Zg=="); + tst_Base64("fo", "Zm8="); + tst_Base64("foo", "Zm9v"); + tst_Base64("foob", "Zm9vYg=="); + tst_Base64("fooba", "Zm9vYmE="); + tst_Base64("foobar", "Zm9vYmFy"); +// tst_Base64("A", "IE"); +// tst_Base64("a", "ME"); +// tst_Base64("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLIZDGNBVGY3Q"); + } + void tst_Base32(String orig, String expd) { + String actl = Base32Converter.EncodeString(orig); + Tfds.Eq(expd, actl); + String decode = Base32Converter.DecodeString(actl); + Tfds.Eq(orig, decode); + } + void tst_Base64(String orig, String expd) { + String actl = Base64Converter.EncodeString(orig); + Tfds.Eq(expd, actl); + String decode = Base64Converter.DecodeString(actl); + Tfds.Eq(orig, decode); + } +} +//http://tools.ietf.org/html/rfc4648: test vectors for "foobar" diff --git a/100_core/src/gplx/core/texts/CharStream.java b/100_core/src/gplx/core/texts/CharStream.java new file mode 100644 index 000000000..1e83e7c42 --- /dev/null +++ b/100_core/src/gplx/core/texts/CharStream.java @@ -0,0 +1,67 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +public class CharStream { + public char[] Ary() {return ary;} char[] ary; + public int Len() {return len;} int len; + public int Pos() {return pos;} int pos = BgnPos; static final int BgnPos = -1; + public boolean AtBgn() {return pos <= BgnPos;} + public boolean AtEnd() {return pos >= len;} + public boolean AtMid() {return pos > BgnPos && pos < len;} + public char Cur() {try {return ary[pos];} catch (Exception exc) {Err_.Noop(exc); throw Err_.new_missing_idx(pos, this.Len());}} + public void MoveNext() {pos++;} + public void MoveNextBy(int offset) {pos += offset;} + public void MoveBack() {pos--;} + public void MoveBackBy(int offset) {pos -= offset;} + public void Move_to(int val) {pos = val;} + public boolean Match(String match) { + int matchLen = String_.Len(match); + for (int i = 0; i < matchLen; i++) { + int cur = pos + i; + if (cur >= len || ary[cur] != String_.CharAt(match, i)) return false; + } + return true; + } + public boolean MatchAndMove(String match) { + int matchLen = String_.Len(match); + boolean rv = Match(match); + if (rv) MoveNextBy(matchLen); + return rv; + } + public boolean MatchAndMove(char match) { + boolean rv = ary[pos] == match; + if (rv) pos++; + return rv; + } + public String To_str() {return Char_.To_str(ary, 0, len);} + public String XtoStrAtCur(int length) { + length = (pos + length > len) ? len - pos : length; + return Char_.To_str(ary, pos, length); + } + public String To_str_by_pos(int bgn, int end) { + if (bgn < 0) bgn = 0; if (end > len - 1) end = len - 1; + return Char_.To_str(ary, bgn, end - bgn + 1); + } + public static CharStream pos0_(String text) { + CharStream rv = new CharStream(); + rv.ary = String_.XtoCharAry(text); + rv.len = Array_.Len(rv.ary); + rv.MoveNext(); // bgn at pos=0 + return rv; + } CharStream(){} +} diff --git a/100_core/src/gplx/core/texts/CharStream_tst.java b/100_core/src/gplx/core/texts/CharStream_tst.java new file mode 100644 index 000000000..71fd52931 --- /dev/null +++ b/100_core/src/gplx/core/texts/CharStream_tst.java @@ -0,0 +1,61 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import org.junit.*; +public class CharStream_tst { + @Before public void setup() { + stream = CharStream.pos0_("abcdefgh"); + } + @Test public void To_str() { + Tfds.Eq(stream.To_str(), "abcdefgh"); + } + @Test public void CurrentText() { + stream.MoveNextBy(1); + Tfds.Eq(stream.XtoStrAtCur(2), "bc"); + Tfds.Eq(stream.To_str(), "abcdefgh"); + } + @Test public void CurrentText_outOfBounds() { + stream.MoveNextBy(7); + Tfds.Eq(stream.XtoStrAtCur(2), "h"); + } + @Test public void Match() { + stream.MoveNextBy(6); + tst_Match(true, "g"); + tst_Match(false, "z"); + tst_Match(true, "gh"); + tst_Match(false, "gz"); + tst_Match(false, "ghi"); + } + @Test public void AtBounds() { + stream.Move_to(-1); + tst_AtBounds(true, false, false); + + stream.Move_to(0); + tst_AtBounds(false, true, false); + + stream.Move_to(stream.Len()); + tst_AtBounds(false, false, true); + } + void tst_Match(boolean expd, String text) {Tfds.Eq(expd, stream.Match(text));} + void tst_AtBounds(boolean atBgn, boolean atMid, boolean atEnd) { + Tfds.Eq(atBgn, stream.AtBgn()); + Tfds.Eq(atMid, stream.AtMid()); + Tfds.Eq(atEnd, stream.AtEnd()); + } + CharStream stream; +} diff --git a/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch.java b/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch.java new file mode 100644 index 000000000..e712d566c --- /dev/null +++ b/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import gplx.langs.regxs.*; +public class RegxPatn_cls_ioMatch { + public String Raw() {return raw;} private String raw; + public boolean CaseSensitive() {return caseSensitive;} private boolean caseSensitive; + public boolean Matches(String text) { + text = String_.CaseNormalize(caseSensitive, text); + return Regx_adp_.Match(text, compiled);} // WNT-centric: Io_mgr paths are case-insensitive; + @Override public String toString() {return raw;} + + String compiled; + @gplx.Internal protected RegxPatn_cls_ioMatch(String raw, String compiled, boolean caseSensitive) { + this.caseSensitive = caseSensitive; + this.raw = raw; + compiled = String_.CaseNormalize(caseSensitive, compiled); + this.compiled = compiled; + } +} diff --git a/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_.java b/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_.java new file mode 100644 index 000000000..fe86e4ba0 --- /dev/null +++ b/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_.java @@ -0,0 +1,53 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; import gplx.langs.regxs.*; +public class RegxPatn_cls_ioMatch_ { + public static final String Wildcard = "*"; + public static final String OrDelimiter = "|"; + public static final RegxPatn_cls_ioMatch All = RegxPatn_cls_ioMatch_.parse(Wildcard, false); + public static final String ImpossiblePath = "<>"; //"<>" should be an impossible url; NOTE: do not pick * or | or : or \ + public static final RegxPatn_cls_ioMatch None = RegxPatn_cls_ioMatch_.parse(RegxPatn_cls_ioMatch_.ImpossiblePath, false); + public static RegxPatn_cls_ioMatch cast(Object obj) {try {return (RegxPatn_cls_ioMatch)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, RegxPatn_cls_ioMatch.class, obj);}} + public static RegxPatn_cls_ioMatch parse(String raw, boolean caseSensitive) { + String compiled = RegxPatn_cls_ioMatch_.Compile(raw); + return new RegxPatn_cls_ioMatch(raw, compiled, caseSensitive); + } + @gplx.Internal protected static String Compile(String raw) { + if (raw == ImpossiblePath) return ImpossiblePath; + + String_bldr sb = String_bldr_.new_(); + sb.Add(Regx_bldr.Tkn_LineBegin); // Char_LineBegin for exact match (else "LIKE a" would match "abc") + int rawLen = String_.Len(raw); + for (int i = 0; i < rawLen; i++) { + char c = String_.CharAt(raw, i); + if (c == '\\') + sb.Add("\\\\"); + else if (c == '*') + sb.Add(".").Add(Regx_bldr.Tkn_Wild_0Plus); + else if (c == '|') + sb.Add(Regx_bldr.Tkn_LineEnd).Add("|").Add(Regx_bldr.Tkn_LineBegin); // each term must be bracketed by lineBgn/lineEnd; ex: A|B -> ^A$|^B$ + else + sb.Add(c); + } + sb.Add(Regx_bldr.Tkn_LineEnd); + return sb.To_str(); + } + public static final String InvalidCharacters = "|*?\"<>"; // : / \ are omitted b/c they will cause full paths to fail + public static final String ValidCharacters = Regx_bldr.Excludes(InvalidCharacters); +} diff --git a/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_tst.java b/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_tst.java new file mode 100644 index 000000000..52d244de9 --- /dev/null +++ b/100_core/src/gplx/core/texts/RegxPatn_cls_ioMatch_tst.java @@ -0,0 +1,58 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import org.junit.*; +public class RegxPatn_cls_ioMatch_tst { + @Test public void SimpleMatches() { + tst_Matches("file.cs", "file.cs", true); // basic + tst_Matches("file.cs", "file.cs.exe", false); // fail: must match name precisely + tst_Matches("file.cs", "tst_file.cs", false); // fail: must match name precisely + } + @Test public void Wildcard() { + tst_Matches("*.cs", "file.cs", true); // pass: before + tst_Matches("file*", "file_valid.cs", true); // pass: after + tst_Matches("*.exe", "file.cs", false); // fail: before + tst_Matches("file*", "invalid_file.cs", false); // fail: after + } + @Test public void DoubleWildcard() { + tst_Matches("*cs*", "file.cs", true); // pass: after + tst_Matches("*cs*", "csFile.exe", true); // pass: before + tst_Matches("*cs*", "file.cs.exe", true); // pass: middle + tst_Matches("*cs*", "file.exe", false); // fail + } + @Test public void Compound() { + tst_Matches("*.cs|*.exe", "file.cs", true); // pass: match first + tst_Matches("*.cs|*.exe", "file.exe", true); // pass: match second + tst_Matches("*.cs|*.exe", "file.dll", false); // fail: match neither + tst_Matches("*.cs|*.exe", "file.cs.exe.dll", false); // fail: match neither (though both are embedded) + } + @Test public void Backslash() { + tst_Matches("*\\bin\\*", "C:\\project\\bin\\", true); // pass: dir + tst_Matches("*\\bin\\*", "C:\\project\\bin\\file.dll", true); // pass: fil + tst_Matches("*\\bin\\*", "C:\\project\\binFiles\\", false); // fail + } + @Test public void MixedCase() { + tst_Matches("file.cs", "file.cs", true); // pass: same case + tst_Matches("file.cs", "File.cS", true); // pass: diff case + } + void tst_Matches(String regx, String raw, boolean expd) { + RegxPatn_cls_ioMatch pattern = RegxPatn_cls_ioMatch_.parse(regx, false); + boolean actl = pattern.Matches(raw); + Tfds.Eq(expd, actl); + } +} diff --git a/100_core/src/gplx/core/texts/RegxPatn_cls_like.java b/100_core/src/gplx/core/texts/RegxPatn_cls_like.java new file mode 100644 index 000000000..6d49ea45f --- /dev/null +++ b/100_core/src/gplx/core/texts/RegxPatn_cls_like.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import gplx.langs.regxs.*; +public class RegxPatn_cls_like { + public char Escape() {return escape;} char escape; public static final char EscapeDefault = '|'; + public String Raw() {return raw;} private String raw; + public boolean Matches(String text) {return Regx_adp_.Match(text, compiled);} + @Override public String toString() {return String_.Format("LIKE {0} ESCAPE {1} -> {2}", raw, escape, compiled);} + + String compiled; + @gplx.Internal protected RegxPatn_cls_like(String raw, String compiled, char escape) { + this.raw = raw; + this.compiled = compiled; + this.escape = escape; + } +} diff --git a/100_core/src/gplx/core/texts/RegxPatn_cls_like_.java b/100_core/src/gplx/core/texts/RegxPatn_cls_like_.java new file mode 100644 index 000000000..063acb9e6 --- /dev/null +++ b/100_core/src/gplx/core/texts/RegxPatn_cls_like_.java @@ -0,0 +1,63 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; import gplx.langs.regxs.*; +public class RegxPatn_cls_like_ { + public static RegxPatn_cls_like parse(String regxRaw, char escape) { + String regx = Compile(regxRaw, escape); + return new RegxPatn_cls_like(regxRaw, regx, escape); + } + static String Compile(String raw, char escape) { + char Wildcard = '%', AnyChar = '_'; + boolean insideCharSet = false; + String_bldr sb = String_bldr_.new_(); + sb.Add(Regx_bldr.Tkn_LineBegin); + int rawLen = String_.Len(raw); + for (int i = 0; i < rawLen; i++) { + char c = String_.CharAt(raw, i); + if (c == escape) { // escape: ignore cur, append next + i++; + if (i < rawLen) sb.Add(String_.CharAt(raw, i)); + else throw Err_.new_wo_type("escape cannot be last char", "raw", raw, "escape", escape, "i", i); + } + else if (c == Wildcard) { // % -> .* + sb.Add(Regx_bldr.Tkn_AnyChar).Add(Regx_bldr.Tkn_Wild_0Plus); + } + else if (c == AnyChar) // _ -> . + sb.Add(Regx_bldr.Tkn_AnyChar); + else if (c == Regx_bldr.Tkn_CharSetBegin) { // toggle insideCharSet for ^ + insideCharSet = true; + sb.Add(c); + } + else if (c == Regx_bldr.Tkn_CharSetEnd) { // toggle insideCharSet for ^ + insideCharSet = false; + sb.Add(c); + } + else if (c == Regx_bldr.Tkn_Not && insideCharSet) { // ^ is used for Not in CharSet, but also used for LineStart; do not escape if insideCharSet + insideCharSet = false; + sb.Add(c); + } + else if (Regx_bldr.RegxChar_chk(c)) + sb.Add(Regx_bldr.Tkn_Escape).Add(c); + else // regular text + sb.Add(c); + } + sb.Add(Regx_bldr.Tkn_LineEnd); + return sb.To_str(); + } +} diff --git a/100_core/src/gplx/core/texts/RegxPatn_cls_like_tst.java b/100_core/src/gplx/core/texts/RegxPatn_cls_like_tst.java new file mode 100644 index 000000000..e6d53b150 --- /dev/null +++ b/100_core/src/gplx/core/texts/RegxPatn_cls_like_tst.java @@ -0,0 +1,86 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.langs.regxs.*; +public class RegxPatn_cls_like_tst { + @Test public void Basic() { + tst_Match("abcd", "abcd", true); // basic; pass + tst_Match("abcd", "zbcd", false); // basic; fail + tst_Match("abcd", "abc", false); // no wildcard; must be exact match + tst_Match("a cd", "a cd", true); // check space works + } + @Test public void Wildcard() { + tst_Match("abcd", "a%", true); // bgn; pass + tst_Match("abcd", "b%", false); // bgn; fail + tst_Match("abcd", "%d", true); // end; pass + tst_Match("abcd", "%c", false); // end; fail + tst_Match("abcd", "%b%", true); // flank; pass + tst_Match("abcd", "%e%", false); // flank; fail + tst_Match("abcd", "%a%", true); // flank; bgn; pass + tst_Match("abcd", "%d%", true); // flank; end; pass + } + @Test public void Any() { + tst_Match("abcd", "a_cd", true); // basic; pass + tst_Match("abcd", "z_cd", false); // basic; fail + tst_Match("abcd", "a_c", false); // fail; check no wildcard + } + @Test public void CharSet() { + tst_Match("abcd", "a[b]cd", true); // pass + tst_Match("abcd", "a[x]cd", false); // fail + tst_Match("abcd", "a[bcde]cd", true); // multiple; pass + tst_Match("abcd", "a[xyz]cd", false); // multiple; fail + tst_Match("abcd", "a[^z]cd", true); // not; pass + tst_Match("abcd", "a[^b]cd", false); // not; fail + } + @Test public void Escape() { + tst_Match("a%b", "a|%b", true); // escape wildcard; pass + tst_Match("a%bc", "a|%b", false); // escape wildcard; fail + tst_Match("a|b", "a|b", false); // escape char; fail + tst_Match("a|b", "a||b", true); // escape char; pass + } + @Test public void Escape_diffChar() { + tst_Match("a%b", "a~%b", '~', true); // escape wildcard; pass + tst_Match("a%bc", "a~%b", '~', false); // escape wildcard; fail + tst_Match("a|b", "a|b", '~', true); // no escape needed + tst_Match("a~b", "a~b", '~', false); // escape char; fail + tst_Match("a~b", "a~~b", '~', true); // escape char; pass + } + @Test public void Chars() { // Escape Regx_bldr; ex: LIKE 'a{' -> a\{ + tst_EscapeRegxChar(Regx_bldr.Tkn_Escape); // \ + tst_EscapeRegxChar(Regx_bldr.Tkn_GroupBegin); // [ + tst_EscapeRegxChar(Regx_bldr.Tkn_GroupEnd); // ] + tst_EscapeRegxChar(Regx_bldr.Tkn_LineBegin); // ^ + tst_EscapeRegxChar(Regx_bldr.Tkn_LineEnd); // $ + tst_EscapeRegxChar(Regx_bldr.Tkn_RepBegin); // { + tst_EscapeRegxChar(Regx_bldr.Tkn_RepEnd); // } + tst_EscapeRegxChar(Regx_bldr.Tkn_Wild_0or1); // ? + tst_EscapeRegxChar(Regx_bldr.Tkn_Wild_0Plus); // * + tst_EscapeRegxChar(Regx_bldr.Tkn_Wild_1Plus); // + + } + void tst_Match(String raw, String regx, boolean expd) {tst_Match(raw, regx, RegxPatn_cls_like.EscapeDefault, expd);} + void tst_Match(String raw, String regx, char escape, boolean expd) { + RegxPatn_cls_like like = RegxPatn_cls_like_.parse(regx, escape); + boolean actl = like.Matches(raw); + Tfds.Eq(expd, actl, "raw={0} regx={1} expd={2}", raw, regx, expd); + } + void tst_EscapeRegxChar(char regexChar) { + RegxPatn_cls_like like = RegxPatn_cls_like_.parse(Object_.Xto_str_strict_or_empty(regexChar), '|'); + Tfds.Eq(true, like.Matches(Object_.Xto_str_strict_or_empty(regexChar))); + Tfds.Eq(false, like.Matches("a")); // catches errors for improper escaping of wildcard + } +} diff --git a/100_core/src/gplx/core/texts/StringTableBldr.java b/100_core/src/gplx/core/texts/StringTableBldr.java new file mode 100644 index 000000000..81c4bba24 --- /dev/null +++ b/100_core/src/gplx/core/texts/StringTableBldr.java @@ -0,0 +1,57 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import gplx.core.strings.*; +public class StringTableBldr { + public void ClearRows() {rows.Clear();} + public StringTableCol Col_(int i) {return FetchAtOrNew(i);} + public StringTableBldr DefaultHalign_(StringTableColAlign v) {defaultHalign = v; return this;} StringTableColAlign defaultHalign = StringTableColAlign.Left; + public StringTableBldr Add(String... row) { + rows.Add(row); + for (int i = 0; i < row.length; i++) { + StringTableCol col = FetchAtOrNew(i); + col.AdjustFor(row[i]); + } + return this; + } + public StringTableCol FetchAtOrNew(int i) { + if (i < cols.Count()) return StringTableCol.as_(cols.Get_at(i)); + StringTableCol col = StringTableCol.new_(); + col.Halign_(defaultHalign); + cols.Add(i, col); + return col; + } + public String To_str() { + sb.Clear(); + for (int rowI = 0; rowI < rows.Count(); rowI++) { + String[] row = (String[])rows.Get_at(rowI); + for (int colI = 0; colI < row.length; colI++) { + if (colI != 0) sb.Add(" "); + StringTableCol col = StringTableCol.as_(cols.Get_at(colI)); if (col == null) throw Err_.new_missing_idx(colI, cols.Count()); + sb.Add(col.PadCell(row[colI])); + } + sb.Add(String_.CrLf); + } + return sb.To_str_and_clear(); + } + + public static StringTableBldr new_() {return new StringTableBldr();} StringTableBldr() {} + Ordered_hash cols = Ordered_hash_.New(); + List_adp rows = List_adp_.New(); + String_bldr sb = String_bldr_.new_(); +} diff --git a/100_core/src/gplx/core/texts/StringTableBldr_tst.java b/100_core/src/gplx/core/texts/StringTableBldr_tst.java new file mode 100644 index 000000000..b87bd6768 --- /dev/null +++ b/100_core/src/gplx/core/texts/StringTableBldr_tst.java @@ -0,0 +1,59 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +import org.junit.*; +public class StringTableBldr_tst { + @Before public void setup() { + bldr = StringTableBldr.new_(); + } StringTableBldr bldr; + @Test public void TwoCols() { + bldr.Add("a", "aa") + .Add("bb", "b"); + tst_XtoStr + ( "a aa" + , "bb b " + , "" + ); + } + @Test public void RightAlign() { + bldr.Add("a", "aa") + .Add("bb", "b"); + bldr.FetchAtOrNew(0).Halign_(StringTableColAlign.Right); + bldr.FetchAtOrNew(1).Halign_(StringTableColAlign.Right); + tst_XtoStr + ( " a aa" + , "bb b" + , "" + ); + } + @Test public void CenterAlign() { + bldr.Add("aaaa", "a") + .Add("b", "bbbb"); + bldr.FetchAtOrNew(0).Halign_(StringTableColAlign.Mid); + bldr.FetchAtOrNew(1).Halign_(StringTableColAlign.Mid); + tst_XtoStr + ( "aaaa a " + , " b bbbb" + , "" + ); + } + void tst_XtoStr(String... expdLines) { + String expd = String_.Concat_with_obj(String_.CrLf, (Object[])expdLines); + Tfds.Eq(expd, bldr.To_str()); + } +} diff --git a/100_core/src/gplx/core/texts/StringTableCol.java b/100_core/src/gplx/core/texts/StringTableCol.java new file mode 100644 index 000000000..962a1f0c2 --- /dev/null +++ b/100_core/src/gplx/core/texts/StringTableCol.java @@ -0,0 +1,39 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +public class StringTableCol { + public StringTableColAlign Halign() {return halign;} public StringTableCol Halign_(StringTableColAlign val) {halign = val; return this;} StringTableColAlign halign = StringTableColAlign.Left; + public int LengthMax() {return lengthMax;} int lengthMax = Int_.Min_value; + public int LengthMin() {return lengthMin;} int lengthMin = Int_.Max_value; + public void AdjustFor(String s) { + int length = String_.Len(s); + if (length > lengthMax) lengthMax = length; + if (length < lengthMin) lengthMin = length; + } + public String PadCell(String cell) { + int diff = lengthMax - String_.Len(cell); + int val = halign.Val(); + if (val == StringTableColAlign.Left.Val()) return cell + String_.Repeat(" ", diff); + else if (val == StringTableColAlign.Right.Val()) return String_.Repeat(" ", diff) + cell; + else if (val == StringTableColAlign.Mid.Val()) return String_.Concat(String_.Repeat(" ", diff / 2), cell, String_.Repeat(" ", (diff / 2) + (diff % 2))); + else throw Err_.new_unhandled(halign.Val()); + } + public static StringTableCol new_() {return new StringTableCol();} StringTableCol() {} + public static StringTableCol as_(Object obj) {return obj instanceof StringTableCol ? (StringTableCol)obj : null;} + public static StringTableCol cast(Object obj) {try {return (StringTableCol)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, StringTableCol.class, obj);}} +} diff --git a/100_core/src/gplx/core/texts/StringTableColAlign.java b/100_core/src/gplx/core/texts/StringTableColAlign.java new file mode 100644 index 000000000..c6b2a2631 --- /dev/null +++ b/100_core/src/gplx/core/texts/StringTableColAlign.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.texts; import gplx.*; import gplx.core.*; +public class StringTableColAlign { + public int Val() {return val;} int val = 0; + public static StringTableColAlign new_(int v) { + StringTableColAlign rv = new StringTableColAlign(); + rv.val = v; + return rv; + } StringTableColAlign() {} + public static final StringTableColAlign Left = new_(0); + public static final StringTableColAlign Mid = new_(1); + public static final StringTableColAlign Right = new_(2); +} diff --git a/100_core/src/gplx/core/threads/Gfo_lock.java b/100_core/src/gplx/core/threads/Gfo_lock.java new file mode 100644 index 000000000..378156273 --- /dev/null +++ b/100_core/src/gplx/core/threads/Gfo_lock.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads; import gplx.*; import gplx.core.*; +import java.util.concurrent.locks.*; +public class Gfo_lock { + private final ReentrantLock lock = new ReentrantLock(true); + public void Lock() { + lock.lock(); + } + public void Unlock() { + lock.unlock(); + } +} diff --git a/100_core/src/gplx/core/threads/Thread_adp.java b/100_core/src/gplx/core/threads/Thread_adp.java new file mode 100644 index 000000000..4d218b1d8 --- /dev/null +++ b/100_core/src/gplx/core/threads/Thread_adp.java @@ -0,0 +1,46 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads; import gplx.*; import gplx.core.*; +import java.lang.*; +public class Thread_adp implements Runnable { + private final String thread_name; private final Cancelable cxl; private final boolean cxlable; + private final Gfo_invk invk_itm; private final String invk_cmd; private final GfoMsg invk_msg; + private Thread thread; + @gplx.Internal protected Thread_adp(String thread_name, Cancelable cxl, Gfo_invk invk_itm, String invk_cmd, GfoMsg invk_msg) { + this.thread_name = thread_name; this.cxl = cxl; this.cxlable = cxl != Cancelable_.Never; + this.invk_itm = invk_itm; this.invk_cmd = invk_cmd; this.invk_msg = invk_msg; + } + public String Thread__name() {return thread_name;} + public void Thread__cancel() {cxl.Cancel();} + public boolean Thread__cancelable() {return cxlable;} + public boolean Thread__is_alive() {return thread == null ? false : thread.isAlive();} + public void Thread__interrupt() {thread.interrupt();} + public void run() { + try { + Gfo_invk_.Invk_by_msg(invk_itm, invk_cmd, invk_msg); + } + catch (Exception e) { // catch exception + Gfo_log_.Instance.Warn("thread.failed", "thread_name", thread_name, "cmd", invk_cmd, "err", Err_.Message_gplx_log(e)); + } + } + public void Thread__start() { + this.thread = (thread_name == null) ? new Thread(this) : new Thread(this, thread_name); + thread.start(); + } + public static final Thread_adp Noop = new Thread_adp(Thread_adp_.Name_null, Cancelable_.Never, Gfo_invk_.Noop, "", GfoMsg_.Null); +} diff --git a/100_core/src/gplx/core/threads/Thread_adp_.java b/100_core/src/gplx/core/threads/Thread_adp_.java new file mode 100644 index 000000000..7a5ff1d60 --- /dev/null +++ b/100_core/src/gplx/core/threads/Thread_adp_.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads; import gplx.*; import gplx.core.*; +public class Thread_adp_ { + public static void Sleep(int milliseconds) { + try {Thread.sleep(milliseconds);} catch (InterruptedException e) {throw Err_.new_exc(e, "core", "thread interrupted", "milliseconds", milliseconds);} + } + public static Thread_adp Start_by_key(String thread_name, Gfo_invk invk_itm, String invk_cmd) {return Start(thread_name, Cancelable_.Never, invk_itm, invk_cmd , GfoMsg_.new_cast_(invk_cmd));} + public static Thread_adp Start_by_val(String thread_name, Gfo_invk invk_itm, String invk_cmd, Object val) {return Start(thread_name, Cancelable_.Never, invk_itm, invk_cmd , GfoMsg_.new_cast_(invk_cmd).Add("v", val));} + public static Thread_adp Start_by_msg(String thread_name, Gfo_invk invk_itm, GfoMsg invk_msg) {return Start(thread_name, Cancelable_.Never, invk_itm, invk_msg.Key() , invk_msg);} + public static Thread_adp Start_by_key(String thread_name, Cancelable cxl, Gfo_invk invk_itm, String invk_cmd) {return Start(thread_name, cxl, invk_itm, invk_cmd , GfoMsg_.new_cast_(invk_cmd));} + public static Thread_adp Start_by_val(String thread_name, Cancelable cxl, Gfo_invk invk_itm, String invk_cmd, Object val) {return Start(thread_name, cxl, invk_itm, invk_cmd , GfoMsg_.new_cast_(invk_cmd).Add("v", val));} + private static Thread_adp Start(String thread_name, Cancelable cxl, Gfo_invk invk_itm, String invk_key, GfoMsg invk_msg) { + Thread_adp rv = new Thread_adp(thread_name, cxl, invk_itm, invk_key, invk_msg); + rv.Thread__start(); + return rv; + } + public static final String Name_null = null; +} diff --git a/100_core/src/gplx/core/threads/Thread_adp_mgr.java b/100_core/src/gplx/core/threads/Thread_adp_mgr.java new file mode 100644 index 000000000..99e8c23cf --- /dev/null +++ b/100_core/src/gplx/core/threads/Thread_adp_mgr.java @@ -0,0 +1,61 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads; import gplx.*; import gplx.core.*; +public class Thread_adp_mgr { + private final Ordered_hash hash = Ordered_hash_.New(); + private final int sleep_time, quit_time; + public Thread_adp_mgr(int sleep_time, int quit_time) {this.sleep_time = sleep_time; this.quit_time = quit_time;} + public void Add(String key, Thread_adp thread) { + Thread_halt_itm itm = null; + synchronized (hash) { + itm = (Thread_halt_itm)hash.Get_by(key); + if (itm != null && !itm.Thread.Thread__is_alive()) + hash.Del(key); + } + itm = new Thread_halt_itm(key, thread); + hash.Add(key, itm); + } + public void Halt(String uid, Thread_halt_cbk cbk) { + Thread_halt_itm itm = (Thread_halt_itm)hash.Get_by(uid); + Halt_by_wkr(itm, cbk); + } + public void Halt_all(Thread_halt_cbk cbk) { + int len = hash.Len(); + for (int i = 0; i < len; ++i) { + Thread_halt_itm itm = (Thread_halt_itm)hash.Get_at(i); + Halt_by_wkr(itm, cbk); + } + } + private void Halt_by_wkr(Thread_halt_itm itm, Thread_halt_cbk cbk) { + Thread_halt_wkr halt_wkr = new Thread_halt_wkr(this, itm, cbk, sleep_time, quit_time); + Thread_adp_.Start_by_key("thread_mgr.halt", halt_wkr, Thread_halt_wkr.Invk__halt); + synchronized (hash) { + hash.Del(itm.Key); + } + } + public void Del(String key) { + synchronized (hash) { + hash.Del(key); + } + } +} +class Thread_halt_itm { + public Thread_halt_itm(String key, Thread_adp thread) {this.Key = key; this.Thread = thread;} + public final String Key; + public final Thread_adp Thread; +} diff --git a/100_core/src/gplx/core/threads/Thread_halt_cbk.java b/100_core/src/gplx/core/threads/Thread_halt_cbk.java new file mode 100644 index 000000000..5c54cb500 --- /dev/null +++ b/100_core/src/gplx/core/threads/Thread_halt_cbk.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads; import gplx.*; import gplx.core.*; +public interface Thread_halt_cbk { + void Thread__on_halt(boolean interrupted); +} diff --git a/100_core/src/gplx/core/threads/Thread_halt_cbk_.java b/100_core/src/gplx/core/threads/Thread_halt_cbk_.java new file mode 100644 index 000000000..9839eb5b2 --- /dev/null +++ b/100_core/src/gplx/core/threads/Thread_halt_cbk_.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads; import gplx.*; import gplx.core.*; +public class Thread_halt_cbk_ { + public static final Thread_halt_cbk Noop = new Thread_halt_cbk_noop(); +} +class Thread_halt_cbk_noop implements Thread_halt_cbk { + public void Thread__on_halt(boolean interrupted) {} +} diff --git a/100_core/src/gplx/core/threads/Thread_halt_wkr.java b/100_core/src/gplx/core/threads/Thread_halt_wkr.java new file mode 100644 index 000000000..df52bfe48 --- /dev/null +++ b/100_core/src/gplx/core/threads/Thread_halt_wkr.java @@ -0,0 +1,63 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads; import gplx.*; import gplx.core.*; +class Thread_halt_wkr implements Gfo_invk { + private final Thread_adp_mgr mgr; + private final Thread_adp thread; private final String thread_key; private final Thread_halt_cbk cbk; + private final long bgn_time; + private final int sleep_time, quit_time; + public Thread_halt_wkr(Thread_adp_mgr mgr, Thread_halt_itm itm, Thread_halt_cbk cbk, int sleep_time, int quit_time) { + this.mgr = mgr; this.thread = itm.Thread; this.thread_key = itm.Key; this.cbk = cbk; + this.sleep_time = sleep_time; this.quit_time = quit_time; + this.bgn_time = gplx.core.envs.System_.Ticks(); + } + private void Halt() { + // first, cancel the thread + thread.Thread__cancel(); + + // now check if canceled; interrupt if not; + while (true) { + long time_now = gplx.core.envs.System_.Ticks(); + boolean halted = false, interrupted = false; + if (thread.Thread__is_alive()) { // thread is still alive + if ( !thread.Thread__cancelable() // itm is not cancelable + || time_now > bgn_time + quit_time // itm is cancelable, but too much time passed + ) { + thread.Thread__interrupt(); // interrupt it + halted = interrupted = true; + } + } + else + halted = true; + + if (halted) { // thread halted; call cbk; + cbk.Thread__on_halt(interrupted); + mgr.Del(thread_key); + break; + } + else // else sleep and try again + Thread_adp_.Sleep(sleep_time); + } + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk__halt)) Halt(); + else return Gfo_invk_.Rv_unhandled; + return this; + } + public static final String Invk__halt = "halt"; +} diff --git a/100_core/src/gplx/core/threads/poolables/Gfo_poolable_itm.java b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_itm.java new file mode 100644 index 000000000..586242ae1 --- /dev/null +++ b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_itm.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads.poolables; import gplx.*; import gplx.core.*; import gplx.core.threads.*; +public interface Gfo_poolable_itm { + Gfo_poolable_itm Pool__make (Gfo_poolable_mgr mgr, int idx, Object[] args); + void Pool__rls(); +} diff --git a/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr.java b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr.java new file mode 100644 index 000000000..bd4dee9e1 --- /dev/null +++ b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr.java @@ -0,0 +1,98 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads.poolables; import gplx.*; import gplx.core.*; import gplx.core.threads.*; +import gplx.core.memorys.*; +public class Gfo_poolable_mgr implements Gfo_memory_itm { + private final Object thread_lock = new Object(); + private final Gfo_poolable_itm prototype; private final Object[] make_args; + private Gfo_poolable_itm[] pool; private int pool_nxt, pool_len; + public Gfo_poolable_mgr(Gfo_poolable_itm prototype, Object[] make_args, int init_pool_len, int pool_max) {// NOTE: random IndexOutOfBounds errors in Get around free_ary[--free_len] with free_len being -1; put member variable initialization within thread_lock to try to avoid; DATE:2014-09-21 + this.prototype = prototype; this.make_args = make_args; + this.pool_len = init_pool_len; + this.Clear_fast(); + } + public void Rls_mem() {Clear_safe();} + public void Clear_safe() {synchronized (thread_lock) {Clear_fast();}} + public void Clear_fast() { + this.pool = new Gfo_poolable_itm[pool_len]; + for (int i = 0; i < pool_len; ++i) + pool[i] = prototype.Pool__make(this, i, make_args); + this.free_ary = new int[pool_len]; + pool_nxt = free_len = 0; + } + public Gfo_poolable_itm Get_safe() {synchronized (thread_lock) {return Get_fast();}} + public Gfo_poolable_itm Get_fast() { + Gfo_poolable_itm rv = null; + int pool_idx = -1; + if (free_len > 0) { // free_itms in pool; use it + pool_idx = free_ary[--free_len]; + rv = pool[pool_idx]; + } + else { // nothing in pool; take next + if (pool_nxt == pool_len) + Expand_pool(); + pool_idx = pool_nxt++; + rv = pool[pool_idx]; + if (rv == null) { + rv = prototype.Pool__make(this, pool_idx, make_args); + pool[pool_idx] = rv; + } + } + return rv; + } + public void Rls_safe(int idx) {synchronized (thread_lock) {Rls_fast(idx);}} + public void Rls_fast(int idx) { + if (idx == -1) throw Err_.new_wo_type("rls called on poolable that was not created by pool_mgr"); + int pool_idx = pool_nxt - 1; + if (idx == pool_idx) // in-sequence; decrement count + if (free_len == 0) + this.pool_nxt = pool_idx; + else { // idx == pool_idx; assume entire pool released, but out of order + for (int i = 0; i < free_len; ++i) + free_ary[i] = 0; + free_len = 0; + } + else { // out-of-sequence + free_ary[free_len] = idx; + ++free_len; + } + } + private void Expand_pool() { + // expand pool + int new_pool_len = pool_len == 0 ? 2 : pool_len * 2; + Gfo_poolable_itm[] new_pool = new Gfo_poolable_itm[new_pool_len]; + Array_.Copy_to(pool, 0, new_pool, 0, pool_len); + this.pool = new_pool; + this.pool_len = new_pool_len; + + // expand free_ary to same len + int[] new_free = new int[pool_len]; + Array_.Copy_to(free_ary, 0, new_free, 0, free_len); + this.free_ary = new_free; + } + @gplx.Internal protected int[] Free_ary() {return free_ary;} private int[] free_ary; private int free_len; + @gplx.Internal protected int Free_len() {return free_len;} + @gplx.Internal protected int Pool_len() {return pool_len;} + @gplx.Internal protected int Pool_nxt() {return pool_nxt;} + + public static Gfo_poolable_mgr New_rlsable(Gfo_poolable_itm prototype, Object[] make_args, int init_pool_len, int pool_max) { + Gfo_poolable_mgr rv = new Gfo_poolable_mgr(prototype, make_args, init_pool_len, pool_max); + Gfo_memory_mgr.Instance.Reg_safe(rv); + return rv; + } +} diff --git a/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_.java b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_.java new file mode 100644 index 000000000..c86ff4bac --- /dev/null +++ b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads.poolables; import gplx.*; import gplx.core.*; import gplx.core.threads.*; +public class Gfo_poolable_mgr_ { + public static Gfo_poolable_mgr New(int len, int max, Gfo_poolable_itm prototype) {return new Gfo_poolable_mgr(prototype, Object_.Ary_empty, len, max);} + public static Gfo_poolable_mgr New(int len, int max, Gfo_poolable_itm prototype, Object[] make_args) {return new Gfo_poolable_mgr(prototype, make_args, len, max);} +} diff --git a/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_tst.java b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_tst.java new file mode 100644 index 000000000..4c63b5be8 --- /dev/null +++ b/100_core/src/gplx/core/threads/poolables/Gfo_poolable_mgr_tst.java @@ -0,0 +1,83 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads.poolables; import gplx.*; import gplx.core.*; import gplx.core.threads.*; +import org.junit.*; +public class Gfo_poolable_mgr_tst { + private final Gfo_poolable_mgr_tstr tstr = new Gfo_poolable_mgr_tstr(); + @Before public void init() {tstr.Clear();} + @Test public void Get__one() { + tstr.Test__get(0); + tstr.Test__free__len(0); + tstr.Test__pool__len(2); + } + @Test public void Get__many__expand() { + tstr.Test__get(0); + tstr.Test__get(1); + tstr.Test__get(2); + tstr.Test__free__len(0); + tstr.Test__pool__len(4); + } + @Test public void Rls__lifo() { + tstr.Test__get(0); + tstr.Test__get(1); + tstr.Test__get(2); + tstr.Exec__rls(2); + tstr.Exec__rls(1); + tstr.Exec__rls(0); + tstr.Test__pool__nxt(0); + tstr.Test__free__len(0); + } + @Test public void Rls__fifo() { + tstr.Test__get(0); + tstr.Test__get(1); + tstr.Test__get(2); + tstr.Exec__rls(0); + tstr.Exec__rls(1); + tstr.Test__pool__nxt(3); + tstr.Test__free__len(2); // 2 items in free_ary + tstr.Test__free__ary(0, 1, 0, 0); + + tstr.Test__get(1); + tstr.Exec__rls(1); + + tstr.Exec__rls(2); + tstr.Test__free__len(0); // 0 items in free_ary + tstr.Test__free__ary(0, 0, 0, 0); + } +} +class Gfo_poolable_mgr_tstr { + private final Gfo_poolable_mgr mgr = new Gfo_poolable_mgr(new Sample_poolable_itm(null, -1, Object_.Ary_empty), Object_.Ary("make"), 2, 8); + public void Clear() {mgr.Clear_fast();} + public void Test__get(int expd_idx) { + Sample_poolable_itm actl_itm = (Sample_poolable_itm)mgr.Get_fast(); + Tfds.Eq(expd_idx, actl_itm.Pool__idx(), "pool_idx"); + } + public void Test__free__ary(int... expd) {Tfds.Eq_ary(expd, mgr.Free_ary(), "mgr.Free_ary()");} + public void Test__free__len(int expd) {Tfds.Eq(expd, mgr.Free_len(), "mgr.Free_len()");} + public void Test__pool__len(int expd) {Tfds.Eq(expd, mgr.Pool_len(), "mgr.Pool_len()");} + public void Test__pool__nxt(int expd) {Tfds.Eq(expd, mgr.Pool_nxt(), "mgr.Pool_nxt()");} + public void Exec__rls(int idx) {mgr.Rls_fast(idx);} +} +class Sample_poolable_itm implements Gfo_poolable_itm { + private Gfo_poolable_mgr pool_mgr; + public Sample_poolable_itm(Gfo_poolable_mgr pool_mgr, int pool_idx, Object[] make_args) {this.pool_mgr = pool_mgr; this.pool_idx = pool_idx; this.pool__make_args = make_args;} + public int Pool__idx() {return pool_idx;} private final int pool_idx; + public Object[] Pool__make_args() {return pool__make_args;} private final Object[] pool__make_args; + public void Pool__rls() {pool_mgr.Rls_safe(pool_idx);} + public Gfo_poolable_itm Pool__make (Gfo_poolable_mgr mgr, int idx, Object[] args) {return new Sample_poolable_itm(pool_mgr, idx, args);} +} diff --git a/100_core/src/gplx/core/threads/utils/Gfo_blocking_queue.java b/100_core/src/gplx/core/threads/utils/Gfo_blocking_queue.java new file mode 100644 index 000000000..08d33be03 --- /dev/null +++ b/100_core/src/gplx/core/threads/utils/Gfo_blocking_queue.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads.utils; import gplx.*; import gplx.core.*; import gplx.core.threads.*; +import java.util.concurrent.ArrayBlockingQueue; +public class Gfo_blocking_queue { + private final ArrayBlockingQueue queue; + public Gfo_blocking_queue(int capacity) { + this.capacity = capacity; + this.queue = new ArrayBlockingQueue(capacity); + } + public int Capacity() {return capacity;} private final int capacity; + public void Put(Object o) { + try {queue.put(o);} + catch (InterruptedException e) {throw Err_.new_exc(e, "threads", "put interrupted");} + } + public Object Take() { + try {return queue.take();} + catch (InterruptedException e) {throw Err_.new_exc(e, "threads", "take interrupted");} + } +} diff --git a/100_core/src/gplx/core/threads/utils/Gfo_countdown_latch.java b/100_core/src/gplx/core/threads/utils/Gfo_countdown_latch.java new file mode 100644 index 000000000..1845fa8ee --- /dev/null +++ b/100_core/src/gplx/core/threads/utils/Gfo_countdown_latch.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.threads.utils; import gplx.*; import gplx.core.*; import gplx.core.threads.*; +import java.util.concurrent.CountDownLatch; +public class Gfo_countdown_latch { + private final CountDownLatch latch; + public Gfo_countdown_latch(int count) { + latch = new CountDownLatch(count); + } + public void Countdown() { + latch.countDown(); + } + public void Await() { + try {latch.await();} + catch (InterruptedException e) {throw Err_.new_exc(e, "threads", "await interrupted");} + } +} diff --git a/100_core/src/gplx/core/times/DateAdp_parser.java b/100_core/src/gplx/core/times/DateAdp_parser.java new file mode 100644 index 000000000..e43900941 --- /dev/null +++ b/100_core/src/gplx/core/times/DateAdp_parser.java @@ -0,0 +1,120 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.times; import gplx.*; import gplx.core.*; +public class DateAdp_parser { + public int[] Parse_iso8651_like(String raw_str) {Parse_iso8651_like(tmp_rv, raw_str); return tmp_rv;} int[] tmp_rv = new int[7]; + public void Parse_iso8651_like(int[] rv, String raw_str) { + byte[] raw_bry = Bry_.new_u8(raw_str); + Parse_iso8651_like(rv, raw_bry, 0, raw_bry.length); + } + public void Parse_iso8651_like(int[] rv, byte[] src, int bgn, int end) { + /* 1981-04-05T14:30:30.000-05:00 ISO 8601: http://en.wikipedia.org/wiki/ISO_8601 + 1981.04.05 14.30.30.000-05.00 ISO 8601 loose + 99991231_235959.999 gplx + yyyy-MM-ddTHH:mm:ss.fffzzzz Format String */ + int rv_len = rv.length; + for (int i = 0; i < rv_len; i++) + rv[i] = 0; // .Clear + int pos = bgn, rv_idx = 0, int_len = 0, max_len = max_lens[rv_idx]; + while (true) { + int int_val = -1; + byte b = pos < end ? src[pos] : Byte_ascii.Null; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + int_val = b - Byte_ascii.Num_0; // convert ascii to val; EX: 49 -> 1 + int_len = int_bldr.Add(int_val); + break; + } + if ( (int_val == -1 && int_len > 0) // char is not number && number exists (ignore consecutive delimiters: EX: "1981 01") + || int_len == max_len) { // max_len reached; necessary for gplxFormat + rv[rv_idx++] = int_bldr.BldAndClear(); + if (rv_idx == 7) break; // past frac; break; + int_len = 0; + max_len = max_lens[rv_idx]; + } + if (pos == end) break; + ++pos; + } + } + int[] max_lens = new int[] {4/*y*/,2/*M*/,2/*d*/,2/*H*/,2/*m*/,2/*s*/,3/*S*/,0}; + IntBldr int_bldr = IntBldr.new_(4); + public static DateAdp_parser new_() {return new DateAdp_parser();} DateAdp_parser() {} +} +class IntBldr { + public int Add(char c) { + if (idx > digitsLen - 1) throw Err_.new_missing_idx(idx, digitsLen); + digits[idx++] = XbyChar(c); + return idx; + } + public int Add(int i) { + if (idx > digitsLen - 1) throw Err_.new_missing_idx(idx, digitsLen); + digits[idx++] = i; + return idx; + } + public int Parse(String raw) { + ParseStr(raw); + try {return Bld();} + catch (Exception exc) {throw Err_.new_parse_exc(exc, int.class, raw);} + } + public boolean ParseTry(String raw) { + ParseStr(raw); + for (int i = 0; i < idx; i++) + if (digits[i] < 0) return false; + return true; + } + public int Bld() { + int rv = 0, exponent = 1; + for (int i = idx - 1; i > -1; i--) { + int digit = digits[i]; + if (digit < 0) throw Err_.new_wo_type("invalid char", "char", (char)-digits[i], "ascii", -digits[i]); + rv += digit * exponent; + exponent *= 10; + } + return sign * rv; + } + public int BldAndClear() { + int rv = Bld(); + this.Clear(); + return rv; + } + public void Clear() {idx = 0; sign = 1;} + void ParseStr(String raw) { + this.Clear(); + int rawLength = String_.Len(raw); + for (int i = 0; i < rawLength; i++) { + char c = String_.CharAt(raw, i); + if (i == 0 && c == '-') + sign = -1; + else + Add(c); + } + } + int XbyChar(char c) { + if (c == '0') return 0; else if (c == '1') return 1; else if (c == '2') return 2; else if (c == '3') return 3; else if (c == '4') return 4; + else if (c == '5') return 5; else if (c == '6') return 6; else if (c == '7') return 7; else if (c == '8') return 8; else if (c == '9') return 9; + else return -(int)c; // error handling: don't throw error; store ascii value in digit and throw if needed + } + int[] digits; int idx, digitsLen; int sign = 1; + public static IntBldr new_(int digitsMax) { + IntBldr rv = new IntBldr(); + rv.digits = new int[digitsMax]; + rv.digitsLen = digitsMax; + return rv; + } +} diff --git a/100_core/src/gplx/core/times/DateAdp_parser_tst.java b/100_core/src/gplx/core/times/DateAdp_parser_tst.java new file mode 100644 index 000000000..55fe9599b --- /dev/null +++ b/100_core/src/gplx/core/times/DateAdp_parser_tst.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.times; import gplx.*; import gplx.core.*; +import org.junit.*; +public class DateAdp_parser_tst { + @Before public void init() {} DateAdp_parser_fxt fxt = new DateAdp_parser_fxt(); + @Test public void Parse_gplx() { + fxt.Test_Parse_iso8651_like("2000-01-02T03:04:05.006-05:00" , 2000, 1, 2, 3, 4, 5, 6); + fxt.Test_Parse_iso8651_like("2000-01-02" , 2000, 1, 2, 0, 0, 0, 0); + } +} +class DateAdp_parser_fxt { + DateAdp_parser parser = DateAdp_parser.new_(); int[] actl = new int[7]; + public void Test_Parse_iso8651_like(String s, int... expd) { + byte[] bry = Bry_.new_a7(s); + parser.Parse_iso8651_like(actl, bry, 0, bry.length); + Tfds.Eq_ary(expd, actl, s); + } +} diff --git a/100_core/src/gplx/core/times/Time_span__basic_tst.java b/100_core/src/gplx/core/times/Time_span__basic_tst.java new file mode 100644 index 000000000..151050113 --- /dev/null +++ b/100_core/src/gplx/core/times/Time_span__basic_tst.java @@ -0,0 +1,87 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.times; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Time_span__basic_tst { + @Test public void seconds_() { + Time_span expd = Time_span_.fracs_(123987); + Time_span actl = Time_span_.seconds_(123.987); + Tfds.Eq(expd, actl); + } + @Test public void TotalSecs() { + Time_span val = Time_span_.fracs_(1987); + Tfds.Eq_decimal(Decimal_adp_.parts_(1, 987), val.Total_secs()); + } + @Test public void Units() { + tst_Units("01:02:03.987", 1, 2, 3, 987); + tst_Units("01:00:03", 1, 0, 3, 0); + tst_Units("01:00:00.987", 1, 0, 0, 987); + tst_Units("02:00.987", 0, 2, 0, 987); + } + @Test public void Add() { + Time_span val = Time_span_.fracs_(3); + Time_span arg = Time_span_.fracs_(2); + Time_span expd = Time_span_.fracs_(5); + Time_span actl = val.Add(arg); + Tfds.Eq(expd, actl); + } + @Test public void Subtract() { + Time_span val = Time_span_.fracs_(3); + Time_span arg = Time_span_.fracs_(2); + Time_span expd = Time_span_.fracs_(1); + Time_span actl = val.Subtract(arg); + Tfds.Eq(expd, actl); + } + @Test public void Add_unit_identity() { + tst_AddUnit("00:00:01.000", 0, 0, "00:00:01.000"); + } + @Test public void Add_unit_basic() { + tst_AddUnit("01:59:58.987", 0, 1013, "02:00:00.000"); + tst_AddUnit("01:59:58.987", 1, 2, "02:00:00.987"); + tst_AddUnit("01:59:58.987", 2, 1, "02:00:58.987"); + tst_AddUnit("01:59:58.987", 3, 1, "02:59:58.987"); + } + @Test public void Add_unit_negative() { + tst_AddUnit("01:00:00.00", 0, -1, "00:59:59.999"); + tst_AddUnit("01:00:00.00", 1, -1, "00:59:59.000"); + tst_AddUnit("01:00:00.00", 2, -1, "00:59:00.000"); + tst_AddUnit("01:00:00.00", 3, -1, "00:00:00.000"); + } + @Test public void XtoStrUiAbbrv() { + tst_XtoStrUiAbbrv("01:02:03.004", "1h 2m 3s 4f"); + tst_XtoStrUiAbbrv("00:00:03.004", "3s 4f"); + tst_XtoStrUiAbbrv("00:00:03.000", "3s 0f"); + tst_XtoStrUiAbbrv("11:22:33.444", "11h 22m 33s 444f"); + tst_XtoStrUiAbbrv("00:00:00.000", "0f"); + } void tst_XtoStrUiAbbrv(String raw, String expd) {Tfds.Eq(expd, Time_span_.parse(raw).XtoStrUiAbbrv());} + void tst_AddUnit(String valRaw, int unitIdx, int delta, String expdRaw) { + Time_span val = Time_span_.parse(valRaw); + Time_span actl = val.Add_unit(unitIdx, delta); + Tfds.Eq(Time_span_.parse(expdRaw), actl); + } + void tst_Units(String text, int... expd) { + Time_span val = Time_span_.parse(text); + int hour = 0, min = 0, sec = 0, frac = 0; + int[] ary = val.Units(); + hour = ary[Time_span_.Idx_Hour]; min = ary[Time_span_.Idx_Min]; sec = ary[Time_span_.Idx_Sec]; frac = ary[Time_span_.Idx_Frac]; + Tfds.Eq(expd[0], hour); + Tfds.Eq(expd[1], min); + Tfds.Eq(expd[2], sec); + Tfds.Eq(expd[3], frac); + } +} diff --git a/100_core/src/gplx/core/times/Time_span__parse_tst.java b/100_core/src/gplx/core/times/Time_span__parse_tst.java new file mode 100644 index 000000000..e6967e507 --- /dev/null +++ b/100_core/src/gplx/core/times/Time_span__parse_tst.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.times; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Time_span__parse_tst { + @Test public void Zero() { + tst_Parse("0", 0); + } + @Test public void Milliseconds() { + tst_Parse("0.987", 987); + tst_Parse("0.00199", 1); // do not parse as 199 + tst_Parse("0.1", 100); // do not parse as 1 + } + @Test public void Seconds() { + tst_Parse("1.987", 1987); + } + @Test public void Minutes() { + tst_Parse("1:02.987", 62987); + } + @Test public void MinuteSecondOnly() { + tst_Parse("1:02", 62000); + } + @Test public void Hour() { + tst_Parse("1:02:03.987", 3723987); + } + @Test public void Negative() { + tst_Parse("-1:02:03.987", -3723987); + } + @Test public void Loopholes() { + tst_Parse("001:02", 62000); // multiple leading zeroes + tst_Parse("1.2.3.4", 1200); // ignore all decimals except first + tst_Parse("60:60.9999", 3660999); // value does not need to be bounded to limits (except fracs, which is always < 1000) + tst_Parse(" 01 : 02 : 03 . 987", 3723987); // whitespace + } + void tst_Parse(String text, long expd) { + Time_span val = Time_span_.parse(text); + Tfds.Eq(expd, val.Fracs()); + } +} diff --git a/100_core/src/gplx/core/times/Time_span__to_str_tst.java b/100_core/src/gplx/core/times/Time_span__to_str_tst.java new file mode 100644 index 000000000..ef7edd9e4 --- /dev/null +++ b/100_core/src/gplx/core/times/Time_span__to_str_tst.java @@ -0,0 +1,59 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.times; import gplx.*; import gplx.core.*; +import org.junit.*; +public class Time_span__to_str_tst { + @Test public void Zero() { + tst_Default(0, "0"); + } + @Test public void MinuteSeconds() { + tst_Default(77000, "1:17"); + } + @Test public void ZeroSuppression() { + tst_Default(660000, "11:00"); //fractional 0 and leading 0s are suppressed; i.e.: not 00:11:00.000 + } + @Test public void HourTest() { + tst_Default(3723987, "1:02:03.987"); + } + @Test public void NegSeconds() { + tst_Default(-2000, "-2"); + } + @Test public void NegMins() { + tst_Default(-60000, "-1:00"); + } + @Test public void NegHours() { + tst_Default(-3723981, "-1:02:03.981"); + } + @Test public void ZeroPadding() { + tst_ZeroPadding("0", "00:00:00.000"); + tst_ZeroPadding("1:02:03.123", "01:02:03.123"); + tst_ZeroPadding("1", "00:00:01.000"); + tst_ZeroPadding(".987", "00:00:00.987"); + tst_ZeroPadding("2:01.456", "00:02:01.456"); + } + void tst_Default(long fractionals, String expd) { + Time_span ts = Time_span_.fracs_(fractionals); + String actl = ts.To_str(Time_span_.Fmt_Default); + Tfds.Eq(expd, actl); + } + void tst_ZeroPadding(String val, String expd) { + Time_span timeSpan = Time_span_.parse(val); + String actl = timeSpan.To_str(Time_span_.Fmt_PadZeros); + Tfds.Eq(expd, actl); + } +} diff --git a/100_core/src/gplx/core/type_xtns/BoolClassXtn.java b/100_core/src/gplx/core/type_xtns/BoolClassXtn.java new file mode 100644 index 000000000..92b239568 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/BoolClassXtn.java @@ -0,0 +1,41 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class BoolClassXtn extends ClassXtn_base implements ClassXtn { + public static final String Key_const = "bo" + "ol"; + public String Key() {return Key_const;} + @Override public Class UnderClass() {return boolean.class;} + public Object DefaultValue() {return false;} + public boolean Eq(Object lhs, Object rhs) {try {return Bool_.cast(lhs) == Bool_.cast(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) { + if ( String_.Eq(raw, "true") + || String_.Eq(raw, "True") // needed for Store_Wtr() {boolVal.toString();} + || String_.Eq(raw, "1") // needed for db; gplx field for boolean is int; need simple way to convert from dbInt to langBool + ) + return true; + else if + ( String_.Eq(raw, "false") + || String_.Eq(raw, "False") + || String_.Eq(raw, "0") + ) + return false; + throw Err_.new_parse_type(boolean.class, raw); + } + @Override public Object XtoDb(Object obj) {return obj;} + public static final BoolClassXtn Instance = new BoolClassXtn(); BoolClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src/gplx/core/type_xtns/ByteClassXtn.java b/100_core/src/gplx/core/type_xtns/ByteClassXtn.java new file mode 100644 index 000000000..a874c0060 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/ByteClassXtn.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class ByteClassXtn extends ClassXtn_base implements ClassXtn { + public static final String Key_const = "byte"; + public String Key() {return Key_const;} + @Override public Class UnderClass() {return byte.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Byte_.cast(lhs) == Byte_.cast(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return raw == null ? (Object)null : Byte_.parse(raw);} + @Override public Object XtoDb(Object obj) {return Byte_.cast(obj);} + public static final ByteClassXtn Instance = new ByteClassXtn(); ByteClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src/gplx/core/type_xtns/ClassXtn.java b/100_core/src/gplx/core/type_xtns/ClassXtn.java new file mode 100644 index 000000000..e2ac23f7a --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/ClassXtn.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public interface ClassXtn { + String Key(); + Class UnderClass(); + Object DefaultValue(); + Object ParseOrNull(String raw); + Object XtoDb(Object obj); + String XtoUi(Object obj, String fmt); + boolean MatchesClass(Object obj); + boolean Eq(Object lhs, Object rhs); + int compareTo(Object lhs, Object rhs); +} diff --git a/100_core/src/gplx/core/type_xtns/ClassXtnPool.java b/100_core/src/gplx/core/type_xtns/ClassXtnPool.java new file mode 100644 index 000000000..b6cc16cbb --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/ClassXtnPool.java @@ -0,0 +1,39 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +import gplx.core.lists.*; +public class ClassXtnPool extends Hash_adp_base { + public void Add(ClassXtn typx) {Add_base(typx.Key(), typx);} + public ClassXtn Get_by_or_fail(String key) {return (ClassXtn)Get_by_or_fail_base(key);} + + public static final ClassXtnPool Instance = new ClassXtnPool(); + public static final String Format_null = ""; + public static ClassXtnPool new_() {return new ClassXtnPool();} + ClassXtnPool() { + Add(ObjectClassXtn.Instance); + Add(StringClassXtn.Instance); + Add(IntClassXtn.Instance); + Add(BoolClassXtn.Instance); + Add(ByteClassXtn.Instance); + Add(DateAdpClassXtn.Instance); + Add(TimeSpanAdpClassXtn.Instance); + Add(IoUrlClassXtn.Instance); + Add(DecimalAdpClassXtn.Instance); + Add(FloatClassXtn.Instance); + } +} diff --git a/100_core/src/gplx/core/type_xtns/ClassXtn_base.java b/100_core/src/gplx/core/type_xtns/ClassXtn_base.java new file mode 100644 index 000000000..7b92fc58f --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/ClassXtn_base.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public abstract class ClassXtn_base { + public abstract Class UnderClass(); + public abstract Object ParseOrNull(String raw); + @gplx.Virtual public Object XtoDb(Object obj) {return obj;} + @gplx.Virtual public String XtoUi(Object obj, String fmt) {return Object_.Xto_str_strict_or_null_mark(obj);} + @gplx.Virtual public boolean MatchesClass(Object obj) {if (obj == null) throw Err_.new_null(); + return Type_adp_.Eq_typeSafe(obj, UnderClass()); + } + @gplx.Virtual public int compareTo(Object lhs, Object rhs) {return CompareAble_.Compare_obj(lhs, rhs);} +} diff --git a/100_core/src/gplx/core/type_xtns/DateAdpClassXtn.java b/100_core/src/gplx/core/type_xtns/DateAdpClassXtn.java new file mode 100644 index 000000000..598828521 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/DateAdpClassXtn.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class DateAdpClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "datetime"; + public boolean Eq(Object lhs, Object rhs) {try {return DateAdp_.cast(lhs).Eq(DateAdp_.cast(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Class UnderClass() {return DateAdp.class;} + public Object DefaultValue() {return DateAdp_.MinValue;} + @Override public Object ParseOrNull(String raw) {return DateAdp_.parse_gplx(raw);} + @Override public Object XtoDb(Object obj) {return DateAdp_.cast(obj).XtoStr_gplx_long();} + @Override public String XtoUi(Object obj, String fmt) {return DateAdp_.cast(obj).XtoStr_fmt(fmt);} + public static final DateAdpClassXtn Instance = new DateAdpClassXtn(); DateAdpClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src/gplx/core/type_xtns/DateAdpClassXtn_tst.java b/100_core/src/gplx/core/type_xtns/DateAdpClassXtn_tst.java new file mode 100644 index 000000000..8d5f5a7ad --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/DateAdpClassXtn_tst.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +import org.junit.*; +public class DateAdpClassXtn_tst { + @Test public void XtoDb() { + tst_XtoDb("20091115 220102.999", "2009-11-15 22:01:02.999"); + } + void tst_XtoDb(String val, String expdRaw) { + String actlRaw = (String)DateAdpClassXtn.Instance.XtoDb(DateAdp_.parse_gplx(val)); + Tfds.Eq(expdRaw, actlRaw); + } +} diff --git a/100_core/src/gplx/core/type_xtns/DecimalAdpClassXtn.java b/100_core/src/gplx/core/type_xtns/DecimalAdpClassXtn.java new file mode 100644 index 000000000..73485e6a6 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/DecimalAdpClassXtn.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class DecimalAdpClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "decimal"; // current dsv files reference "decimal" + @Override public Class UnderClass() {return Decimal_adp.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Decimal_adp_.cast(lhs).Eq(Decimal_adp_.cast(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return Decimal_adp_.parse(raw);} + @Override public String XtoUi(Object obj, String fmt) {return Decimal_adp_.cast(obj).To_str();} + public static final DecimalAdpClassXtn Instance = new DecimalAdpClassXtn(); DecimalAdpClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src/gplx/core/type_xtns/DoubleClassXtn.java b/100_core/src/gplx/core/type_xtns/DoubleClassXtn.java new file mode 100644 index 000000000..81e82064c --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/DoubleClassXtn.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class DoubleClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "double"; + @Override public Class UnderClass() {return double.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Double_.cast(lhs) == Double_.cast(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return Double_.parse(raw);} + public static final DoubleClassXtn Instance = new DoubleClassXtn(); DoubleClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src/gplx/core/type_xtns/FloatClassXtn.java b/100_core/src/gplx/core/type_xtns/FloatClassXtn.java new file mode 100644 index 000000000..4522d4279 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/FloatClassXtn.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class FloatClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "float"; + @Override public Class UnderClass() {return float.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Float_.cast(lhs) == Float_.cast(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return Float_.parse(raw);} + public static final FloatClassXtn Instance = new FloatClassXtn(); FloatClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src/gplx/core/type_xtns/IntClassXtn.java b/100_core/src/gplx/core/type_xtns/IntClassXtn.java new file mode 100644 index 000000000..25bfc9b8d --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/IntClassXtn.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class IntClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "int"; + @Override public Class UnderClass() {return Integer.class;} + public Object DefaultValue() {return 0;} + @Override public Object ParseOrNull(String raw) {return raw == null ? (Object)null : Int_.parse(raw);} + public boolean Eq(Object lhs, Object rhs) {try {return Int_.cast(lhs) == Int_.cast(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object XtoDb(Object obj) {return Int_.cast(obj);} // necessary for enums + + public static final IntClassXtn Instance = new IntClassXtn(); IntClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src/gplx/core/type_xtns/IoUrlClassXtn.java b/100_core/src/gplx/core/type_xtns/IoUrlClassXtn.java new file mode 100644 index 000000000..7c3e16e61 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/IoUrlClassXtn.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class IoUrlClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "ioPath"; + @Override public Class UnderClass() {return Io_url.class;} + public Object DefaultValue() {return Io_url_.Empty;} + @Override public Object ParseOrNull(String raw) {return Io_url_.new_any_(raw);} + @Override public Object XtoDb(Object obj) {return Io_url_.cast(obj).Raw();} + @Override public String XtoUi(Object obj, String fmt) {return Io_url_.cast(obj).Raw();} + @Override public boolean MatchesClass(Object obj) {return Io_url_.as_(obj) != null;} + public boolean Eq(Object lhs, Object rhs) {try {return Io_url_.cast(lhs).Eq(Io_url_.cast(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + public static final IoUrlClassXtn Instance = new IoUrlClassXtn(); IoUrlClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src/gplx/core/type_xtns/LongClassXtn.java b/100_core/src/gplx/core/type_xtns/LongClassXtn.java new file mode 100644 index 000000000..3169b6842 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/LongClassXtn.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class LongClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "long"; + @Override public Class UnderClass() {return long.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Long_.cast(lhs) == Long_.cast(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return raw == null ? (Object)null : Long_.parse(raw);} + @Override public Object XtoDb(Object obj) {return Long_.cast(obj);} // necessary for enums + public static final LongClassXtn Instance = new LongClassXtn(); LongClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src/gplx/core/type_xtns/ObjectClassXtn.java b/100_core/src/gplx/core/type_xtns/ObjectClassXtn.java new file mode 100644 index 000000000..48e9a45c1 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/ObjectClassXtn.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class ObjectClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "Object"; + @Override public Class UnderClass() {return Object.class;} + public Object DefaultValue() {return null;} + @Override public Object ParseOrNull(String raw) {throw Err_.new_unimplemented();} + @Override public Object XtoDb(Object obj) {throw Err_.new_unimplemented();} + public boolean Eq(Object lhs, Object rhs) {return lhs == rhs;} + public static final ObjectClassXtn Instance = new ObjectClassXtn(); ObjectClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src/gplx/core/type_xtns/StringClassXtn.java b/100_core/src/gplx/core/type_xtns/StringClassXtn.java new file mode 100644 index 000000000..f85e3f319 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/StringClassXtn.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class StringClassXtn extends ClassXtn_base implements ClassXtn { + public static final String Key_const = "string"; + public String Key() {return Key_const;} + @Override public Class UnderClass() {return String.class;} + public Object DefaultValue() {return "";} + @Override public Object ParseOrNull(String raw) {return raw;} + @Override public String XtoUi(Object obj, String fmt) {return String_.as_(obj);} + public boolean Eq(Object lhs, Object rhs) {try {return String_.Eq(String_.cast(lhs), String_.cast(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + public static final StringClassXtn Instance = new StringClassXtn(); StringClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src/gplx/core/type_xtns/TimeSpanAdpClassXtn.java b/100_core/src/gplx/core/type_xtns/TimeSpanAdpClassXtn.java new file mode 100644 index 000000000..64ea04311 --- /dev/null +++ b/100_core/src/gplx/core/type_xtns/TimeSpanAdpClassXtn.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.type_xtns; import gplx.*; import gplx.core.*; +public class TimeSpanAdpClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "timeSpan"; + @Override public Class UnderClass() {return Time_span.class;} + public Object DefaultValue() {return Time_span_.Zero;} + @Override public Object ParseOrNull(String raw) {return Time_span_.parse(raw);} + @Override public Object XtoDb(Object obj) {return Time_span_.cast(obj).Total_secs();} + @Override public String XtoUi(Object obj, String fmt) {return Time_span_.cast(obj).To_str(fmt);} + public boolean Eq(Object lhs, Object rhs) {try {return Time_span_.cast(lhs).Eq(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + public static final TimeSpanAdpClassXtn Instance = new TimeSpanAdpClassXtn(); TimeSpanAdpClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src/gplx/langs/dsvs/DsvDataRdrOpts.java b/100_core/src/gplx/langs/dsvs/DsvDataRdrOpts.java new file mode 100644 index 000000000..635eaeb79 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataRdrOpts.java @@ -0,0 +1,26 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import gplx.core.gfo_ndes.*; +public class DsvDataRdrOpts { + public boolean HasHeader() {return hasHeader;} public DsvDataRdrOpts HasHeader_(boolean val) {hasHeader = val; return this;} private boolean hasHeader = false; + public String NewLineSep() {return newLineSep;} public DsvDataRdrOpts NewLineSep_(String val) {newLineSep = val; return this;} private String newLineSep = String_.CrLf; + public String FldSep() {return fldSep;} public DsvDataRdrOpts FldSep_(String val) {fldSep = val; return this;} private String fldSep = ","; + public GfoFldList Flds() {return flds;} public DsvDataRdrOpts Flds_(GfoFldList val) {flds = val; return this;} GfoFldList flds = GfoFldList_.Null; + public static DsvDataRdrOpts new_() {return new DsvDataRdrOpts();} DsvDataRdrOpts() {} +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataRdr_.java b/100_core/src/gplx/langs/dsvs/DsvDataRdr_.java new file mode 100644 index 000000000..080de4b6e --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataRdr_.java @@ -0,0 +1,248 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import gplx.core.strings.*; import gplx.core.gfo_ndes.*; import gplx.core.stores.*; import gplx.core.type_xtns.*; +import gplx.core.texts.*; /*CharStream*/ +public class DsvDataRdr_ { + public static DataRdr dsv_(String text) {return DsvParser.dsv_().ParseAsRdr(text);} + public static DataRdr csv_hdr_(String text) {return csv_opts_(text, DsvDataRdrOpts.new_().HasHeader_(true));} + public static DataRdr csv_dat_(String text) {return csv_opts_(text, DsvDataRdrOpts.new_());} + public static DataRdr csv_dat_with_flds_(String text, String... flds) {return csv_opts_(text, DsvDataRdrOpts.new_().Flds_(GfoFldList_.str_(flds)));} + public static DataRdr csv_opts_(String text, DsvDataRdrOpts opts) { + DsvParser parser = DsvParser.csv_(opts.HasHeader(), opts.Flds()); // NOTE: this sets up the bldr; do not call .Init after this + parser.Symbols().RowSep_(opts.NewLineSep()).FldSep_(opts.FldSep()); + + DataRdr root = parser.ParseAsRdr(text); // don't return root; callers expect csv to return rdr for rows + DataRdr csvTable = root.Subs(); + return csvTable.Subs(); + } +} +class DsvParser { + @gplx.Internal protected DsvSymbols Symbols() {return sym;} DsvSymbols sym = DsvSymbols.default_(); + @gplx.Internal protected void Init() {sb.Clear(); bldr.Init(); qteOn = false;} + @gplx.Internal protected DataRdr ParseAsRdr(String raw) {return GfoNdeRdr_.root_(ParseAsNde(raw), csvOn);} // NOTE: csvOn means parse; assume manual typed flds not passed in (ex: id,Int) + @gplx.Internal protected GfoNde ParseAsNde(String raw) { + if (String_.Eq(raw, "")) return bldr.BldRoot(); + CharStream strm = CharStream.pos0_(raw); + while (true) { + if (strm.AtEnd()) { + ProcessLine(strm, true); + break; + } + if (qteOn) + ReadStreamInQte(strm); + else if (strm.MatchAndMove(sym.QteDlm())) + qteOn = true; + else if (strm.MatchAndMove(sym.FldSep())) + ProcessFld(strm); + else if (strm.MatchAndMove(sym.RowSep())) + ProcessLine(strm, false); + else { + sb.Add(strm.Cur()); + strm.MoveNext(); + } + } + return bldr.BldRoot(); + } + void ReadStreamInQte(CharStream strm) { + if (strm.MatchAndMove(sym.QteDlm())) { // is quote + if (strm.MatchAndMove(sym.QteDlm())) // double quote -> quote; "a"" + sb.Add(sym.QteDlm()); + else if (strm.MatchAndMove(sym.FldSep())) { // closing quote before field; "a", + ProcessFld(strm); + qteOn = false; + } + else if (strm.MatchAndMove(sym.RowSep()) || strm.AtEnd()) { // closing quote before record; "a"\r\n + ProcessLine(strm, false); + qteOn = false; + } + else + throw Err_.new_wo_type("invalid quote in quoted field; quote must be followed by quote, fieldSpr, or recordSpr", "sym", strm.Cur(), "text", strm.To_str_by_pos(strm.Pos() - 10, strm.Pos() + 10)); + } + else { // regular char; append and continue + sb.Add(strm.Cur()); + strm.MoveNext(); + } + } + void ProcessFld(CharStream strm) { + String val = sb.To_str_and_clear(); + if (cmdSeqOn) { + cmdSeqOn = false; + if (String_.Eq(val, sym.CmdDlm()) && qteOn) { // 2 cmdDlms in a row; cmdSeq encountered; next fld must be cmdName + nextValType = ValType_CmdName; + return; + } + tkns.Add(sym.CmdDlm()); // curTkn is not cmdDlm; prevTkn happened to be cmdDlm; add prev to tkns and continue; ex: a, ,b + } + if (String_.Eq(val, sym.CmdDlm())) // val is cmdDlm; do not add now; wait til next fld to decide + cmdSeqOn = true; + else if (nextValType == ValType_Data) { + if (String_.Len(val) == 0) val = qteOn ? "" : null; // differentiate between null and emptyString; ,, vs ,"", + tkns.Add(val); + } + else if (nextValType == ValType_CmdName) { + if (String_.Eq(val, sym.TblNameSym())) lineMode = LineType_TblBgn; // # + else if (String_.Eq(val, sym.FldNamesSym())) lineMode = LineType_FldNames; // @ + else if (String_.Eq(val, sym.FldTypesSym())) lineMode = LineType_FldTypes; // $ + else if (String_.Eq(val, sym.CommentSym())) lineMode = LineType_Comment; // ' + else throw Err_.new_wo_type("unknown dsv cmd", "cmd", val); + } + else + throw Err_.new_wo_type("unable to process field value", "value", val); + } + void ProcessLine(CharStream strm, boolean cleanup) { + if (sb.Count() == 0 && tkns.Count() == 0) + if (csvOn) { // csvOn b/c csvMode allows blank lines as empty data + if (cleanup) // cleanup b/c blankLine should not be added when called by cleanup, else will always add extra row at end + return; // cleanup, so no further action needed; return; + else + ProcessFld(strm); + } + else + lineMode = LineType_BlankLine; + else + ProcessFld(strm); // always process fld; either (1) chars waiting in sb "a,b"; or (2) last char was fldSep "a," + if (cmdSeqOn) { // only happens if last fld is comma space (, ); do not let cmds span lines + cmdSeqOn = false; + tkns.Add(sym.CmdDlm()); + } + if (lineMode == LineType_TblBgn) bldr.MakeTblBgn(tkns); + else if (lineMode == LineType_FldNames) bldr.MakeFldNames(tkns); + else if (lineMode == LineType_FldTypes) bldr.MakeFldTypes(tkns); + else if (lineMode == LineType_Comment) bldr.MakeComment(tkns); + else if (lineMode == LineType_BlankLine) bldr.MakeBlankLine(); + else bldr.MakeVals(tkns); + nextValType = ValType_Data; + lineMode = LineType_Data; + } + String_bldr sb = String_bldr_.new_(); List_adp tkns = List_adp_.New(); DsvTblBldr bldr = DsvTblBldr.new_(); + boolean cmdSeqOn = false, qteOn = false, csvOn = false; + int nextValType = ValType_Data, lineMode = LineType_Data; + @gplx.Internal protected static DsvParser dsv_() {return new DsvParser();} + @gplx.Internal protected static DsvParser csv_(boolean hasHdr, GfoFldList flds) { + DsvParser rv = new DsvParser(); + rv.csvOn = true; + rv.lineMode = hasHdr ? LineType_FldNames : LineType_Data; + List_adp names = List_adp_.New(), types = List_adp_.New(); + for (int i = 0; i < flds.Count(); i++) { + GfoFld fld = flds.Get_at(i); + names.Add(fld.Key()); types.Add(fld.Type().Key()); + } + rv.bldr.MakeFldNames(names); rv.bldr.MakeFldTypes(types); + return rv; + } + static final int ValType_Data = 0, ValType_CmdName = 1; + static final int LineType_Data = 0, LineType_Comment = 1, LineType_TblBgn = 2, LineType_FldNames = 3, LineType_FldTypes = 4, LineType_BlankLine = 5; +} +class DsvTblBldr { + public void Init() { + root = GfoNde_.root_(); tbl = GfoNde_.tbl_(NullTblName, GfoFldList_.new_()); + fldNames.Clear(); fldTypes.Clear(); + stage = Stage_Init; + } + public GfoNde BldRoot() { + if (stage != Stage_Init) CreateTbl(); // CreateTbl if HDR or ROW is in progress + return root; + } + public void MakeTblBgn(List_adp tkns) { + if (stage != Stage_Init) CreateTbl(); // CreateTbl if HDR or ROW is in progress + tbl.Name_((String)tkns.Get_at(0)); + layout.HeaderList().Add_TableName(); + stage = Stage_Hdr; tkns.Clear(); + } + public void MakeFldNames(List_adp tkns) { + if (stage == Stage_Row) CreateTbl(); // CreateTbl if ROW is in progress; NOTE: exclude HDR, as first HDR would have called CreateTbl + fldNames.Clear(); + for (Object fldNameObj : tkns) + fldNames.Add(fldNameObj); + layout.HeaderList().Add_LeafNames(); + stage = Stage_Hdr; tkns.Clear(); + } + public void MakeFldTypes(List_adp tkns) { + if (stage == Stage_Row) CreateTbl(); // CreateTbl if ROW is in progress; NOTE: exclude HDR, as first HDR would have called CreateTbl + fldTypes.Clear(); + for (Object fldTypeObj : tkns) { + ClassXtn type = ClassXtnPool.Instance.Get_by_or_fail((String)fldTypeObj); + fldTypes.Add(type); + } + layout.HeaderList().Add_LeafTypes(); + stage = Stage_Hdr; tkns.Clear(); + } + public void MakeComment(List_adp tkns) { + if (stage == Stage_Row) // comments in ROW; ignore; NOTE: tkns.Clear() could be merged, but this seems clearer + tkns.Clear(); + else { // comments in HDR + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < tkns.Count(); i++) + sb.Add((String)tkns.Get_at(i)); + layout.HeaderList().Add_Comment(sb.To_str()); + tkns.Clear(); + } + } + public void MakeBlankLine() { + if (stage != Stage_Init) CreateTbl(); // CreateTbl if HDR or ROW is in progress; + layout.HeaderList().Add_BlankLine(); + stage = Stage_Init; // NOTE: mark stage as INIT; + } + public void MakeVals(List_adp tkns) { + if (stage != Stage_Row) CreateFlds(tkns.Count()); // stage != Stage_Row means if (noRowsCreated) + GfoNde row = GfoNde_.vals_(tbl.SubFlds(), MakeValsAry(tkns)); + tbl.Subs().Add(row); + stage = Stage_Row; tkns.Clear(); + } + Object[] MakeValsAry(List_adp tkns) { + GfoFldList subFlds = tbl.SubFlds(); int subFldsCount = subFlds.Count(); + if (tkns.Count() > subFldsCount) throw Err_.new_wo_type("values.Count cannot be greater than fields.Count", "values.Count", tkns.Count(), "fields.Count", subFldsCount); + Object[] rv = new Object[subFldsCount]; + for (int i = 0; i < subFldsCount; i++) { + ClassXtn typx = subFlds.Get_at(i).Type(); + String val = i < tkns.Count() ? (String)tkns.Get_at(i) : null; + rv[i] = typx.ParseOrNull(val); + } + return rv; + } + void CreateTbl() { + if (tbl.SubFlds().Count() == 0) CreateFlds(0); // this check occurs when tbl has no ROW; (TOMB: tdb test fails) + tbl.EnvVars().Add(DsvStoreLayout.Key_const, layout); + root.Subs().Add(tbl); // add pending table + layout = DsvStoreLayout.dsv_brief_(); + tbl = GfoNde_.tbl_(NullTblName, GfoFldList_.new_()); + stage = Stage_Hdr; + } + void CreateFlds(int valCount) { + int fldNamesCount = fldNames.Count(), fldTypesCount = fldTypes.Count(); + if (fldNamesCount == 0 && fldTypesCount == 0) { // csv tbls where no names or types, just values + for (int i = 0; i < valCount; i++) + tbl.SubFlds().Add("fld" + i, StringClassXtn.Instance); + } + else { // all else, where either names or types is defined + int maxCount = fldNamesCount > fldTypesCount ? fldNamesCount : fldTypesCount; + for (int i = 0; i < maxCount; i++) { + String name = i < fldNamesCount ? (String)fldNames.Get_at(i) : "fld" + i; + ClassXtn typx = i < fldTypesCount ? (ClassXtn)fldTypes.Get_at(i) : StringClassXtn.Instance; + tbl.SubFlds().Add(name, typx); + } + } + } + GfoNde root; GfoNde tbl; DsvStoreLayout layout = DsvStoreLayout.dsv_brief_(); + List_adp fldNames = List_adp_.New(); List_adp fldTypes = List_adp_.New(); + int stage = Stage_Init; + public static DsvTblBldr new_() {return new DsvTblBldr();} DsvTblBldr() {this.Init();} + @gplx.Internal protected static final String NullTblName = ""; + static final int Stage_Init = 0, Stage_Hdr = 1, Stage_Row = 2; +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataRdr_csv_dat_tst.java b/100_core/src/gplx/langs/dsvs/DsvDataRdr_csv_dat_tst.java new file mode 100644 index 000000000..51b2bfc8b --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataRdr_csv_dat_tst.java @@ -0,0 +1,216 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import org.junit.*; import gplx.core.strings.*; import gplx.core.gfo_ndes.*; +public class DsvDataRdr_csv_dat_tst { + @Before public void setup() { + fx.Parser_(DsvParser.csv_(false, GfoFldList_.Null)); + fx.Clear(); + } DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void Empty() { + fx.run_parse_(""); + fx.tst_DatNull(); + } + @Test public void Fld_0() { + fx.run_parse_("a"); + fx.tst_DatCsv(fx.ary_("a")); + } + @Test public void Fld_N() { + fx.run_parse_("a,b,c"); + fx.tst_FldListCsv("fld0", "fld1", "fld2"); + fx.tst_DatCsv(fx.ary_("a", "b", "c")); + } + @Test public void Row_N() { + fx.run_parse_ + ( "a,b,c", String_.CrLf + , "1,2,3" + ); + fx.tst_DatCsv + ( fx.ary_("a", "b", "c") + , fx.ary_("1", "2", "3") + ); + } + @Test public void Escape_WhiteSpace() { + fx.run_parse_("a,\" \t\",c"); + fx.tst_DatCsv(fx.ary_("a", " \t", "c")); + } + @Test public void Escape_FldSep() { + fx.run_parse_("a,\",\",c"); + fx.tst_DatCsv(fx.ary_("a", ",", "c")); + } + @Test public void Escape_RowSep() { + fx.run_parse_("a,\"" + String_.CrLf + "\",c"); + fx.tst_DatCsv(fx.ary_("a", String_.CrLf, "c")); + } + @Test public void Escape_Quote() { + fx.run_parse_("a,\"\"\"\",c"); + fx.tst_DatCsv(fx.ary_("a", "\"", "c")); + } + @Test public void Blank_Null() { + fx.run_parse_("a,,c"); + fx.tst_DatCsv(fx.ary_("a", null, "c")); + } + @Test public void Blank_EmptyString() { + fx.run_parse_("a,\"\",c"); + fx.tst_DatCsv(fx.ary_("a", "", "c")); + } + @Test public void Blank_Null_Multiple() { + fx.run_parse_(",,"); + fx.tst_DatCsv(fx.ary_(null, null, null)); + } + @Test public void TrailingNull() { + fx.run_parse_("a,"); + fx.tst_DatCsv(fx.ary_("a", null)); + } + @Test public void TrailingEmpty() { + fx.run_parse_("a,\"\""); + fx.tst_DatCsv(fx.ary_("a", "")); + } + @Test public void Quote_Error() { + try { + fx.run_parse_("a,\"\" ,c"); + Tfds.Fail_expdError(); + } + catch (Err e) { + Tfds.Eq_true(String_.Has(Err_.Message_lang(e), "invalid quote in quoted field")); + } + } + @Test public void Misc_AllowValsLessThanFields() { + // assume null when vals.Count < fields.Count; PURPOSE: MsExcel will not save trailing commas for csvExport; ex: a, -> a + fx.run_parse_ + ( "a0,a1", String_.CrLf + , "b0" + ); + fx.tst_DatCsv + ( fx.ary_("a0", "a1") + , fx.ary_("b0", null) + ); + } + @Test public void Misc_NewLineValidForSingleColumnTables() { + fx.run_parse_ + ( "a", String_.CrLf + , String_.CrLf + , "c" , String_.CrLf + , String_.CrLf + ); + fx.tst_DatCsv + ( fx.ary_("a") + , fx.ary_null_() + , fx.ary_("c") + , fx.ary_null_() + ); + } + @Test public void Misc_NewLineValidForSingleColumnTables_FirstLine() { + fx.run_parse_ + ( String_.CrLf + , "b", String_.CrLf + , "c" + ); + fx.tst_DatCsv + ( fx.ary_null_() + , fx.ary_("b") + , fx.ary_("c") + ); + } + @Test public void Hdr_Basic() { + fx.Parser_(DsvParser.csv_(true, GfoFldList_.Null)); + fx.run_parse_ + ( "id,name", String_.CrLf + , "0,me" + ); + fx.tst_FldListCsv("id", "name"); + fx.tst_DatCsv(fx.ary_("0", "me")); + } +// @Test public void Hdr_Manual() { +// fx.Parser_(DsvParser.csv_(false, GfoFldList_.new_().Add("id", IntClassXtn.Instance).Add("name", StringClassXtn.Instance), true)); +// fx.run_parse_("0,me"); +// fx.tst_DatCsv(fx.ary_(0, "me")); // NOTE: testing auto-parsing of id to int b/c id fld is IntClassXtn.Instance; +// } +} +class DsvDataRdr_fxt { + public Object[] ary_(Object... ary) {return ary;} + public Object[] ary_null_() {return new Object[] {null};} + public void Clear() {parser.Init(); root = null;} + public DsvParser Parser() {return parser;} public DsvDataRdr_fxt Parser_(DsvParser val) {parser = val; return this;} DsvParser parser = DsvParser.dsv_(); + public GfoNde Root() {return root;} GfoNde root; + public void run_parse_(String... ary) {root = parser.ParseAsNde(String_.Concat(ary));} + public void run_parse_lines_(String... ary) {root = parser.ParseAsNde(String_.Concat_lines_crlf(ary));} + public DsvDataRdr_fxt tst_FldListCsv(String... names) {return tst_Flds(TblIdx0, GfoFldList_.str_(names));} + public DsvDataRdr_fxt tst_Flds(int tblIdx, GfoFldList expdFlds) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(tblIdx); + List_adp expdList = List_adp_.New(), actlList = List_adp_.New(); + String_bldr sb = String_bldr_.new_(); + GfoFldList_BldDbgList(expdFlds, expdList, sb); + GfoFldList_BldDbgList(tbl.SubFlds(), actlList, sb); + Tfds.Eq_list(expdList, actlList); + return this; + } + void GfoFldList_BldDbgList(GfoFldList flds, List_adp list, String_bldr sb) { + for (int i = 0; i < flds.Count(); i++) { + GfoFld fld = flds.Get_at(i); + sb.Add(fld.Key()).Add(",").Add(fld.Type().Key()); + list.Add(sb.To_str_and_clear()); + } + } + public DsvDataRdr_fxt tst_Tbls(String... expdNames) { + List_adp actlList = List_adp_.New(); + for (int i = 0; i < root.Subs().Count(); i++) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(i); + actlList.Add(tbl.Name()); + } + Tfds.Eq_ary(expdNames, actlList.To_str_ary()); + return this; + } + public DsvDataRdr_fxt tst_DatNull() { + Tfds.Eq(0, root.Subs().Count()); + return this; + } + public DsvDataRdr_fxt tst_DatCsv(Object[]... expdRows) {return tst_Dat(0, expdRows);} + public DsvDataRdr_fxt tst_Dat(int tblIdx, Object[]... expdRows) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(tblIdx); + if (expdRows.length == 0) { + Tfds.Eq(0, tbl.Subs().Count()); + return this; + } + List_adp expdList = List_adp_.New(), actlList = List_adp_.New(); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < tbl.Subs().Count(); i++) { + GfoNde row = tbl.Subs().FetchAt_asGfoNde(i); + for (int j = 0; j < row.Flds().Count(); j++) { + if (j != 0) sb.Add("~"); + sb.Add_obj(Object_.Xto_str_strict_or_null_mark(row.ReadAt(j))); + } + expdList.Add(sb.To_str_and_clear()); + } + for (Object[] expdRow : expdRows) { + if (expdRow == null) { + actlList.Add(""); + continue; + } + for (int j = 0; j < expdRow.length; j++) { + if (j != 0) sb.Add("~"); + sb.Add_obj(Object_.Xto_str_strict_or_null_mark(expdRow[j])); + } + actlList.Add(sb.To_str_and_clear()); + } + Tfds.Eq_list(expdList, actlList); + return this; + } + public static DsvDataRdr_fxt new_() {return new DsvDataRdr_fxt();} + static final int TblIdx0 = 0; +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_dat_tst.java b/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_dat_tst.java new file mode 100644 index 000000000..d3b6fb0c0 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_dat_tst.java @@ -0,0 +1,70 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import org.junit.*; +public class DsvDataRdr_dsv_dat_tst { + @Before public void setup() {fx.Clear();} DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void NameOnly() { + fx.run_parse_("tableName, ,\" \",#"); + fx.tst_Tbls("tableName"); + fx.tst_Dat(0); + } + @Test public void Rows_N() { + fx.run_parse_lines_ + ( "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_Tbls("numbers"); + fx.tst_Dat(0 + , fx.ary_("1", "2", "3") + , fx.ary_("4", "5", "6") + ); + } + @Test public void Tbls_N() { + fx.run_parse_lines_ + ( "letters, ,\" \",#" + , "a,b,c" + , "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_Tbls("letters", "numbers"); + fx.tst_Dat(0, fx.ary_("a", "b", "c")); + fx.tst_Dat(1, fx.ary_("1", "2", "3"), fx.ary_("4", "5", "6")); + } + @Test public void IgnoreTrailingBlankRow() { + fx.run_parse_lines_ + ( "letters, ,\" \",#" + , "a,b,c" + , "" // ignored + ); + fx.tst_Tbls("letters"); + fx.tst_Dat(0, fx.ary_("a", "b", "c")); + } + @Test public void AllowCommentsDuringData() { + fx.run_parse_lines_ + ( "letters, ,\" \",#" + , "a,b,c" + , "// letters omitted, ,\" \",//" // these comments are not preserved + , "x,y,z" + ); + fx.tst_Tbls("letters"); + fx.tst_Dat(0, fx.ary_("a", "b", "c"), fx.ary_("x", "y", "z")); + } +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_hdr_tst.java b/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_hdr_tst.java new file mode 100644 index 000000000..1cf43d07a --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_hdr_tst.java @@ -0,0 +1,82 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import org.junit.*; import gplx.core.gfo_ndes.*; import gplx.core.type_xtns.*; +public class DsvDataRdr_dsv_hdr_tst { + @Before public void setup() {fx.Clear();} DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void Names() { + fx.run_parse_lines_ + ( "id,name, ,\" \",@" + , "0,me" + , "1,you" + ); + fx.tst_Flds(0, GfoFldList_.str_("id", "name")); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Dat(0 + , fx.ary_("0", "me") + , fx.ary_("1", "you") + ); + } + @Test public void Types() { + fx.run_parse_lines_ + ( "int," + StringClassXtn.Key_const + ", ,\" \",$" + , "0,me" + , "1,you" + ); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", IntClassXtn.Instance).Add("fld1", StringClassXtn.Instance)); + fx.tst_Dat(0 + , fx.ary_(0, "me") + , fx.ary_(1, "you") + ); + } + @Test public void NamesAndTypes() { + fx.run_parse_lines_ + ( "id,name, ,\" \",@" + , "int," + StringClassXtn.Key_const + ", ,\" \",$" + , "0,me" + , "1,you" + ); + fx.tst_Flds(0, GfoFldList_.new_().Add("id", IntClassXtn.Instance).Add("name", StringClassXtn.Instance)); + fx.tst_Dat(0 + , fx.ary_(0, "me") + , fx.ary_(1, "you") + ); + } + @Test public void MultipleTables_NoData() { + fx.run_parse_lines_ + ( "persons, ,\" \",#" + , "id,name, ,\" \",@" + , "things, ,\" \",#" + , "id,data, ,\" \",@" + ); + fx.tst_Tbls("persons", "things"); + fx.tst_Flds(0, GfoFldList_.str_("id", "name")); + fx.tst_Flds(1, GfoFldList_.str_("id", "data")); + fx.tst_Dat(0); + fx.tst_Dat(1); + } + @Test public void Comment() { + fx.run_parse_lines_ + ( "--------------------, ,\" \",//" + , "tbl0, ,\" \",#" + , "a0,a1" + ); + fx.tst_Tbls("tbl0"); + fx.tst_Dat(0, fx.ary_("a0", "a1")); + } +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_misc_tst.java b/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_misc_tst.java new file mode 100644 index 000000000..477319e39 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_misc_tst.java @@ -0,0 +1,76 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import org.junit.*; import gplx.core.gfo_ndes.*; import gplx.core.type_xtns.*; +public class DsvDataRdr_dsv_misc_tst { + @Before public void setup() {fx.Clear();} DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void CmdDlm_NearMatches() { + fx.run_parse_("a, ,b"); + fx.tst_DatCsv(fx.ary_("a", " ", "b")); + fx.Clear(); + + fx.run_parse_("a,\" \",b"); + fx.tst_DatCsv(fx.ary_("a", " ", "b")); + fx.Clear(); + + fx.run_parse_("a, ,b,\" \",c"); + fx.tst_DatCsv(fx.ary_("a", " ", "b", " ", "c")); + fx.Clear(); + } + @Test public void CmdDlm_DoNotSpanLines() { + fx.run_parse_lines_ + ( "a, " + , "\" \",b" + ); + fx.tst_DatCsv + ( fx.ary_("a", " ") + , fx.ary_(" ", "b") + ); + } + @Test public void CmdDlm_SecondFldMustBeQuoted() { + fx.run_parse_lines_("a, , ,b"); // will fail with "invalid command: b", if second , , is interpreted as command delimiter + fx.tst_DatCsv(fx.ary_("a", " ", " ", "b")); + } + @Test public void Null_Int() { + fx.run_parse_ // not using run_parse_lines_ b/c (a) will have extra lineBreak; (b) test will look funny; + ( "int," + StringClassXtn.Key_const + ", ,\" \",$", String_.CrLf + , ",val1" + ); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", IntClassXtn.Instance).Add("fld1", StringClassXtn.Instance)); + fx.tst_Dat(0, fx.ary_(null, "val1")); + } + @Test public void Null_String() { + fx.run_parse_ // not using run_parse_lines_ b/c (a) will have extra lineBreak; (b) test will look funny; + ( StringClassXtn.Key_const + "," + StringClassXtn.Key_const + ", ,\" \",$", String_.CrLf + , ",val1" + ); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", StringClassXtn.Instance).Add("fld1", StringClassXtn.Instance)); + fx.tst_Dat(0, fx.ary_(null, "val1")); + } + @Test public void EmptyString() { + fx.run_parse_ // not using run_parse_lines_ b/c (a) will have extra lineBreak; (b) test will look funny; + ( StringClassXtn.Key_const + "," + StringClassXtn.Key_const + ", ,\" \",$", String_.CrLf + , "\"\",val1" + ); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", StringClassXtn.Instance).Add("fld1", StringClassXtn.Instance)); + fx.tst_Dat(0, fx.ary_("", "val1")); + } +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataRdr_layout_tst.java b/100_core/src/gplx/langs/dsvs/DsvDataRdr_layout_tst.java new file mode 100644 index 000000000..dfb52c4c6 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataRdr_layout_tst.java @@ -0,0 +1,131 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import org.junit.*; import gplx.core.gfo_ndes.*; import gplx.core.type_xtns.*; +public class DsvDataRdr_layout_tst { + @Test public void TableName() { + run_parse_lines("table0, ,\" \",#"); + tst_Layout(0, DsvHeaderItm.Id_TableName); + } + @Test public void Comment() { + run_parse_lines("-------------, ,\" \",//", "data"); // need dataLine or parser will throw away standalone header + tst_Layout(0, DsvHeaderItm.Id_Comment); + } + @Test public void BlankLine() { + run_parse_lines("", "data"); // need dataLine or parser will throw away standalone header + tst_Layout(0, DsvHeaderItm.Id_BlankLine); + } + @Test public void LeafNames() { + run_parse_lines("id,name, ,\" \",@"); + tst_Layout(0, DsvHeaderItm.Id_LeafNames); + } + @Test public void LeafTypes() { + run_parse_lines("int," + StringClassXtn.Key_const + ", ,\" \",$"); + tst_Layout(0, DsvHeaderItm.Id_LeafTypes); + } + @Test public void Combined() { + run_parse_lines + ( "" + , "-------------, ,\" \",//" + , "table0, ,\" \",#" + , "int," + StringClassXtn.Key_const + ", ,\" \",$" + , "id,name, ,\" \",@" + , "-------------, ,\" \",//" + , "0,me" + ); + tst_Layout(0 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_LeafTypes + , DsvHeaderItm.Id_LeafNames + , DsvHeaderItm.Id_Comment + ); + } + @Test public void Tbl_N() { + run_parse_lines + ( "" + , "*************, ,\" \",//" + , "table0, ,\" \",#" + , "-------------, ,\" \",//" + , "0,me" + , "" + , "*************, ,\" \",//" + , "table1, ,\" \",#" + , " extended data, ,\" \",//" + , "-------------, ,\" \",//" + , "1,you,more" + ); + tst_Layout(0 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + ); + tst_Layout(1 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_Comment + ); + } + @Test public void Tbl_N_FirstIsEmpty() { + run_parse_lines + ( "" + , "*************, ,\" \",//" + , "table0, ,\" \",#" + , "-------------, ,\" \",//" + , "" + , "" + , "*************, ,\" \",//" + , "table1, ,\" \",#" + , " extended data, ,\" \",//" + , "-------------, ,\" \",//" + , "1,you,more" + ); + tst_Layout(0 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + ); + tst_Layout(1 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_Comment + ); + } + void run_parse_lines(String... ary) { + String raw = String_.Concat_lines_crlf(ary); + DsvParser parser = DsvParser.dsv_(); + root = parser.ParseAsNde(raw); + } + void tst_Layout(int subIdx, int... expd) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(subIdx); + DsvStoreLayout layout = (DsvStoreLayout)tbl.EnvVars().Get_by(DsvStoreLayout.Key_const); + int[] actl = new int[layout.HeaderList().Count()]; + for (int i = 0; i < actl.length; i++) + actl[i] = layout.HeaderList().Get_at(i).Id(); + Tfds.Eq_ary(expd, actl); + } + GfoNde root; +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataWtr.java b/100_core/src/gplx/langs/dsvs/DsvDataWtr.java new file mode 100644 index 000000000..e8e01f0e2 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataWtr.java @@ -0,0 +1,115 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import gplx.core.strings.*; import gplx.core.gfo_ndes.*; import gplx.core.stores.*; +public class DsvDataWtr extends DataWtr_base implements DataWtr { + public void InitWtr(String key, Object val) { + if (key == DsvStoreLayout.Key_const) layout = (DsvStoreLayout)val; + } + @Override public void WriteData(String name, Object val) {sb.WriteFld(val == null ? null : val.toString());} + public void WriteLeafBgn(String leafName) {} + public void WriteLeafEnd() {sb.WriteRowSep();} + @Override public void WriteNodeBgn(String name) {WriteTableBgn(name, GfoFldList_.Null);} + public void WriteTableBgn(String name, GfoFldList flds) { + for (int i = 0; i < layout.HeaderList().Count(); i++) { + DsvHeaderItm data = layout.HeaderList().Get_at(i); + int id = data.Id(); + if (id == DsvHeaderItm.Id_TableName) WriteTableName(name); + else if (id == DsvHeaderItm.Id_LeafNames) WriteMeta(flds, true, sym.FldNamesSym()); + else if (id == DsvHeaderItm.Id_LeafTypes) WriteMeta(flds, false, sym.FldTypesSym()); + else if (id == DsvHeaderItm.Id_BlankLine) sb.WriteRowSep(); + else if (id == DsvHeaderItm.Id_Comment) WriteComment(data.Val().toString()); + } + } + @Override public void WriteNodeEnd() {} + public void Clear() {sb.Clear();} + public String To_str() {return sb.To_str();} + void WriteTableName(String tableName) { + sb.WriteFld(tableName); + sb.WriteCmd(sym.TblNameSym()); + sb.WriteRowSep(); + } + void WriteMeta(GfoFldList flds, boolean isName, String cmd) { + for (int i = 0; i < flds.Count(); i++) { + GfoFld fld = flds.Get_at(i); + String val = isName ? fld.Key(): fld.Type().Key(); + sb.WriteFld(val); + } + if (layout.WriteCmdSequence()) sb.WriteCmd(cmd); + sb.WriteRowSep(); + } + void WriteComment(String comment) { + sb.WriteFld(comment); + sb.WriteCmd(sym.CommentSym()); + sb.WriteRowSep(); + } + @Override public SrlMgr SrlMgr_new(Object o) {return new DsvDataWtr();} + DsvStringBldr sb; DsvSymbols sym = DsvSymbols.default_(); DsvStoreLayout layout = DsvStoreLayout.csv_dat_(); + @gplx.Internal protected DsvDataWtr() {sb = DsvStringBldr.new_(sym);} +} +class DsvStringBldr { + public void Clear() {sb.Clear();} + public String To_str() {return sb.To_str();} + public void WriteCmd(String cmd) { + WriteFld(sym.CmdSequence(), true); + WriteFld(cmd); + } + public void WriteFldSep() {sb.Add(sym.FldSep());} + public void WriteRowSep() { + sb.Add(sym.RowSep()); + isNewRow = true; + } + public void WriteFld(String val) {WriteFld(val, false);} + void WriteFld(String val, boolean writeRaw) { + if (isNewRow) // if isNewRow, then fld is first, and no fldSpr needed (RowSep serves as fldSpr) + isNewRow = false; + else + sb.Add(sym.FldSep()); + + if (val == null) {} // null -> append nothing + else if (String_.Eq(val, String_.Empty))// "" -> append "" + sb.Add("\"\""); + else if (writeRaw) // only cmds should be writeRaw (will append ," ") + sb.Add(val); + else { // escape as necessary; ex: "the quote "" char"; "the comma , char" + boolean quoteField = false; + if (String_.Has(val, sym.QteDlm())) { + val = String_.Replace(val, "\"", "\"\""); + quoteField = true; + } + else if (String_.Has(val, sym.FldSep())) + quoteField = true; + else if (sym.RowSepIsNewLine() + && (String_.Has(val, "\n") || String_.Has(val, "\r"))) + quoteField = true; + else if (String_.Has(val, sym.RowSep())) + quoteField = true; + + if (quoteField) sb.Add(sym.QteDlm()); + sb.Add(val); + if (quoteField) sb.Add(sym.QteDlm()); + } + } + + String_bldr sb = String_bldr_.new_(); DsvSymbols sym; boolean isNewRow = true; + public static DsvStringBldr new_(DsvSymbols sym) { + DsvStringBldr rv = new DsvStringBldr(); + rv.sym = sym; + return rv; + } DsvStringBldr() {} +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataWtr_.java b/100_core/src/gplx/langs/dsvs/DsvDataWtr_.java new file mode 100644 index 000000000..19707681e --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataWtr_.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +public class DsvDataWtr_ { + public static DsvDataWtr csv_hdr_() { + DsvDataWtr rv = new DsvDataWtr(); + rv.InitWtr(DsvStoreLayout.Key_const, DsvStoreLayout.csv_hdr_()); + return rv; + } + public static DsvDataWtr csv_dat_() { + DsvDataWtr rv = new DsvDataWtr(); + rv.InitWtr(DsvStoreLayout.Key_const, DsvStoreLayout.csv_dat_()); + return rv; + } + public static DsvDataWtr new_() {return new DsvDataWtr();} +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataWtr_csv_tst.java b/100_core/src/gplx/langs/dsvs/DsvDataWtr_csv_tst.java new file mode 100644 index 000000000..dd18b9cb3 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataWtr_csv_tst.java @@ -0,0 +1,100 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import org.junit.*; import gplx.core.gfo_ndes.*; import gplx.core.type_xtns.*; +public class DsvDataWtr_csv_tst { + @Test public void Dat_Val_0() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root); + expd = String_.Concat_lines_crlf(""); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Val_1() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a"); + expd = String_.Concat_lines_crlf("a"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Val_N() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", "b", "c"); + expd = String_.Concat_lines_crlf("a,b,c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Row_N() { + root = fx_nde.csv_dat_(); + this.AddCsvRow(root, "a", "b", "c"); + this.AddCsvRow(root, "d", "e", "f"); + expd = String_.Concat_lines_crlf + ( "a,b,c" + , "d,e,f" + ); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Escape_FldSpr() { // , + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", ",", "c"); + expd = String_.Concat_lines_crlf("a,\",\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Escape_RcdSpr() { // NewLine + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", String_.CrLf, "c"); + expd = String_.Concat_lines_crlf("a,\"" + String_.CrLf + "\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Escape_Quote() { // " -> "" + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", "\"", "c"); + expd = String_.Concat_lines_crlf("a,\"\"\"\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Whitespace() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", " b\t", "c"); + expd = String_.Concat_lines_crlf("a, b\t,c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Null() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", null, "c"); + expd = String_.Concat_lines_crlf("a,,c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_EmptyString() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", "", "c"); + expd = String_.Concat_lines_crlf("a,\"\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Hdr_Flds() { + wtr = DsvDataWtr_.csv_hdr_(); + GfoFldList flds = GfoFldList_.new_().Add("id", StringClassXtn.Instance).Add("name", StringClassXtn.Instance); + root = fx_nde.csv_hdr_(flds); this.AddCsvRow(root, "0", "me"); + expd = String_.Concat_lines_crlf + ( "id,name" + , "0,me" + ); + fx.tst_XtoStr(wtr, root, expd); + } + void AddCsvRow(GfoNde root, String... ary) { + GfoNde sub = GfoNde_.vals_(root.SubFlds(), ary); + root.Subs().Add(sub); + } + GfoNde root; String expd; DsvDataWtr wtr = DsvDataWtr_.csv_dat_(); DsvDataWtr_fxt fx = DsvDataWtr_fxt.new_(); GfoNdeFxt fx_nde = GfoNdeFxt.new_(); +} +class DsvDataWtr_fxt { + public void tst_XtoStr(DsvDataWtr wtr, GfoNde root, String expd) { + wtr.Clear(); + root.XtoStr_wtr(wtr); + String actl = wtr.To_str(); + Tfds.Eq(expd, actl); + } + public static DsvDataWtr_fxt new_() {return new DsvDataWtr_fxt();} DsvDataWtr_fxt() {} +} diff --git a/100_core/src/gplx/langs/dsvs/DsvDataWtr_tbls_tst.java b/100_core/src/gplx/langs/dsvs/DsvDataWtr_tbls_tst.java new file mode 100644 index 000000000..302028273 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvDataWtr_tbls_tst.java @@ -0,0 +1,73 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +import org.junit.*; import gplx.core.gfo_ndes.*; +public class DsvDataWtr_tbls_tst { + @Before public void setup() { + DsvStoreLayout layout = DsvStoreLayout.dsv_brief_(); + layout.HeaderList().Add_TableName(); + wtr.InitWtr(DsvStoreLayout.Key_const, layout); + } + @Test public void Rows_0() { + root = fx_nde.tbl_("tbl0"); + expd = String_.Concat_lines_crlf( "tbl0, ,\" \",#"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Rows_N() { + root = fx_nde.tbl_ + ( "numbers" + , fx_nde.row_vals_(1, 2, 3) + , fx_nde.row_vals_(4, 5, 6) + ); + expd = String_.Concat_lines_crlf + ( "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Tbls_N_Empty() { + root = fx_nde.root_ + ( fx_nde.tbl_("tbl0") + , fx_nde.tbl_("tbl1") + ); + expd = String_.Concat_lines_crlf + ( "tbl0, ,\" \",#" + , "tbl1, ,\" \",#" + ); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Tbls_N() { + root = fx_nde.root_ + ( fx_nde.tbl_("letters" + , fx_nde.row_vals_("a", "b", "c")) + , fx_nde.tbl_("numbers" + , fx_nde.row_vals_(1, 2, 3) + , fx_nde.row_vals_(4, 5, 6) + )); + expd = String_.Concat_lines_crlf + ( "letters, ,\" \",#" + , "a,b,c" + , "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_XtoStr(wtr, root, expd); + } + GfoNde root; String expd; DsvDataWtr wtr = DsvDataWtr_.csv_dat_(); DsvDataWtr_fxt fx = DsvDataWtr_fxt.new_(); GfoNdeFxt fx_nde = GfoNdeFxt.new_(); +} diff --git a/100_core/src/gplx/langs/dsvs/DsvHeaderList.java b/100_core/src/gplx/langs/dsvs/DsvHeaderList.java new file mode 100644 index 000000000..5f2d01778 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvHeaderList.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +public class DsvHeaderList { + @gplx.Internal protected int Count() {return list.Count();} + @gplx.Internal protected DsvHeaderItm Get_at(int i) {return (DsvHeaderItm)list.Get_at(i);} + public DsvHeaderList Add_LeafTypes() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_LeafTypes, null)); return this;} + public DsvHeaderList Add_LeafNames() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_LeafNames, null)); return this;} + public DsvHeaderList Add_TableName() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_TableName, null)); return this;} + public DsvHeaderList Add_BlankLine() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_BlankLine, null)); return this;} + public DsvHeaderList Add_Comment(String comment) {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_Comment, comment)); return this;} + void Add(DsvHeaderItm data) {list.Add(data);} + + List_adp list = List_adp_.New(); + public static DsvHeaderList new_() {return new DsvHeaderList();} DsvHeaderList() {} +} +class DsvHeaderItm { + public int Id() {return id;} int id; + public Object Val() {return val;} Object val; + @gplx.Internal protected DsvHeaderItm(int id, Object val) {this.id = id; this.val = val;} + + public static final int + Id_Comment = 1 + , Id_TableName = 2 + , Id_BlankLine = 3 + , Id_LeafTypes = 4 + , Id_LeafNames = 5 + ; +} diff --git a/100_core/src/gplx/langs/dsvs/DsvStoreLayout.java b/100_core/src/gplx/langs/dsvs/DsvStoreLayout.java new file mode 100644 index 000000000..657cfa608 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvStoreLayout.java @@ -0,0 +1,51 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +public class DsvStoreLayout { + public DsvHeaderList HeaderList() {return headerList;} DsvHeaderList headerList = DsvHeaderList.new_(); + @gplx.Internal protected boolean WriteCmdSequence() {return writeCmdSequence;} @gplx.Internal protected DsvStoreLayout WriteCmdSequence_(boolean val) {writeCmdSequence = val; return this;} private boolean writeCmdSequence; + + static DsvStoreLayout new_() {return new DsvStoreLayout();} + public static DsvStoreLayout csv_dat_() {return new_();} + public static DsvStoreLayout csv_hdr_() { + DsvStoreLayout rv = new_(); + rv.HeaderList().Add_LeafNames(); + return rv; + } + public static DsvStoreLayout dsv_brief_() { + DsvStoreLayout rv = new_(); + rv.writeCmdSequence = true; + return rv; + } + public static DsvStoreLayout dsv_full_() { + DsvStoreLayout rv = DsvStoreLayout.new_(); + rv.writeCmdSequence = true; + rv.HeaderList().Add_BlankLine(); + rv.HeaderList().Add_BlankLine(); + rv.HeaderList().Add_Comment("================================"); + rv.HeaderList().Add_TableName(); + rv.HeaderList().Add_Comment("================================"); + rv.HeaderList().Add_LeafTypes(); + rv.HeaderList().Add_LeafNames(); + rv.HeaderList().Add_Comment("================================"); + return rv; + } + public static DsvStoreLayout as_(Object obj) {return obj instanceof DsvStoreLayout ? (DsvStoreLayout)obj : null;} + public static DsvStoreLayout cast(Object obj) {try {return (DsvStoreLayout)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, DsvStoreLayout.class, obj);}} + public static final String Key_const = "StoreLayoutWtr"; +} diff --git a/100_core/src/gplx/langs/dsvs/DsvSymbols.java b/100_core/src/gplx/langs/dsvs/DsvSymbols.java new file mode 100644 index 000000000..aba65be55 --- /dev/null +++ b/100_core/src/gplx/langs/dsvs/DsvSymbols.java @@ -0,0 +1,52 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.dsvs; import gplx.*; import gplx.langs.*; +class DsvSymbols { + public String FldSep() {return fldSep;} public DsvSymbols FldSep_(String v) {fldSep = v; CmdSequence_set(); return this;} private String fldSep; + public String RowSep() {return rowSep;} + public DsvSymbols RowSep_(String v) { + rowSep = v; + rowSepIsNewLine = String_.Has(v, "\n") || String_.Has(v, "\r"); + return this; + } String rowSep; + public String QteDlm() {return qteDlm;} public void QteDlm_set(String v) {qteDlm = v; CmdSequence_set();} private String qteDlm; + public String CmdDlm() {return cmdDlm;} public void CmdDlm_set(String v) {cmdDlm = v; CmdSequence_set();} private String cmdDlm; + public String CmdSequence() {return cmdSequence;} private String cmdSequence; + public String CommentSym() {return commentSym;} public void CommentSym_set(String v) {commentSym = v;} private String commentSym; + public String TblNameSym() {return tblNameSym;} public void TblNamesSym_set(String v) {tblNameSym = v;} private String tblNameSym; + public String FldNamesSym() {return fldNamesSym;} public void FldNamesSym_set(String v) {fldNamesSym = v;} private String fldNamesSym; + public String FldTypesSym() {return fldTypesSym;} public void FldTypesSym_set(String v) {fldTypesSym = v;} private String fldTypesSym; + public boolean RowSepIsNewLine() {return rowSepIsNewLine;} private boolean rowSepIsNewLine; + public void Reset() { + fldSep = ","; + RowSep_ ("\r\n"); + + qteDlm = "\""; + cmdDlm = " "; + CmdSequence_set(); + + commentSym = "//"; + tblNameSym = "#"; + fldNamesSym = "@"; + fldTypesSym = "$"; + } + void CmdSequence_set() { // commandDelimiters are repeated; once without quotes and once with quotes; ex: , ," ", + cmdSequence = String_.Concat(cmdDlm, fldSep, qteDlm, cmdDlm, qteDlm); + } + public static DsvSymbols default_() {return new DsvSymbols();} DsvSymbols() {this.Reset();} +} diff --git a/100_core/src/gplx/langs/gfs/GfsCore.java b/100_core/src/gplx/langs/gfs/GfsCore.java new file mode 100644 index 000000000..92e41b423 --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsCore.java @@ -0,0 +1,90 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +import gplx.core.gfo_regys.*; +public class GfsCore implements Gfo_invk { + public Gfo_invk Root() {return root;} + @gplx.Internal protected GfsRegy Root_as_regy() {return root;} GfsRegy root = GfsRegy.new_(); + public void Clear() {root.Clear();} + public GfoMsgParser MsgParser() {return msgParser;} public GfsCore MsgParser_(GfoMsgParser v) {msgParser = v; return this;} GfoMsgParser msgParser; + public void Del(String key) {root.Del(key);} + public void AddLib(GfsLibIni... ary) {for (GfsLibIni itm : ary) itm.Ini(this);} + public void AddCmd(Gfo_invk invk, String key) {root.AddCmd(invk, key);} + public void AddObj(Gfo_invk invk, String key) {root.AddObj(invk, key);} + public void AddDeep(Gfo_invk invk, String... ary) { + Gfo_invk_cmd_mgr_owner cur = (Gfo_invk_cmd_mgr_owner)((GfsRegyItm)root.Get_by(ary[0])).InvkAble(); + for (int i = 1; i < ary.length - 1; i++) + cur = (Gfo_invk_cmd_mgr_owner)cur.InvkMgr().Invk(GfsCtx.Instance, 0, ary[i], GfoMsg_.Null, cur); + cur.InvkMgr().Add_cmd(ary[ary.length - 1], invk); + } + public String FetchKey(Gfo_invk invk) {return root.FetchByType(invk).Key();} + public Object ExecOne(GfsCtx ctx, GfoMsg msg) {return GfsCore_.Exec(ctx, root, msg, null, 0);} + public Object ExecOne_to(GfsCtx ctx, Gfo_invk invk, GfoMsg msg) {return GfsCore_.Exec(ctx, invk, msg, null, 0);} + public Object ExecMany(GfsCtx ctx, GfoMsg rootMsg) { + Object rv = null; + for (int i = 0; i < rootMsg.Subs_count(); i++) { + GfoMsg subMsg = (GfoMsg)rootMsg.Subs_getAt(i); + rv = GfsCore_.Exec(ctx, root, subMsg, null, 0); + } + return rv; + } + public void ExecRegy(String key) { + GfoRegyItm itm = GfoRegy.Instance.FetchOrNull(key); + if (itm == null) {UsrDlg_.Instance.Warn(UsrMsg.new_("could not find script for key").Add("key", key)); return;} + Io_url url = itm.Url(); + if (!Io_mgr.Instance.ExistsFil(url)) { + UsrDlg_.Instance.Warn(UsrMsg.new_("script url does not exist").Add("key", key).Add("url", url)); + return; + } + this.ExecText(Io_mgr.Instance.LoadFilStr(url)); + } + public Object ExecFile_ignoreMissing(Io_url url) {if (!Io_mgr.Instance.ExistsFil(url)) return null; return ExecText(Io_mgr.Instance.LoadFilStr(url));} + public Object ExecFile(Io_url url) {return ExecText(Io_mgr.Instance.LoadFilStr(url));} + public Object ExecFile_ignoreMissing(Gfo_invk root, Io_url url) { + if (!Io_mgr.Instance.ExistsFil(url)) return null; + if (msgParser == null) throw Err_.new_wo_type("msgParser is null"); + return Exec_bry(Io_mgr.Instance.LoadFilBry(url), root); + } + public Object Exec_bry(byte[] bry) {return Exec_bry(bry, root);} + public Object Exec_bry(byte[] bry, Gfo_invk root) { + GfoMsg rootMsg = msgParser.ParseToMsg(String_.new_u8(bry)); + Object rv = null; + GfsCtx ctx = GfsCtx.new_(); + for (int i = 0; i < rootMsg.Subs_count(); i++) { + GfoMsg subMsg = (GfoMsg)rootMsg.Subs_getAt(i); + rv = GfsCore_.Exec(ctx, root, subMsg, null, 0); + } + return rv; + } + public Object ExecText(String text) { + if (msgParser == null) throw Err_.new_wo_type("msgParser is null"); + GfsCtx ctx = GfsCtx.new_(); + return ExecMany(ctx, msgParser.ParseToMsg(text)); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_ExecFil)) { + Io_url url = m.ReadIoUrl("url"); + if (ctx.Deny()) return this; + return ExecFile(url); + } + else return Gfo_invk_.Rv_unhandled; +// return this; + } public static final String Invk_ExecFil = "ExecFil"; + public static final GfsCore Instance = new GfsCore(); + @gplx.Internal protected static GfsCore new_() {return new GfsCore();} +} diff --git a/100_core/src/gplx/langs/gfs/GfsCoreHelp.java b/100_core/src/gplx/langs/gfs/GfsCoreHelp.java new file mode 100644 index 000000000..f85f81e3f --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsCoreHelp.java @@ -0,0 +1,73 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +import gplx.core.strings.*; +class GfsCoreHelp implements Gfo_invk { + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + String path = m.ReadStrOr("path", ""); + if (String_.Eq(path, "")) { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < core.Root_as_regy().Count(); i++) { + GfsRegyItm itm = (GfsRegyItm)core.Root_as_regy().Get_at(i); + sb.Add_spr_unless_first(itm.Key(), String_.CrLf, i); + } + return sb.To_str(); + } + else return Exec(ctx, core.Root_as_regy(), path); + } + public static Err Err_Unhandled(String objPath, String key) {return Err_.new_wo_type("obj does not handle msgKey", "objPath", objPath, "key", key).Trace_ignore_add_1_();} + static Err Err_Unhandled(String[] itmAry, int i) { + String_bldr sb = String_bldr_.new_(); + for (int j = 0; j < i; j++) + sb.Add_spr_unless_first(itmAry[j], ".", j); + return Err_Unhandled(sb.To_str(), itmAry[i]); + } + static Object Exec(GfsCtx rootCtx, Gfo_invk rootInvk, String path) { + String[] itmAry = String_.Split(path, "."); + Gfo_invk invk = rootInvk; + GfsCtx ctx = GfsCtx.new_(); + Object curRv = null; + for (int i = 0; i < itmAry.length; i++) { + String itm = itmAry[i]; + curRv = invk.Invk(ctx, 0, itm, GfoMsg_.Null); + if (curRv == Gfo_invk_.Rv_unhandled) throw Err_Unhandled(itmAry, i); + invk = (Gfo_invk)curRv; + } + GfsCoreHelp helpData = GfsCoreHelp.as_(curRv); + if (helpData != null) { // last itm is actually Method + return ""; + } + else { + ctx = GfsCtx.new_().Help_browseMode_(true); + invk.Invk(ctx, 0, "", GfoMsg_.Null); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < ctx.Help_browseList().Count(); i++) { + String s = (String)ctx.Help_browseList().Get_at(i); + sb.Add_spr_unless_first(s, String_.CrLf, i); + } + return sb.To_str(); + } + } + public static GfsCoreHelp as_(Object obj) {return obj instanceof GfsCoreHelp ? (GfsCoreHelp)obj : null;} + public static GfsCoreHelp new_(GfsCore core) { + GfsCoreHelp rv = new GfsCoreHelp(); + rv.core = core; + return rv; + } GfsCoreHelp() {} + GfsCore core; +} diff --git a/100_core/src/gplx/langs/gfs/GfsCore_.java b/100_core/src/gplx/langs/gfs/GfsCore_.java new file mode 100644 index 000000000..d761fefe7 --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsCore_.java @@ -0,0 +1,96 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +public class GfsCore_ { + public static final String Arg_primitive = "v"; + public static Object Exec(GfsCtx ctx, Gfo_invk owner_invk, GfoMsg owner_msg, Object owner_primitive, int depth) { + if (owner_msg.Args_count() == 0 && owner_msg.Subs_count() == 0 && String_.Eq(owner_msg.Key(), "")) {UsrDlg_.Instance.Warn("empty msg"); return Gfo_invk_.Rv_unhandled;} + if (owner_primitive != null) owner_msg.Parse_(false).Add(GfsCore_.Arg_primitive, owner_primitive); + Object rv = owner_invk.Invk(ctx, 0, owner_msg.Key(), owner_msg); + if (rv == Gfo_invk_.Rv_cancel) return rv; + else if (rv == Gfo_invk_.Rv_unhandled) { + if (ctx.Fail_if_unhandled()) + throw Err_.new_wo_type("Object does not support key", "key", owner_msg.Key(), "ownerType", Type_adp_.FullNameOf_obj(owner_invk)); + else { + Gfo_usr_dlg usr_dlg = ctx.Usr_dlg(); + if (usr_dlg != null) usr_dlg.Warn_many(GRP_KEY, "unhandled_key", "Object does not support key: key=~{0} ownerType=~{1}", owner_msg.Key(), Type_adp_.FullNameOf_obj(owner_invk)); + return Gfo_invk_.Noop; + } + } + if (owner_msg.Subs_count() == 0) { // msg is leaf + GfsRegyItm regyItm = GfsRegyItm.as_(rv); + if (regyItm == null) return rv; // rv is primitive or other non-regy Object + if (regyItm.IsCmd()) // rv is cmd; invk cmd + return regyItm.InvkAble().Invk(ctx, 0, owner_msg.Key(), owner_msg); + else // rv is host + return regyItm.InvkAble(); + } + else { // intermediate; cast to invk and call Exec + Gfo_invk invk = Gfo_invk_.as_(rv); + Object primitive = null; + if (invk == null) { // rv is primitive; find appropriate mgr + Class type = rv.getClass(); + if (type == String.class) invk = String_.Gfs; + else if (Int_.TypeMatch(type)) invk = Int_.Gfs; + else if (Type_adp_.Eq(type, Bool_.Cls_ref_type)) invk = Bool_.Gfs; + else throw Err_.new_wo_type("unknown primitive", "type", Type_adp_.NameOf_type(type), "obj", Object_.Xto_str_strict_or_null_mark(rv)); + primitive = rv; + } + Object exec_rv = null; + int len = owner_msg.Subs_count(); + for (int i = 0; i < len; i++) // iterate over subs; needed for a{b;c;d;} + exec_rv = Exec(ctx, invk, owner_msg.Subs_getAt(i), primitive, depth + 1); + return exec_rv; + } + } + static final String GRP_KEY = "gplx.gfs_core"; +} +// class GfsRegyMgr : Gfo_invk { +// public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { +// if (ctx.Match(k, Invk_Add)) { +// String libKey = m.ReadStr("libKey"), regKey = m.ReadStr("regKey"); +// if (ctx.Deny()) return this; +// GfsRegyItm itm = regy.Get_by(libKey); +// if (regy.Has(regKey)) {ctx.Write_warn("'{0}' already exists", regKey); return this;} +// regy.Add(regKey, itm.InvkAble(), itm.Type_cmd()); +// ctx.Write_note("added '{0}' as '{1}'", regKey, libKey); +// } +// else if (ctx.Match(k, Invk_Del)) { +// String regKey = m.ReadStr("regKey"); +// if (ctx.Deny()) return this; +// if (!regy.Has(regKey)) {ctx.Write_warn("{0} does not exist", regKey); return this;} +// regy.Del(regKey); +// ctx.Write_note("removed '{0}'", regKey); +// } +// else if (ctx.Match(k, Invk_Load)) { +// Io_url url = (Io_url)m.ReadObj("url", Io_url_.Parser); +// if (ctx.Deny()) return this; +// String loadText = Io_mgr.Instance.LoadFilStr(url); +// GfoMsg loadMsg = core.MsgParser().ParseToMsg(loadText); +// return core.Exec(ctx, loadMsg); +// } +// else return Gfo_invk_.Rv_unhandled; +// return this; +// } public static final String Invk_Add = "Add", Invk_Del = "Del", Invk_Load = "Load"; +// GfsCore core; GfsRegy regy; +// public static GfsRegyMgr new_(GfsCore core, GfsRegy regy) { +// GfsRegyMgr rv = new GfsRegyMgr(); +// rv.core = core; rv.regy = regy; +// return rv; +// } GfsRegyMgr() {} +// } diff --git a/100_core/src/gplx/langs/gfs/GfsCore_tst.java b/100_core/src/gplx/langs/gfs/GfsCore_tst.java new file mode 100644 index 000000000..b45c009f3 --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsCore_tst.java @@ -0,0 +1,113 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +import org.junit.*; +public class GfsCore_tst { + @Before public void setup() { + core = GfsCore.new_(); + core.AddObj(String_.Gfs, "String_"); + core.AddObj(Int_.Gfs, "Int_"); + } GfsCore core; + @Test public void Basic() { // String_.Len('abc') >> 3 + tst_Msg + ( msg_("String_").Subs_ + ( msg_("Len").Add("v", "abc")) + , 3); + } + @Test public void PrimitiveConversion() { // String_.Len('abc').Add(-3) >> 0 + tst_Msg + ( msg_("String_").Subs_ + ( msg_("Len").Add("v", "abc").Subs_ + ( msg_("Add").Add("operand", -3)) + ) + , 0); + } +// @Test public void Fail_notFound() { // String_.DoesNotExists +// tst_Err +// ( msg_("help").Add("", "String_.DoesNotExist") +// , GfsHelp.Err_Unhandled("String_", "DoesNotExist")); +// } + @Test public void Cmd() { // cmd + core.AddCmd(new GfsTest_cmd(), "testCmd"); + tst_Msg + ( msg_("testCmd").Add("s", "pass") + , "pass"); + } + @Test public void EmptyMsg() { + tst_Msg + ( msg_("") + , Gfo_invk_.Rv_unhandled); + } +// @Test public void Fail_argMissing() { // String_.Len() +// tst_String__Len_Err(msg_("Len"), GfsCtx.Err_KeyNotFound("v", "<>")); +// } +// @Test public void Fail_argWrongKey() { // String_.Len(badKey='abc') +// tst_String__Len_Err(msg_("Len").Add("badKey", "abc"), GfsCtx.Err_KeyNotFound("v", "badKey;")); +// } +// @Test public void Fail_argExtraKey() { // String_.Len(v='abc' extraKey=1) +// tst_String__Len_Err(msg_("Len").Add("v", "abc").Add("extraKey", 1), GfsCtx.Err_KeyNotFound("v", "badKey;")); +// } + @Test public void Add_obj_deep() { // String_.Len(badKey='abc') + GfsCore_tst_nest obj1 = GfsCore_tst_nest.new_("1", "val1"); + GfsCore_tst_nest obj1_1 = GfsCore_tst_nest.new_("1_1", "val2"); + core.AddObj(obj1, "1"); + core.AddDeep(obj1_1, "1", "1_1"); + + GfoMsg root = GfoMsg_.root_("1", "1_1", GfsCore_tst_nest.Prop2); + Object actl = core.ExecOne(GfsCtx.Instance, root); + Tfds.Eq("val2", actl); + } + void tst_String__Len_Err(GfoMsg m, Err expd) { + tst_Err(msg_("String_").Subs_(m), expd); + } + void tst_Err(GfoMsg msg, Err expd) { + GfoMsg root = msg; + GfsCtx ctx = GfsCtx.new_(); + try { + core.ExecOne(ctx, root); + Tfds.Fail_expdError(); + } + catch (Exception e) { + Tfds.Eq_err(expd, e); + } + } + GfoMsg msg_(String k) {return GfoMsg_.new_cast_(k);} + void tst_Msg(GfoMsg msg, Object expd) { + GfsCtx ctx = GfsCtx.new_(); + Object actl = core.ExecOne(ctx, msg); + Tfds.Eq(expd, actl); + } +} +class GfsCore_tst_nest implements Gfo_invk, Gfo_invk_cmd_mgr_owner { + public Gfo_invk_cmd_mgr InvkMgr() {return invkMgr;} Gfo_invk_cmd_mgr invkMgr = Gfo_invk_cmd_mgr.new_(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Prop1)) {return prop1;} + else if (ctx.Match(k, Prop2)) {return prop2;} + else if (ctx.Match(k, prop1)) {return this;} + else return invkMgr.Invk(ctx, ikey, k, m, this); + } public static final String Prop1 = "Prop1", Prop2 = "Prop2"; + String prop1, prop2; + public static GfsCore_tst_nest new_(String prop1, String prop2) { + GfsCore_tst_nest rv = new GfsCore_tst_nest(); + rv.prop1 = prop1; rv.prop2 = prop2; + return rv; + } GfsCore_tst_nest() {} +} +class GfsTest_cmd implements Gfo_invk { + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return m.ReadStr("s");} +} diff --git a/100_core/src/gplx/langs/gfs/GfsLibIni.java b/100_core/src/gplx/langs/gfs/GfsLibIni.java new file mode 100644 index 000000000..82ea544e9 --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsLibIni.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +public interface GfsLibIni { + void Ini(GfsCore core); +} diff --git a/100_core/src/gplx/langs/gfs/GfsLibIni_core.java b/100_core/src/gplx/langs/gfs/GfsLibIni_core.java new file mode 100644 index 000000000..d1c5a9f2f --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsLibIni_core.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +import gplx.core.gfo_regys.*; +public class GfsLibIni_core implements GfsLibIni { + public void Ini(GfsCore core) { + core.AddCmd(GfsCoreHelp.new_(core), "help"); + core.AddObj(String_.Gfs, "String_"); + core.AddObj(Int_.Gfs, "Int_"); + core.AddObj(DateAdp_.Gfs, "Date_"); + core.AddObj(RandomAdp_.Gfs, "RandomAdp_"); + core.AddObj(GfoTemplateFactory.Instance, "factory"); + core.AddObj(GfoRegy.Instance, "GfoRegy_"); + core.AddObj(GfsCore.Instance, "GfsCore_"); + core.AddObj(gplx.core.ios.IoUrlInfoRegy.Instance, "IoUrlInfoRegy_"); + core.AddObj(gplx.core.ios.IoUrlTypeRegy.Instance, "IoUrlTypeRegy_"); + + GfoRegy.Instance.Parsers().Add("Io_url", Io_url_.Parser); + } + public static final GfsLibIni_core Instance = new GfsLibIni_core(); GfsLibIni_core() {} +} diff --git a/100_core/src/gplx/langs/gfs/GfsRegy.java b/100_core/src/gplx/langs/gfs/GfsRegy.java new file mode 100644 index 000000000..2bb823ff8 --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsRegy.java @@ -0,0 +1,54 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +class GfsRegy implements Gfo_invk { + public int Count() {return hash.Count();} + public void Clear() {hash.Clear(); typeHash.Clear();} + public boolean Has(String k) {return hash.Has(k);} + public GfsRegyItm Get_at(int i) {return (GfsRegyItm)hash.Get_at(i);} + public GfsRegyItm Get_by(String key) {return (GfsRegyItm)hash.Get_by(key);} + public GfsRegyItm FetchByType(Gfo_invk invk) {return (GfsRegyItm)typeHash.Get_by(Type_adp_.FullNameOf_obj(invk));} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + Object rv = (GfsRegyItm)hash.Get_by(k); if (rv == null) throw Err_.new_missing_key(k); + return rv; + } + public void AddObj(Gfo_invk invk, String key) {Add(key, invk, false);} + public void AddCmd(Gfo_invk invk, String key) {Add(key, invk, true);} + public void Add(String key, Gfo_invk invk, boolean typeCmd) { + if (hash.Has(key)) return; + GfsRegyItm regyItm = new GfsRegyItm().Key_(key).InvkAble_(invk).IsCmd_(typeCmd).TypeKey_(Type_adp_.FullNameOf_obj(invk)); + hash.Add(key, regyItm); + typeHash.Add_if_dupe_use_1st(regyItm.TypeKey(), regyItm); // NOTE: changed to allow same Object to be added under different aliases (app, xowa) DATE:2014-06-09; + } + public void Del(String k) { + GfsRegyItm itm =(GfsRegyItm)hash.Get_by(k); + if (itm != null) typeHash.Del(itm.TypeKey()); + hash.Del(k); + } + Hash_adp typeHash = Hash_adp_.New(); + Ordered_hash hash = Ordered_hash_.New(); + public static GfsRegy new_() {return new GfsRegy();} GfsRegy() {} +} +class GfsRegyItm implements Gfo_invk { + public String Key() {return key;} public GfsRegyItm Key_(String v) {key = v; return this;} private String key; + public String TypeKey() {return typeKey;} public GfsRegyItm TypeKey_(String v) {typeKey = v; return this;} private String typeKey; + public boolean IsCmd() {return isCmd;} public GfsRegyItm IsCmd_(boolean v) {isCmd = v; return this;} private boolean isCmd; + public Gfo_invk InvkAble() {return invkAble;} public GfsRegyItm InvkAble_(Gfo_invk v) {invkAble = v; return this;} Gfo_invk invkAble; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return invkAble.Invk(ctx, ikey, k, m);} + public static GfsRegyItm as_(Object obj) {return obj instanceof GfsRegyItm ? (GfsRegyItm)obj : null;} +} diff --git a/100_core/src/gplx/langs/gfs/GfsTypeNames.java b/100_core/src/gplx/langs/gfs/GfsTypeNames.java new file mode 100644 index 000000000..4581f4ecd --- /dev/null +++ b/100_core/src/gplx/langs/gfs/GfsTypeNames.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +public class GfsTypeNames { + public static final String + String = "string" + , Int = "int" + , Bool = "bool" + , Float = "float" + , YesNo = "yn" + , Date = "date" + ; +} diff --git a/100_core/src/gplx/langs/gfs/Gfs_Date_tst.java b/100_core/src/gplx/langs/gfs/Gfs_Date_tst.java new file mode 100644 index 000000000..2c62cfa7f --- /dev/null +++ b/100_core/src/gplx/langs/gfs/Gfs_Date_tst.java @@ -0,0 +1,42 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.gfs; import gplx.*; import gplx.langs.*; +import org.junit.*; +public class Gfs_Date_tst { + @Before public void setup() { + fx = new GfsCoreFxt(); + fx.AddObj(DateAdp_.Gfs, "Date_"); + Datetime_now.Manual_y_(); + } GfsCoreFxt fx; + @Test public void Now() { + fx.tst_MsgStr(fx.msg_(String_.Ary("Date_", "Now")), DateAdp_.parse_gplx("2001-01-01 00:00:00.000")); + } + @Test public void Add_day() { + fx.tst_MsgStr(fx.msg_(String_.Ary("Date_", "Now", "Add_day"), Keyval_.new_("days", 1)), DateAdp_.parse_gplx("2001-01-02 00:00:00.000")); + } +} +class GfsCoreFxt { + public GfsCore Core() {return core;} GfsCore core = GfsCore.new_(); + public GfoMsg msg_(String[] ary, Keyval... kvAry) {return GfoMsg_.root_leafArgs_(ary, kvAry);} + public void AddObj(Gfo_invk invk, String s) {core.AddObj(invk, s);} + public void tst_MsgStr(GfoMsg msg, Object expd) { + GfsCtx ctx = GfsCtx.new_(); + Object actl = core.ExecOne(ctx, msg); + Tfds.Eq(Object_.Xto_str_strict_or_null_mark(expd), Object_.Xto_str_strict_or_null_mark(actl)); + } +} diff --git a/100_core/src/gplx/langs/htmls/Url_encoder_interface.java b/100_core/src/gplx/langs/htmls/Url_encoder_interface.java new file mode 100644 index 000000000..61b282eda --- /dev/null +++ b/100_core/src/gplx/langs/htmls/Url_encoder_interface.java @@ -0,0 +1,22 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.htmls; import gplx.*; import gplx.langs.*; +public interface Url_encoder_interface { + String Encode_str(String v); + byte[] Encode_bry(String v); +} diff --git a/100_core/src/gplx/langs/htmls/Url_encoder_interface_same.java b/100_core/src/gplx/langs/htmls/Url_encoder_interface_same.java new file mode 100644 index 000000000..6bc30b1b7 --- /dev/null +++ b/100_core/src/gplx/langs/htmls/Url_encoder_interface_same.java @@ -0,0 +1,23 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.htmls; import gplx.*; import gplx.langs.*; +public class Url_encoder_interface_same implements Url_encoder_interface { + public String Encode_str(String v) {return v;} + public byte[] Encode_bry(String v) {return Bry_.new_u8(v);} + public static final Url_encoder_interface_same Instance = new Url_encoder_interface_same(); Url_encoder_interface_same() {} +} diff --git a/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_.java b/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_.java new file mode 100644 index 000000000..83623b678 --- /dev/null +++ b/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.htmls.entitys; import gplx.*; import gplx.langs.*; import gplx.langs.htmls.*; +public class Gfh_entity_ { + public static final String + Nl_str = " " + ; + public static final byte[] + Lt_bry = Bry_.new_a7("<"), Gt_bry = Bry_.new_a7(">") + , Amp_bry = Bry_.new_a7("&"), Quote_bry = Bry_.new_a7(""") + , Apos_num_bry = Bry_.new_a7("'") + , Apos_key_bry = Bry_.new_a7("'") + , Eq_bry = Bry_.new_a7("=") + , Nl_bry = Bry_.new_a7(Nl_str), Space_bry = Bry_.new_a7(" ") + , Pipe_bry = Bry_.new_a7("|") + , Colon_bry = Bry_.new_a7(":"), Underline_bry = Bry_.new_a7("_"), Asterisk_bry = Bry_.new_a7("*") + , Brack_bgn_bry = Bry_.new_a7("["), Brack_end_bry = Bry_.new_a7("]") + , Nbsp_num_bry = Bry_.new_a7(" ") + ; +} diff --git a/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_itm.java b/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_itm.java new file mode 100644 index 000000000..bb1a6fd97 --- /dev/null +++ b/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_itm.java @@ -0,0 +1,57 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.htmls.entitys; import gplx.*; import gplx.langs.*; import gplx.langs.htmls.*; +public class Gfh_entity_itm { // TS:immutable + public Gfh_entity_itm(byte tid, int char_int, byte[] xml_name_bry) { + this.tid = tid; + this.char_int = char_int; + this.u8_bry = gplx.core.intls.Utf16_.Encode_int_to_bry(char_int); + this.xml_name_bry = xml_name_bry; + this.key_name_len = xml_name_bry.length - 2; // 2 for & and ; + } + public byte Tid() {return tid;} private final byte tid; + public int Char_int() {return char_int;} private final int char_int; // val; EX: 160 + public byte[] U8_bry() {return u8_bry;} private final byte[] u8_bry; // EX: new byte[] {192, 160}; (C2, A0) + public byte[] Xml_name_bry() {return xml_name_bry;} private final byte[] xml_name_bry; // EX: " " + public int Key_name_len() {return key_name_len;} private final int key_name_len; // EX: "nbsp".Len + + public void Print_ncr(Bry_bfr bfr) { + switch (char_int) { + case Byte_ascii.Lt: case Byte_ascii.Gt: case Byte_ascii.Quote: case Byte_ascii.Amp: + bfr.Add(xml_name_bry); // NOTE: never write actual char; EX: "<" should be written as "<", not "<" + break; + default: + bfr.Add(Escape_bgn); // &# + bfr.Add_int_variable(char_int); // 160 + bfr.Add_byte(Byte_ascii.Semic); // ; + break; + } + } + public void Print_literal(Bry_bfr bfr) { + switch (char_int) { + case Byte_ascii.Lt: bfr.Add(Gfh_entity_.Lt_bry); break; // NOTE: never write actual char; EX: "<" should be written as "<", not "<"; MW does same; DATE:2014-11-07 + case Byte_ascii.Gt: bfr.Add(Gfh_entity_.Gt_bry); break; + case Byte_ascii.Quote: bfr.Add(Gfh_entity_.Quote_bry); break; + case Byte_ascii.Amp: bfr.Add(Gfh_entity_.Amp_bry); break; + default: bfr.Add(u8_bry); break; // write literal; EX: "[" not "[" + } + } + private static final byte[] Escape_bgn = Bry_.new_a7("&#"); + public static final byte Tid_name_std = 1, Tid_name_xowa = 2, Tid_num_hex = 3, Tid_num_dec = 4; + public static final int Char_int_null = -1; +} diff --git a/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_trie.java b/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_trie.java new file mode 100644 index 000000000..8bd904731 --- /dev/null +++ b/100_core/src/gplx/langs/htmls/entitys/Gfh_entity_trie.java @@ -0,0 +1,317 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.htmls.entitys; import gplx.*; import gplx.langs.*; import gplx.langs.htmls.*; +import gplx.core.btries.*; +public class Gfh_entity_trie { // TS + public static final String // NOTE: top_define; entities needed for escaping + Str__xowa_lt = "&xowa_lt;" + , Str__xowa_brack_bgn = "&xowa_brack_bgn;" + , Str__xowa_brack_end = "&xowa_brack_end;" + , Str__xowa_pipe = "&xowa_pipe;" + , Str__xowa_apos = "&xowa_apos;" + , Str__xowa_colon = "&xowa_colon;" + , Str__xowa_underline = "&xowa_underline;" + , Str__xowa_asterisk = "&xowa_asterisk;" + , Str__xowa_space = "&xowa_space;" + , Str__xowa_nl = "&xowa_nl;" + , Str__xowa_dash = "&xowa_dash;" + ; + public static final Btrie_slim_mgr Instance = New(); Gfh_entity_trie() {} + private static Btrie_slim_mgr New() {// REF.MW: Sanitizer|$wgHtmlEntities; NOTE:added apos + Btrie_slim_mgr rv = Btrie_slim_mgr.cs(); + Add_name(rv, Bool_.Y, 60, Str__xowa_lt); + Add_name(rv, Bool_.Y, 91, Str__xowa_brack_bgn); + Add_name(rv, Bool_.Y, 93, Str__xowa_brack_end); + Add_name(rv, Bool_.Y, 124, Str__xowa_pipe); + Add_name(rv, Bool_.Y, 39, Str__xowa_apos); + Add_name(rv, Bool_.Y, 58, Str__xowa_colon); + Add_name(rv, Bool_.Y, 95, Str__xowa_underline); + Add_name(rv, Bool_.Y, 42, Str__xowa_asterisk); + Add_name(rv, Bool_.Y, 32, Str__xowa_space); + Add_name(rv, Bool_.Y, 10, Str__xowa_nl); + Add_name(rv, Bool_.Y, 45, Str__xowa_dash); + Add_name(rv, Bool_.N, 39, "'"); + Add_name(rv, Bool_.N, 193, "Á"); + Add_name(rv, Bool_.N, 225, "á"); + Add_name(rv, Bool_.N, 194, "Â"); + Add_name(rv, Bool_.N, 226, "â"); + Add_name(rv, Bool_.N, 180, "´"); + Add_name(rv, Bool_.N, 198, "Æ"); + Add_name(rv, Bool_.N, 230, "æ"); + Add_name(rv, Bool_.N, 192, "À"); + Add_name(rv, Bool_.N, 224, "à"); + Add_name(rv, Bool_.N, 8501, "ℵ"); + Add_name(rv, Bool_.N, 913, "Α"); + Add_name(rv, Bool_.N, 945, "α"); + Add_name(rv, Bool_.N, 38, "&"); + Add_name(rv, Bool_.N, 8743, "∧"); + Add_name(rv, Bool_.N, 8736, "∠"); + Add_name(rv, Bool_.N, 197, "Å"); + Add_name(rv, Bool_.N, 229, "å"); + Add_name(rv, Bool_.N, 8776, "≈"); + Add_name(rv, Bool_.N, 195, "Ã"); + Add_name(rv, Bool_.N, 227, "ã"); + Add_name(rv, Bool_.N, 196, "Ä"); + Add_name(rv, Bool_.N, 228, "ä"); + Add_name(rv, Bool_.N, 8222, "„"); + Add_name(rv, Bool_.N, 914, "Β"); + Add_name(rv, Bool_.N, 946, "β"); + Add_name(rv, Bool_.N, 166, "¦"); + Add_name(rv, Bool_.N, 8226, "•"); + Add_name(rv, Bool_.N, 8745, "∩"); + Add_name(rv, Bool_.N, 199, "Ç"); + Add_name(rv, Bool_.N, 231, "ç"); + Add_name(rv, Bool_.N, 184, "¸"); + Add_name(rv, Bool_.N, 162, "¢"); + Add_name(rv, Bool_.N, 935, "Χ"); + Add_name(rv, Bool_.N, 967, "χ"); + Add_name(rv, Bool_.N, 710, "ˆ"); + Add_name(rv, Bool_.N, 9827, "♣"); + Add_name(rv, Bool_.N, 8773, "≅"); + Add_name(rv, Bool_.N, 169, "©"); + Add_name(rv, Bool_.N, 8629, "↵"); + Add_name(rv, Bool_.N, 8746, "∪"); + Add_name(rv, Bool_.N, 164, "¤"); + Add_name(rv, Bool_.N, 8224, "†"); + Add_name(rv, Bool_.N, 8225, "‡"); + Add_name(rv, Bool_.N, 8595, "↓"); + Add_name(rv, Bool_.N, 8659, "⇓"); + Add_name(rv, Bool_.N, 176, "°"); + Add_name(rv, Bool_.N, 916, "Δ"); + Add_name(rv, Bool_.N, 948, "δ"); + Add_name(rv, Bool_.N, 9830, "♦"); + Add_name(rv, Bool_.N, 247, "÷"); + Add_name(rv, Bool_.N, 201, "É"); + Add_name(rv, Bool_.N, 233, "é"); + Add_name(rv, Bool_.N, 202, "Ê"); + Add_name(rv, Bool_.N, 234, "ê"); + Add_name(rv, Bool_.N, 200, "È"); + Add_name(rv, Bool_.N, 232, "è"); + Add_name(rv, Bool_.N, 8709, "∅"); + Add_name(rv, Bool_.N, 8195, " "); + Add_name(rv, Bool_.N, 8194, " "); + Add_name(rv, Bool_.N, 917, "Ε"); + Add_name(rv, Bool_.N, 949, "ε"); + Add_name(rv, Bool_.N, 8801, "≡"); + Add_name(rv, Bool_.N, 919, "Η"); + Add_name(rv, Bool_.N, 951, "η"); + Add_name(rv, Bool_.N, 208, "Ð"); + Add_name(rv, Bool_.N, 240, "ð"); + Add_name(rv, Bool_.N, 203, "Ë"); + Add_name(rv, Bool_.N, 235, "ë"); + Add_name(rv, Bool_.N, 8364, "€"); + Add_name(rv, Bool_.N, 8707, "∃"); + Add_name(rv, Bool_.N, 402, "ƒ"); + Add_name(rv, Bool_.N, 8704, "∀"); + Add_name(rv, Bool_.N, 189, "½"); + Add_name(rv, Bool_.N, 188, "¼"); + Add_name(rv, Bool_.N, 190, "¾"); + Add_name(rv, Bool_.N, 8260, "⁄"); + Add_name(rv, Bool_.N, 915, "Γ"); + Add_name(rv, Bool_.N, 947, "γ"); + Add_name(rv, Bool_.N, 8805, "≥"); + Add_name(rv, Bool_.N, 62, ">"); + Add_name(rv, Bool_.N, 8596, "↔"); + Add_name(rv, Bool_.N, 8660, "⇔"); + Add_name(rv, Bool_.N, 9829, "♥"); + Add_name(rv, Bool_.N, 8230, "…"); + Add_name(rv, Bool_.N, 205, "Í"); + Add_name(rv, Bool_.N, 237, "í"); + Add_name(rv, Bool_.N, 206, "Î"); + Add_name(rv, Bool_.N, 238, "î"); + Add_name(rv, Bool_.N, 161, "¡"); + Add_name(rv, Bool_.N, 204, "Ì"); + Add_name(rv, Bool_.N, 236, "ì"); + Add_name(rv, Bool_.N, 8465, "ℑ"); + Add_name(rv, Bool_.N, 8734, "∞"); + Add_name(rv, Bool_.N, 8747, "∫"); + Add_name(rv, Bool_.N, 921, "Ι"); + Add_name(rv, Bool_.N, 953, "ι"); + Add_name(rv, Bool_.N, 191, "¿"); + Add_name(rv, Bool_.N, 8712, "∈"); + Add_name(rv, Bool_.N, 207, "Ï"); + Add_name(rv, Bool_.N, 239, "ï"); + Add_name(rv, Bool_.N, 922, "Κ"); + Add_name(rv, Bool_.N, 954, "κ"); + Add_name(rv, Bool_.N, 923, "Λ"); + Add_name(rv, Bool_.N, 955, "λ"); + Add_name(rv, Bool_.N, 9001, "⟨"); + Add_name(rv, Bool_.N, 171, "«"); + Add_name(rv, Bool_.N, 8592, "←"); + Add_name(rv, Bool_.N, 8656, "⇐"); + Add_name(rv, Bool_.N, 8968, "⌈"); + Add_name(rv, Bool_.N, 8220, "“"); + Add_name(rv, Bool_.N, 8804, "≤"); + Add_name(rv, Bool_.N, 8970, "⌊"); + Add_name(rv, Bool_.N, 8727, "∗"); + Add_name(rv, Bool_.N, 9674, "◊"); + Add_name(rv, Bool_.N, 8206, "‎"); + Add_name(rv, Bool_.N, 8249, "‹"); + Add_name(rv, Bool_.N, 8216, "‘"); + Add_name(rv, Bool_.N, 60, "<"); + Add_name(rv, Bool_.N, 175, "¯"); + Add_name(rv, Bool_.N, 8212, "—"); + Add_name(rv, Bool_.N, 181, "µ"); + Add_name(rv, Bool_.N, 183, "·"); + Add_name(rv, Bool_.N, 8722, "−"); + Add_name(rv, Bool_.N, 924, "Μ"); + Add_name(rv, Bool_.N, 956, "μ"); + Add_name(rv, Bool_.N, 8711, "∇"); + Add_name(rv, Bool_.N, 160, " "); + Add_name(rv, Bool_.N, 8211, "–"); + Add_name(rv, Bool_.N, 8800, "≠"); + Add_name(rv, Bool_.N, 8715, "∋"); + Add_name(rv, Bool_.N, 172, "¬"); + Add_name(rv, Bool_.N, 8713, "∉"); + Add_name(rv, Bool_.N, 8836, "⊄"); + Add_name(rv, Bool_.N, 209, "Ñ"); + Add_name(rv, Bool_.N, 241, "ñ"); + Add_name(rv, Bool_.N, 925, "Ν"); + Add_name(rv, Bool_.N, 957, "ν"); + Add_name(rv, Bool_.N, 211, "Ó"); + Add_name(rv, Bool_.N, 243, "ó"); + Add_name(rv, Bool_.N, 212, "Ô"); + Add_name(rv, Bool_.N, 244, "ô"); + Add_name(rv, Bool_.N, 338, "Œ"); + Add_name(rv, Bool_.N, 339, "œ"); + Add_name(rv, Bool_.N, 210, "Ò"); + Add_name(rv, Bool_.N, 242, "ò"); + Add_name(rv, Bool_.N, 8254, "‾"); + Add_name(rv, Bool_.N, 937, "Ω"); + Add_name(rv, Bool_.N, 969, "ω"); + Add_name(rv, Bool_.N, 927, "Ο"); + Add_name(rv, Bool_.N, 959, "ο"); + Add_name(rv, Bool_.N, 8853, "⊕"); + Add_name(rv, Bool_.N, 8744, "∨"); + Add_name(rv, Bool_.N, 170, "ª"); + Add_name(rv, Bool_.N, 186, "º"); + Add_name(rv, Bool_.N, 216, "Ø"); + Add_name(rv, Bool_.N, 248, "ø"); + Add_name(rv, Bool_.N, 213, "Õ"); + Add_name(rv, Bool_.N, 245, "õ"); + Add_name(rv, Bool_.N, 8855, "⊗"); + Add_name(rv, Bool_.N, 214, "Ö"); + Add_name(rv, Bool_.N, 246, "ö"); + Add_name(rv, Bool_.N, 182, "¶"); + Add_name(rv, Bool_.N, 8706, "∂"); + Add_name(rv, Bool_.N, 8240, "‰"); + Add_name(rv, Bool_.N, 8869, "⊥"); + Add_name(rv, Bool_.N, 934, "Φ"); + Add_name(rv, Bool_.N, 966, "φ"); + Add_name(rv, Bool_.N, 928, "Π"); + Add_name(rv, Bool_.N, 960, "π"); + Add_name(rv, Bool_.N, 982, "ϖ"); + Add_name(rv, Bool_.N, 177, "±"); + Add_name(rv, Bool_.N, 163, "£"); + Add_name(rv, Bool_.N, 8242, "′"); + Add_name(rv, Bool_.N, 8243, "″"); + Add_name(rv, Bool_.N, 8719, "∏"); + Add_name(rv, Bool_.N, 8733, "∝"); + Add_name(rv, Bool_.N, 936, "Ψ"); + Add_name(rv, Bool_.N, 968, "ψ"); + Add_name(rv, Bool_.N, 34, """); + Add_name(rv, Bool_.N, 8730, "√"); + Add_name(rv, Bool_.N, 9002, "⟩"); + Add_name(rv, Bool_.N, 187, "»"); + Add_name(rv, Bool_.N, 8594, "→"); + Add_name(rv, Bool_.N, 8658, "⇒"); + Add_name(rv, Bool_.N, 8969, "⌉"); + Add_name(rv, Bool_.N, 8221, "”"); + Add_name(rv, Bool_.N, 8476, "ℜ"); + Add_name(rv, Bool_.N, 174, "®"); + Add_name(rv, Bool_.N, 8971, "⌋"); + Add_name(rv, Bool_.N, 929, "Ρ"); + Add_name(rv, Bool_.N, 961, "ρ"); + Add_name(rv, Bool_.N, 8207, "‏"); + Add_name(rv, Bool_.N, 8250, "›"); + Add_name(rv, Bool_.N, 8217, "’"); + Add_name(rv, Bool_.N, 8218, "‚"); + Add_name(rv, Bool_.N, 352, "Š"); + Add_name(rv, Bool_.N, 353, "š"); + Add_name(rv, Bool_.N, 8901, "⋅"); + Add_name(rv, Bool_.N, 167, "§"); + Add_name(rv, Bool_.N, 173, "­"); + Add_name(rv, Bool_.N, 931, "Σ"); + Add_name(rv, Bool_.N, 963, "σ"); + Add_name(rv, Bool_.N, 962, "ς"); + Add_name(rv, Bool_.N, 8764, "∼"); + Add_name(rv, Bool_.N, 9824, "♠"); + Add_name(rv, Bool_.N, 8834, "⊂"); + Add_name(rv, Bool_.N, 8838, "⊆"); + Add_name(rv, Bool_.N, 8721, "∑"); + Add_name(rv, Bool_.N, 8835, "⊃"); + Add_name(rv, Bool_.N, 185, "¹"); + Add_name(rv, Bool_.N, 178, "²"); + Add_name(rv, Bool_.N, 179, "³"); + Add_name(rv, Bool_.N, 8839, "⊇"); + Add_name(rv, Bool_.N, 223, "ß"); + Add_name(rv, Bool_.N, 932, "Τ"); + Add_name(rv, Bool_.N, 964, "τ"); + Add_name(rv, Bool_.N, 8756, "∴"); + Add_name(rv, Bool_.N, 920, "Θ"); + Add_name(rv, Bool_.N, 952, "θ"); + Add_name(rv, Bool_.N, 977, "ϑ"); + Add_name(rv, Bool_.N, 8201, " "); + Add_name(rv, Bool_.N, 222, "Þ"); + Add_name(rv, Bool_.N, 254, "þ"); + Add_name(rv, Bool_.N, 732, "˜"); + Add_name(rv, Bool_.N, 215, "×"); + Add_name(rv, Bool_.N, 8482, "™"); + Add_name(rv, Bool_.N, 218, "Ú"); + Add_name(rv, Bool_.N, 250, "ú"); + Add_name(rv, Bool_.N, 8593, "↑"); + Add_name(rv, Bool_.N, 8657, "⇑"); + Add_name(rv, Bool_.N, 219, "Û"); + Add_name(rv, Bool_.N, 251, "û"); + Add_name(rv, Bool_.N, 217, "Ù"); + Add_name(rv, Bool_.N, 249, "ù"); + Add_name(rv, Bool_.N, 168, "¨"); + Add_name(rv, Bool_.N, 978, "ϒ"); + Add_name(rv, Bool_.N, 933, "Υ"); + Add_name(rv, Bool_.N, 965, "υ"); + Add_name(rv, Bool_.N, 220, "Ü"); + Add_name(rv, Bool_.N, 252, "ü"); + Add_name(rv, Bool_.N, 8472, "℘"); + Add_name(rv, Bool_.N, 926, "Ξ"); + Add_name(rv, Bool_.N, 958, "ξ"); + Add_name(rv, Bool_.N, 221, "Ý"); + Add_name(rv, Bool_.N, 253, "ý"); + Add_name(rv, Bool_.N, 165, "¥"); + Add_name(rv, Bool_.N, 376, "Ÿ"); + Add_name(rv, Bool_.N, 255, "ÿ"); + Add_name(rv, Bool_.N, 918, "Ζ"); + Add_name(rv, Bool_.N, 950, "ζ"); + Add_name(rv, Bool_.N, 8205, "‍"); + Add_name(rv, Bool_.N, 8204, "‌"); + Add_prefix(rv, Gfh_entity_itm.Tid_num_hex, "#x"); + Add_prefix(rv, Gfh_entity_itm.Tid_num_hex, "#X"); + Add_prefix(rv, Gfh_entity_itm.Tid_num_dec, "#"); + return rv; + } + private static void Add_name(Btrie_slim_mgr trie, boolean tid_is_xowa, int char_int, String xml_name_str) { + byte itm_tid = tid_is_xowa ? Gfh_entity_itm.Tid_name_xowa : Gfh_entity_itm.Tid_name_std; + byte[] xml_name_bry = Bry_.new_a7(xml_name_str); + byte[] key = Bry_.Mid(xml_name_bry, 1, xml_name_bry.length); // ignore & for purpose of trie; EX: "amp;"; NOTE: must keep trailing ";" else "& " will be valid; + trie.Add_obj(key, new Gfh_entity_itm(itm_tid, char_int, xml_name_bry)); + } + private static void Add_prefix(Btrie_slim_mgr trie, byte prefix_type, String prefix) { + byte[] prefix_ary = Bry_.new_u8(prefix); + Gfh_entity_itm itm = new Gfh_entity_itm(prefix_type, Gfh_entity_itm.Char_int_null, prefix_ary); + trie.Add_obj(prefix_ary, itm); + } +} diff --git a/100_core/src/gplx/langs/regxs/Regx_adp.java b/100_core/src/gplx/langs/regxs/Regx_adp.java new file mode 100644 index 000000000..5d8bfd335 --- /dev/null +++ b/100_core/src/gplx/langs/regxs/Regx_adp.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.regxs; import gplx.*; import gplx.langs.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +public class Regx_adp { + @gplx.Internal protected Regx_adp(String regx) {Pattern_(regx);} + public String Pattern() {return pattern;} public Regx_adp Pattern_(String val) {pattern = val; Under_sync(); return this;} private String pattern; + public boolean Pattern_is_invalid() {return pattern_is_invalid;} private boolean pattern_is_invalid = false; + public Regx_match[] Match_all(String text, int bgn) { + int idx = bgn; + List_adp rv = List_adp_.New(); + int len = String_.Len(text); + while (idx <= len) { // NOTE: must be <= not < else "a?" will return null instead of ""; PAGE:en.d:æ°‘; DATE:2015-01-30 + Regx_match match = this.Match(text, idx); + if (match.Rslt_none()) break; + rv.Add(match); + int find_bgn = match.Find_bgn(); + int find_len = match.Find_len(); + idx = find_len == 0 // find_bgn == find_end + ? find_bgn + 1 // add 1 to resume search from next char; DATE:2014-09-02 + : find_bgn + find_len // otherwise search after find_end + ; + } + return (Regx_match[])rv.To_ary(Regx_match.class); + } + private Pattern under; + void Under_sync() { + try {under = Pattern.compile(pattern, Pattern.DOTALL | Pattern.UNICODE_CHARACTER_CLASS);} // JRE.7:UNICODE_CHARACTER_CLASS; added during %w fix for en.w:A#; DATE:2015-06-10 + catch (Exception e) { // NOTE: if invalid, then default to empty pattern (which should return nothing); EX:d:〆る generates [^]; DATE:2013-10-20 + pattern_is_invalid = true; + under = Pattern.compile("", Pattern.DOTALL | Pattern.UNICODE_CHARACTER_CLASS); + } + } + public Regx_match Match(String input, int bgn) { + Matcher match = under.matcher(input); + boolean success = match.find(bgn); + int match_bgn = success ? match.start() : String_.Find_none; + int match_end = success ? match.end() : String_.Find_none; + Regx_group[] ary = Regx_group.Ary_empty; + int groups_len = match.groupCount(); + if (success && groups_len > 0) { + ary = new Regx_group[groups_len]; + for (int i = 0; i < groups_len; i++) + ary[i] = new Regx_group(true, match.start(i + 1), match.end(i + 1), match.group(i + 1)); + } + return new Regx_match(success, match_bgn, match_end, ary); + } + public String ReplaceAll(String input, String replace) {return under.matcher(input).replaceAll(replace);} + } diff --git a/100_core/src/gplx/langs/regxs/Regx_adp_.java b/100_core/src/gplx/langs/regxs/Regx_adp_.java new file mode 100644 index 000000000..9890621e9 --- /dev/null +++ b/100_core/src/gplx/langs/regxs/Regx_adp_.java @@ -0,0 +1,43 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.regxs; import gplx.*; import gplx.langs.*; +public class Regx_adp_ { + public static Regx_adp new_(String pattern) {return new Regx_adp(pattern);} + public static List_adp Find_all(String input, String find) { + Regx_adp regx = Regx_adp_.new_(find); + int idx = 0; + List_adp rv = List_adp_.New(); + while (true) { + Regx_match match = regx.Match(input, idx); + if (match.Rslt_none()) break; + rv.Add(match); + int findBgn = match.Find_bgn(); + idx = findBgn + match.Find_len(); + if (idx > String_.Len(input)) break; + } + return rv; + } + public static String Replace(String raw, String regx_str, String replace) { + Regx_adp regx = Regx_adp_.new_(regx_str); + return regx.ReplaceAll(raw, replace); + } + public static boolean Match(String input, String pattern) { + Regx_adp rv = new Regx_adp(pattern); + return rv.Match(input, 0).Rslt(); + } +} diff --git a/100_core/src/gplx/langs/regxs/Regx_adp__tst.java b/100_core/src/gplx/langs/regxs/Regx_adp__tst.java new file mode 100644 index 000000000..431565b4e --- /dev/null +++ b/100_core/src/gplx/langs/regxs/Regx_adp__tst.java @@ -0,0 +1,93 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.regxs; import gplx.*; import gplx.langs.*; +import org.junit.*; import gplx.core.tests.*; +public class Regx_adp__tst implements TfdsEqListItmStr { + @Test public void Match() { + tst_Match("a", "a", true); // basic + tst_Match("a", "b", false); // matchNot + tst_Match("a", "ab", true); // matchPart + tst_Match("a\\+b", "a+b", true); // matchEscape + tst_Match("[^a]", "b", true); // charSet_negate + } void tst_Match(String find, String input, boolean expd) {Tfds.Eq(expd, Regx_adp_.Match(input, find));} + @Test public void Match_all() { + tst_Match_all("#REDIRECT [[Template:Error]]", "^\\p{Nd}*", 1); // handle match = true but len = 0; DATE:2013-04-11 + tst_Match_all("a", "$", 1); // $ should match once, not zero; DATE:2014-09-02 + } void tst_Match_all(String input, String regx, int expd) {Tfds.Eq(expd, Regx_adp_.new_(regx).Match_all(input, 0).length);} + @Test public void Replace() { + tst_Replace("ab", "a", "b", "bb"); // basic + tst_Replace("ab", "c", "b", "ab"); // replaceNot + tst_Replace("aba", "a", "b", "bbb"); // replaceMultiple + } void tst_Replace(String input, String find, String replace, String expd) {Tfds.Eq(expd, Regx_adp_.Replace(input, find, replace));} + @Test public void Match_WholeWord() { + tst_WholeWord("a", "ab a", true); // pass a + tst_WholeWord("a", "ab c", false); // fail ab + tst_WholeWord("a", "a_", false); // fail a_ + tst_WholeWord("[a]", "a [a] c", true); // pass [a] + tst_WholeWord("[a]", "a[a]c", false); // fail a[a]c + } void tst_WholeWord(String regx, String text, boolean expd) {Tfds.Eq(expd, Regx_adp_.Match(text, Regx_bldr.WholeWord(regx)));} + @Test public void Match_As() { + tst_Regx("public static [A-Za-z0-9_]+ as_\\(Object obj\\)", "public static Obj1 as_(Object obj) {return obj instanceof Obj1 ? (Obj1)obj : null;}", true); + tst_Regx("public static [A-Za-z0-9_]+ as_\\(Object obj\\)", "public static boolean Asterisk(Object obj) {}", false); + } void tst_Regx(String regx, String text, boolean expd) {Tfds.Eq(expd, Regx_adp_.Match(text, regx));} + @Test public void Find() { + tst_Matches("b", "a b c b a", match_(2, 1), match_(6, 1)); + tst_Matches("d", "a b c b a"); + tst_Matches("b", "a b c b a b b", matches_(2, 6, 10, 12)); // BUGFIX: multiple entries did not work b/c of += instead of + + } + @Test public void Groups() { + tst_Groups("abc def ghi dz", "(d\\p{L}+)", "def", "dz"); + } + Regx_match[] matches_(int... bgnAry) { + int aryLen = Array_.Len(bgnAry); + Regx_match[] rv = new Regx_match[aryLen]; + for (int i = 0; i < aryLen; i++) + rv[i] = match_(bgnAry[i]); + return rv; + } + Regx_match match_(int bgn) {return match_(bgn, Int_.Min_value);} + Regx_match match_(int bgn, int len) {return new Regx_match(true, bgn, bgn + len, Regx_group.Ary_empty);} + void tst_Matches(String find, String input, Regx_match... expd) { + List_adp expdList = Array_.To_list(expd); + List_adp actlList = Regx_adp_.Find_all(input, find); + Tfds.Eq_list(expdList, actlList, this); + } + void tst_Groups(String text, String regx, String... expd) { + Regx_adp regx_mgr = Regx_adp_.new_(regx); + Regx_match[] rslts = regx_mgr.Match_all(text, 0); + Tfds.Eq_ary_str(expd, To_ary(rslts)); + } + String[] To_ary(Regx_match[] ary) { + List_adp rv = List_adp_.New(); + int len = ary.length; + for (int i = 0; i < len; i++) { + Regx_match itm = ary[i]; + int cap_len = itm.Groups().length; + for (int j = 0; j < cap_len; j++) { + rv.Add(itm.Groups()[j].Val()); + } + } + return rv.To_str_ary(); + } + public String To_str(Object curObj, Object expdObj) { + Regx_match cur = (Regx_match)curObj, expd = (Regx_match)expdObj; + String rv = "bgn=" + cur.Find_bgn(); + if (expd != null && expd.Find_len() != Int_.Min_value) rv += " len=" + cur.Find_len(); + return rv; + } +} diff --git a/100_core/src/gplx/langs/regxs/Regx_bldr.java b/100_core/src/gplx/langs/regxs/Regx_bldr.java new file mode 100644 index 000000000..5328c9d19 --- /dev/null +++ b/100_core/src/gplx/langs/regxs/Regx_bldr.java @@ -0,0 +1,62 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.regxs; import gplx.*; import gplx.langs.*; +import gplx.core.strings.*; +public class Regx_bldr { + public static String Includes(String characters) {return String_.Concat_any(Regx_bldr.Tkn_CharSetBegin, characters, Regx_bldr.Tkn_CharSetEnd);} + public static String Excludes(String characters) {return String_.Concat_any(Regx_bldr.Tkn_CharSetBegin, Regx_bldr.Tkn_Not, characters, Regx_bldr.Tkn_CharSetEnd);} + public static String WholeWord(String word) {return String_.Concat_any("(?. +*/ +package gplx.langs.regxs; import gplx.*; import gplx.langs.*; +public class Regx_group { + public Regx_group(boolean rslt, int bgn, int end, String val) {this.rslt = rslt; this.bgn = bgn; this.end = end; this.val = val;} + public boolean Rslt() {return rslt;} private boolean rslt; + public int Bgn() {return bgn;} int bgn; + public int End() {return end;} int end; + public String Val() {return val;} private String val; + public static final Regx_group[] Ary_empty = new Regx_group[0]; +} diff --git a/100_core/src/gplx/langs/regxs/Regx_match.java b/100_core/src/gplx/langs/regxs/Regx_match.java new file mode 100644 index 000000000..34617151f --- /dev/null +++ b/100_core/src/gplx/langs/regxs/Regx_match.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.regxs; import gplx.*; import gplx.langs.*; +public class Regx_match { + public Regx_match(boolean rslt, int find_bgn, int find_end, Regx_group[] groups) {this.rslt = rslt; this.find_bgn = find_bgn; this.find_end = find_end; this.groups = groups;} + public boolean Rslt() {return rslt;} private boolean rslt; + public boolean Rslt_none() {return !rslt;} // NOTE: was "|| find_end - find_bgn == 0"; DATE:2013-04-11; DATE:2014-09-02 + public int Find_bgn() {return find_bgn;} int find_bgn; + public int Find_end() {return find_end;} int find_end; + public int Find_len() {return find_end - find_bgn;} + public Regx_group[] Groups() {return groups;} Regx_group[] groups = Regx_group.Ary_empty; + public static final Regx_match[] Ary_empty = new Regx_match[0]; +} diff --git a/100_core/src/gplx/langs/xmls/HierStrBldr.java b/100_core/src/gplx/langs/xmls/HierStrBldr.java new file mode 100644 index 000000000..54ce53ae8 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/HierStrBldr.java @@ -0,0 +1,56 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import gplx.core.strings.*; import gplx.core.envs.*; +public class HierStrBldr { + public String Root() {return root;} public HierStrBldr Root_(String v) {root = v; return this;} private String root; + public Io_url RootAsIoUrl() {return Io_url_.new_dir_(root);} + public String DirFmt() {return dirFmt;} private String dirFmt; + public String DirSpr() {return dirSpr;} private String dirSpr = Op_sys.Cur().Fsys_dir_spr_str(); + public String FilFmt() {return filFmt;} private String filFmt; + public String NumFmt() {return numFmt;} private String numFmt; + public int[] FilCountMaxs() {return filCountMaxs;} int[] filCountMaxs; + public Io_url GenStrIdxOnlyAsoUrl(int idx) {return Io_url_.new_fil_(GenStrIdxOnly(idx));} + public String GenStrIdxOnly(int idx) {return GenStr(String_.Ary_empty, idx);} + public Io_url GenStrAsIoUrl(String[] subDirs, int idx) { + return Io_url_.new_fil_(GenStr(subDirs, idx)); + } + String GenStr(String[] subDirs, int idx) { + String_bldr sb = String_bldr_.new_(); + sb.Add(root); + for (String subDir : subDirs) + sb.Add(subDir).Add(dirSpr); + int multiple = 1; + int[] multipleAry = new int[filCountMaxs.length]; + for (int i = filCountMaxs.length - 1; i >= 0; i--) { + multiple *= filCountMaxs[i]; + multipleAry[i] = (idx / multiple) * multiple; // NOTE: rounds down to multiple; EX: 11 -> 10 + } + for (int i = 0; i < multipleAry.length; i++) + sb.Add_fmt(dirFmt, Int_.To_str_fmt(multipleAry[i], numFmt)); + sb.Add_fmt(filFmt, Int_.To_str_fmt(idx, numFmt)); + return sb.To_str(); + } + public HierStrBldr Ctor_io(Io_url root, String dirFmt, String filFmt, String numFmt, int... filCountMaxs) { + this.Ctor(root.Raw(), dirFmt + dirSpr, filFmt, numFmt, filCountMaxs); + return this; + } + public void Ctor(String root, String dirFmt, String filFmt, String numFmt, int... filCountMaxs) { + this.root = root; this.dirFmt = dirFmt; this.filFmt = filFmt; this.numFmt = numFmt; this.filCountMaxs = filCountMaxs; + } +} diff --git a/100_core/src/gplx/langs/xmls/HierStrBldr_tst.java b/100_core/src/gplx/langs/xmls/HierStrBldr_tst.java new file mode 100644 index 000000000..1a5c2aeba --- /dev/null +++ b/100_core/src/gplx/langs/xmls/HierStrBldr_tst.java @@ -0,0 +1,47 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.junit.*; +import gplx.core.ios.*; import gplx.core.texts.*; +public class HierStrBldr_tst { + @Before public void setup() {bldr = new HierStrBldr();} HierStrBldr bldr; + @Test public void Hier0() { + bldr.Ctor("/root/", "dir_{0}/", "idx_{0}.csv", "000"); + tst_MakeName( 0, "/root/idx_000.csv"); + tst_MakeName( 1, "/root/idx_001.csv"); + tst_MakeName(10, "/root/idx_010.csv"); + } + @Test public void Hier1() { + bldr.Ctor("/root/", "dir_{0}/", "idx_{0}.csv", "000", 10); + tst_MakeName( 0, "/root/dir_000/idx_000.csv"); + tst_MakeName( 1, "/root/dir_000/idx_001.csv"); + tst_MakeName(10, "/root/dir_010/idx_010.csv"); + } + @Test public void Hier2() { + bldr.Ctor("/root/", "dir_{0}/", "idx_{0}.csv", "000", 5, 10); + tst_MakeName( 0, "/root/dir_000/dir_000/idx_000.csv"); + tst_MakeName( 1, "/root/dir_000/dir_000/idx_001.csv"); + tst_MakeName( 10, "/root/dir_000/dir_010/idx_010.csv"); + tst_MakeName( 49, "/root/dir_000/dir_040/idx_049.csv"); + tst_MakeName( 50, "/root/dir_050/dir_050/idx_050.csv"); + tst_MakeName( 99, "/root/dir_050/dir_090/idx_099.csv"); + tst_MakeName(100, "/root/dir_100/dir_100/idx_100.csv"); + tst_MakeName(110, "/root/dir_100/dir_110/idx_110.csv"); + } + void tst_MakeName(int val, String expd) {Tfds.Eq(expd, bldr.GenStrIdxOnly(val));} +} diff --git a/100_core/src/gplx/langs/xmls/XmlAtr.java b/100_core/src/gplx/langs/xmls/XmlAtr.java new file mode 100644 index 000000000..9128adc0b --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlAtr.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.w3c.dom.Node; +public class XmlAtr { + public String Name() {return xatr.getNodeName();} + public String Value() {return xatr.getNodeValue();} + @gplx.Internal protected XmlAtr(Node xatr) {this.xatr = xatr;} Node xatr; +} diff --git a/100_core/src/gplx/langs/xmls/XmlAtrList.java b/100_core/src/gplx/langs/xmls/XmlAtrList.java new file mode 100644 index 000000000..456f52012 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlAtrList.java @@ -0,0 +1,37 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +public class XmlAtrList { + public int Count() {return list == null ? 0 : list.getLength();} + public String FetchValOr(String key, String or) { + Node xatr = list.getNamedItem(key); + return (xatr == null) ? or : xatr.getNodeValue(); + } + public XmlAtr Fetch(String key) { + Node xatr = list.getNamedItem(key); if (xatr == null) throw Err_.new_missing_key(key); + return new XmlAtr(xatr); + } + public XmlAtr Fetch_or_null(String key) { + Node xatr = list.getNamedItem(key); if (xatr == null) return null; + return new XmlAtr(xatr); + } + public XmlAtr Get_at(int i) {return list == null ? null : new XmlAtr(list.item(i));} + @gplx.Internal protected XmlAtrList(NamedNodeMap list) {this.list = list;} NamedNodeMap list; +} diff --git a/100_core/src/gplx/langs/xmls/XmlDoc.java b/100_core/src/gplx/langs/xmls/XmlDoc.java new file mode 100644 index 000000000..f256f8521 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlDoc.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.w3c.dom.Document; +public class XmlDoc { + public XmlNde Root() {return new XmlNde(xdoc.getDocumentElement());} + @gplx.Internal protected XmlDoc(Document xdoc) {this.xdoc = xdoc;} Document xdoc; +} +//#} \ No newline at end of file diff --git a/100_core/src/gplx/langs/xmls/XmlDoc_.java b/100_core/src/gplx/langs/xmls/XmlDoc_.java new file mode 100644 index 000000000..4bfc065b9 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlDoc_.java @@ -0,0 +1,53 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import gplx.Io_url; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +public class XmlDoc_ { + public static XmlDoc parse(String raw) {return new XmlDoc(doc_(raw));} + static Document doc_(String raw) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder bldr = null; + try {bldr = factory.newDocumentBuilder();} + catch (ParserConfigurationException e) {throw Err_.new_exc(e, "xml", "failed to create newDocumentBuilder");} + StringReader reader = new StringReader(raw); + InputSource source = new InputSource(reader); + Document doc = null; + try {doc = bldr.parse(source);} + catch (SAXException e) {throw Err_.new_exc(e, "xml", "failed to parse xml", "raw", raw);} + catch (IOException e) {throw Err_.new_exc(e, "xml", "failed to parse xml", "raw", raw);} + return doc; + } + public static final String Err_XmlException = "gplx.xmls.XmlException"; +} +//#} \ No newline at end of file diff --git a/100_core/src/gplx/langs/xmls/XmlDoc_tst.java b/100_core/src/gplx/langs/xmls/XmlDoc_tst.java new file mode 100644 index 000000000..19c6c421b --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlDoc_tst.java @@ -0,0 +1,71 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.junit.*; +public class XmlDoc_tst { + String xml; XmlDoc xdoc; XmlNde xnde; + @Test public void parse() { + xml = String_.Concat(""); + xdoc = XmlDoc_.parse(xml); + Tfds.Eq("root", xdoc.Root().Name()); + Tfds.Eq(true, xdoc.Root().NdeType_element()); + } + @Test public void Xml_outer() { + xml = String_.Concat + ( "" + , "" + , "" + , "" + , "" + , "" + ); + xdoc = XmlDoc_.parse(xml); + xnde = xdoc.Root().SubNdes().Get_at(0); + Tfds.Eq("a", xnde.Name()); + Tfds.Eq("", xnde.Xml_outer()); + } + @Test public void Text_inner() { + xml = String_.Concat + ( "" + , "" + , "test me" + , "" + , "" + ); + xdoc = XmlDoc_.parse(xml); + xnde = xdoc.Root().SubNdes().Get_at(0); + Tfds.Eq("a", xnde.Name()); + Tfds.Eq("test me", xnde.Text_inner()); + } + @Test public void Atrs() { + xml = String_.Concat + ( "" + , "" + ); + xdoc = XmlDoc_.parse(xml); + XmlAtrList atrs = xdoc.Root().Atrs(); + XmlAtr atr = atrs.Get_at(1); + tst_Atr(atr, "atr1", "1"); + atr = atrs.Get_at(1); + tst_Atr(atr, "atr1", "1"); + } + void tst_Atr(XmlAtr atr, String expdName, String expdVal) { + Tfds.Eq(expdName, atr.Name()); + Tfds.Eq(expdVal, atr.Value()); + } +} diff --git a/100_core/src/gplx/langs/xmls/XmlFileSplitter.java b/100_core/src/gplx/langs/xmls/XmlFileSplitter.java new file mode 100644 index 000000000..cbf92b483 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlFileSplitter.java @@ -0,0 +1,141 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import gplx.core.consoles.*; +import gplx.core.ios.*; +import gplx.core.texts.*; +public class XmlFileSplitter { + public XmlFileSplitterOpts Opts() {return opts;} XmlFileSplitterOpts opts = new XmlFileSplitterOpts(); + public byte[] Hdr() {return hdr;} private byte[] hdr; + public void Clear() {hdr = null;} + public void Split(Io_url xmlUrl) { + Io_url partDir = opts.PartDir(); + byte[] xmlEndTagAry = Bry_.new_u8(opts.XmlEnd()); + byte[][] nameAry = XtoByteAry(opts.XmlNames()); + int partIdx = 0; + + // bgn reading file + XmlSplitRdr rdr = new XmlSplitRdr().Init_(xmlUrl, opts.FileSizeMax()); + + // split hdr: includes , xmlNamespaces, and any DTD headers; will be prepended to each partFile + rdr.Read(); + int findPos = FindMatchPos(rdr.CurAry(), nameAry); if (findPos == String_.Find_none) throw Err_.new_wo_type("could not find any names in first segment"); + byte[] dataAry = SplitHdr(rdr.CurAry(), findPos); + if (opts.XmlBgn() != null) + hdr = Bry_.new_u8(opts.XmlBgn()); + byte[] tempAry = new byte[0]; + int newFindPos = FindMatchPosRev(dataAry, nameAry); + findPos = (newFindPos <= findPos) ? String_.Find_none : newFindPos; + boolean first = true; + + // split files + XmlSplitWtr partWtr = new XmlSplitWtr().Init_(partDir, hdr, opts); + while (true) { + partWtr.Bgn(partIdx++); + if (opts.StatusFmt() != null) Console_adp__sys.Instance.Write_str_w_nl(String_.Format(opts.StatusFmt(), partWtr.Url().NameOnly())); + partWtr.Write(tempAry); + if (!first) { + rdr.Read(); + dataAry = rdr.CurAry(); + findPos = FindMatchPosRev(dataAry, nameAry); + } + else + first = false; + + // find last closing node + while (findPos == String_.Find_none) { + if (rdr.Done()) { + findPos = rdr.CurRead(); + break; + } + else { + partWtr.Write(dataAry); + rdr.Read(); + dataAry = rdr.CurAry(); + findPos = FindMatchPosRev(dataAry, nameAry); + } + } + + byte[][] rv = SplitRest(dataAry, findPos); + partWtr.Write(rv[0]); + tempAry = rv[1]; + boolean done = rdr.Done() && tempAry.length == 0; + if (!done) + partWtr.Write(xmlEndTagAry); + partWtr.Rls(); + if (done) break; + } + rdr.Rls(); + } + public byte[] SplitHdr(byte[] src, int findPos) { + hdr = new byte[findPos]; + Array_.Copy_to(src, 0, hdr, 0, findPos); + byte[] rv = new byte[src.length - findPos]; + Array_.Copy_to(src, findPos, rv, 0, rv.length); + return rv; + } + public byte[][] SplitRest(byte[] src, int findPos) { + byte[][] rv = new byte[2][]; + rv[0] = new byte[findPos]; + Array_.Copy_to(src, 0, rv[0], 0, findPos); + rv[1] = new byte[src.length - findPos]; + Array_.Copy_to(src, findPos, rv[1], 0, rv[1].length); + return rv; + } + public int FindMatchPos(byte[] src, byte[][] wordAry) {return FindMatchPos(src, wordAry, true);} + public int FindMatchPosRev(byte[] src, byte[][] wordAry) {return FindMatchPos(src, wordAry, false);} + int FindMatchPos(byte[] src, byte[][] wordAry, boolean fwd) { + int[] findAry = new int[wordAry.length]; + for (int i = 0; i < findAry.length; i++) + findAry[i] = fwd ? -1 : Int_.Max_value; + for (int i = 0; i < wordAry.length; i++) { // look at each word in wordAry + int srcLen = src.length, srcPos, srcEnd, srcDif; + if (fwd) {srcPos = 0; srcEnd = srcLen; srcDif = 1;} + else {srcPos = srcLen - 1; srcEnd = -1; srcDif = -1;} + while (srcPos != srcEnd) { // look at each byte in src + byte[] ary = wordAry[i]; + int aryLen = ary.length, aryPos, aryEnd, aryDif; + if (fwd) {aryPos = 0; aryEnd = aryLen; aryDif = 1;} + else {aryPos = aryLen - 1; aryEnd = -1; aryDif = -1;} + boolean found = true; + while (aryPos != aryEnd) { // look at each byte in word + int lkpPos = srcPos + aryPos; + if (lkpPos >= srcLen) {found = false; break;} // outside bounds; exit + if (ary[aryPos] != src[lkpPos]) {found = false; break;} // srcByte doesn't match wordByte; exit + aryPos += aryDif; + } + if (found) {findAry[i] = srcPos; break;} // result found; stop now and keep "best" result + srcPos += srcDif; + } + } + int best = fwd ? -1 : Int_.Max_value; + for (int find : findAry) { + if ((fwd && find > best) + || (!fwd && find < best)) + best = find; + } + if (best == Int_.Max_value) best = -1; + return best; + } + byte[][] XtoByteAry(String[] names) { + byte[][] rv = new byte[names.length][]; + for (int i = 0; i < names.length; i++) + rv[i] = Bry_.new_u8(names[i]); + return rv; + } +} diff --git a/100_core/src/gplx/langs/xmls/XmlFileSplitterOpts.java b/100_core/src/gplx/langs/xmls/XmlFileSplitterOpts.java new file mode 100644 index 000000000..b29913c77 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlFileSplitterOpts.java @@ -0,0 +1,27 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +public class XmlFileSplitterOpts { + public int FileSizeMax() {return fileSizeMax;} public XmlFileSplitterOpts FileSizeMax_(int v) {fileSizeMax = v; return this;} int fileSizeMax = 1024 * 1024; + public String[] XmlNames() {return xmlNames;} public XmlFileSplitterOpts XmlNames_(String... v) {xmlNames = v; return this;} private String[] xmlNames; + public String XmlBgn() {return xmlBgn;} public XmlFileSplitterOpts XmlBgn_(String v) {xmlBgn = v; return this;} private String xmlBgn; + public String XmlEnd() {return xmlEnd;} public XmlFileSplitterOpts XmlEnd_(String v) {xmlEnd = v; return this;} private String xmlEnd; + public Io_url PartDir() {return partDir;} public XmlFileSplitterOpts PartDir_(Io_url v) {partDir = v; return this;} Io_url partDir; + public String StatusFmt() {return statusFmt;} public XmlFileSplitterOpts StatusFmt_(String v) {statusFmt = v; return this;} private String statusFmt = "splitting {0}"; + public HierStrBldr Namer() {return namer;} HierStrBldr namer = new HierStrBldr(); +} diff --git a/100_core/src/gplx/langs/xmls/XmlFileSplitter_tst.java b/100_core/src/gplx/langs/xmls/XmlFileSplitter_tst.java new file mode 100644 index 000000000..6c50bdf25 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlFileSplitter_tst.java @@ -0,0 +1,88 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.junit.*; +import gplx.core.ios.*; import gplx.core.texts.*; +public class XmlFileSplitter_tst { + @Before public void setup() { + splitter = new XmlFileSplitter(); + Io_mgr.Instance.InitEngine_mem(); + } XmlFileSplitter splitter; + @Test public void FindMatchPos() { + tst_FindMatchPos("abcde", "a", 0); + tst_FindMatchPos("abcde", "b", 1); + tst_FindMatchPos("abcde", "cd", 2); + tst_FindMatchPos("abcde", "f", -1); + tst_FindMatchPos("abcde", "fg", -1); + } void tst_FindMatchPos(String src, String find, int expd) {Tfds.Eq(expd, splitter.FindMatchPos(byte_(src), byteAry_(find)));} + @Test public void FindMatchPosRev() { + tst_FindMatchPosRev("abcde", "a", 0); + tst_FindMatchPosRev("abcde", "b", 1); + tst_FindMatchPosRev("abcde", "cd", 2); + tst_FindMatchPosRev("abcde", "f", -1); + tst_FindMatchPosRev("abcde", "ef", -1); + tst_FindMatchPosRev("abcde", "za", -1); + tst_FindMatchPosRev("dbcde", "d", 3); + } void tst_FindMatchPosRev(String src, String find, int expd) {Tfds.Eq(expd, splitter.FindMatchPosRev(byte_(src), byteAry_(find)));} + @Test public void ExtractHdr() { + tst_ExtractHdr("", "", ""); + } + @Test public void Split() { + splitter.Opts().FileSizeMax_(30).XmlNames_(""); + tst_Split + ( "" + , "" + , "" + , "" + ); + tst_Split + ( "" + , "" + , "" + ); + } + void tst_Split(String txt, String... expd) { + Io_url xmlFil = Io_url_.mem_fil_("mem/800_misc/txt.xml"); + Io_url tmpDir = xmlFil.OwnerDir().GenSubDir("temp_xml"); + Io_mgr.Instance.DeleteDirDeep(tmpDir); + splitter.Opts().StatusFmt_(null).PartDir_(tmpDir); + splitter.Opts().Namer().Ctor_io(tmpDir, "", "fil_{0}.xml", "000"); + Io_mgr.Instance.SaveFilStr(xmlFil, txt); + splitter.Split(xmlFil); + Io_url[] tmpFilAry = Io_mgr.Instance.QueryDir_fils(tmpDir); + Tfds.Eq(expd.length, tmpFilAry.length); + for (int i = 0; i < tmpFilAry.length; i++) { + Io_url tmpFil = tmpFilAry[i]; + Tfds.Eq(expd[i], Io_mgr.Instance.LoadFilStr(tmpFil)); + } + } + byte[] byte_(String s) {return Bry_.new_u8(s);} + byte[][] byteAry_(String s) { + byte[][] rv = new byte[1][]; + rv[0] = Bry_.new_u8(s); + return rv; + } + void tst_ExtractHdr(String src, String find, String expdHdr, String expdSrc) { + splitter.Clear(); + byte[] srcAry = byte_(src); + int findPos = splitter.FindMatchPos(srcAry, byteAry_(find)); + srcAry = splitter.SplitHdr(srcAry, findPos); + Tfds.Eq(String_.new_u8(splitter.Hdr()), expdHdr); + Tfds.Eq(String_.new_u8(srcAry), expdSrc); + } +} diff --git a/100_core/src/gplx/langs/xmls/XmlNde.java b/100_core/src/gplx/langs/xmls/XmlNde.java new file mode 100644 index 000000000..066750771 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlNde.java @@ -0,0 +1,51 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import java.io.StringWriter; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.Node; +public class XmlNde { + public XmlAtrList Atrs() {return new XmlAtrList(xnde.getAttributes());} + public XmlNdeList SubNdes() {return new XmlNdeList_cls_xml(xnde.getChildNodes());} + public String Name() {return xnde.getNodeName();} + public String Xml_outer() { + Transformer transformer = transformer_(); + StringWriter writer = new StringWriter(); + try {transformer.transform(new DOMSource(xnde), new StreamResult(writer));} + catch (TransformerException e) {throw Err_.new_exc(e, "xml", "failed to get xml string");} + return writer.toString(); + } + public String Text_inner() {return xnde.getTextContent();} + public boolean NdeType_element() {return xnde.getNodeType() == Node.ELEMENT_NODE;} + public boolean NdeType_textOrEntityReference() {return xnde.getNodeType() == Node.TEXT_NODE || xnde.getNodeType() == Node.ENTITY_REFERENCE_NODE;} + @gplx.Internal protected XmlNde(Node xnde) {this.xnde = xnde;} Node xnde; + static Transformer transformer_() { + TransformerFactory transformerfactory = TransformerFactory.newInstance(); + Transformer transformer = null; + try {transformer = transformerfactory.newTransformer();} + catch (TransformerConfigurationException e) {throw Err_.new_exc(e, "xml", "failed to get create transformer");} + transformer.setOutputProperty("omit-xml-declaration", "yes"); + return transformer; + } + } diff --git a/100_core/src/gplx/langs/xmls/XmlNdeList.java b/100_core/src/gplx/langs/xmls/XmlNdeList.java new file mode 100644 index 000000000..ec141de1b --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlNdeList.java @@ -0,0 +1,35 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.w3c.dom.NodeList; +public interface XmlNdeList { + int Count(); + XmlNde Get_at(int i); +} +class XmlNdeList_cls_xml implements XmlNdeList { + public int Count() {return list.getLength();} + public XmlNde Get_at(int i) {return new XmlNde(list.item(i));} + @gplx.Internal protected XmlNdeList_cls_xml(NodeList list) {this.list = list;} NodeList list; +} +class XmlNdeList_cls_list implements XmlNdeList { + public int Count() {return list.Count();} + public XmlNde Get_at(int i) {return (XmlNde)list.Get_at(i);} + public void Add(XmlNde xnde) {list.Add(xnde);} + @gplx.Internal protected XmlNdeList_cls_list(int count) {list = List_adp_.New(); list.Resize_bounds(count);} List_adp list; +} +//#} \ No newline at end of file diff --git a/100_core/src/gplx/langs/xmls/XmlSplitRdr.java b/100_core/src/gplx/langs/xmls/XmlSplitRdr.java new file mode 100644 index 000000000..4dbdd757e --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlSplitRdr.java @@ -0,0 +1,51 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import gplx.core.ios.streams.*; +public class XmlSplitRdr { + public byte[] CurAry() {return curAry;} private byte[] curAry; + public long CurSum() {return curSum;} long curSum; + public int CurRead() {return curRead;} int curRead; + public boolean Done() {return done;} private boolean done; + public XmlSplitRdr InitAll_(Io_url url) { + stream = Io_mgr.Instance.OpenStreamRead(url); + curLen = stream.Len(); + curAry = new byte[(int)curLen]; + curSum = 0; + curRead = 0; + done = false; + return this; + } + public XmlSplitRdr Init_(Io_url url, int curArySize) { + stream = Io_mgr.Instance.OpenStreamRead(url); + curLen = Io_mgr.Instance.QueryFil(url).Size(); + curAry = new byte[curArySize]; + curSum = 0; + curRead = 0; + done = false; + return this; + } IoStream stream; long curLen; + public void Read() { + curRead = stream.ReadAry(curAry); + curSum += curRead; + done = curSum == curLen; + if (done && curRead != curAry.length) // on last pass, readAry may have garbage at end, remove + curAry = Bry_.Resize(curAry, curRead); + } + public void Rls() {stream.Rls();} +} diff --git a/100_core/src/gplx/langs/xmls/XmlSplitWtr.java b/100_core/src/gplx/langs/xmls/XmlSplitWtr.java new file mode 100644 index 000000000..db2a2cf9c --- /dev/null +++ b/100_core/src/gplx/langs/xmls/XmlSplitWtr.java @@ -0,0 +1,40 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import gplx.core.ios.streams.*; +public class XmlSplitWtr { + public Io_url Url() {return url;} Io_url url; + public XmlSplitWtr Init_(Io_url partDir, byte[] hdr, XmlFileSplitterOpts opts) { + this.partDir = partDir; this.hdr = hdr; this.opts = opts; + return this; + } + public void Bgn(int partIdx) { + String partStr = opts.Namer().GenStrIdxOnly(partIdx); + url = Io_url_.mem_fil_(partStr); + stream = Io_mgr.Instance.OpenStreamWrite(url); + init = true; + } boolean init = true; byte[] hdr; XmlFileSplitterOpts opts; Io_url partDir; IoStream stream; + public void Write(byte[] ary) { + if (init) { + stream.WriteAry(hdr); + init = false; + } + stream.WriteAry(ary); + } + public void Rls() {stream.Rls();} +} diff --git a/100_core/src/gplx/langs/xmls/Xpath_.java b/100_core/src/gplx/langs/xmls/Xpath_.java new file mode 100644 index 000000000..8de63fcef --- /dev/null +++ b/100_core/src/gplx/langs/xmls/Xpath_.java @@ -0,0 +1,106 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import gplx.core.primitives.*; +public class Xpath_ { + public static XmlNdeList SelectAll(XmlNde owner, String xpath) {return Select(owner, xpath, Xpath_Args.all_());} + public static XmlNde SelectFirst(XmlNde owner, String xpath) { + XmlNdeList rv = Select(owner, xpath, Xpath_Args.first_()); + return rv.Count() == 0 ? null : rv.Get_at(0); // selects first + } + public static XmlNdeList SelectElements(XmlNde owner) { + XmlNdeList subNdes = owner.SubNdes(); int count = subNdes.Count(); + XmlNdeList_cls_list list = new XmlNdeList_cls_list(count); + for (int i = 0; i < count; i++) { + XmlNde sub = subNdes.Get_at(i); + if (sub.NdeType_element()) + list.Add(sub); + } + return list; + } + static XmlNdeList Select(XmlNde owner, String xpath, Xpath_Args args) { + XmlNdeList_cls_list rv = new XmlNdeList_cls_list(8); + String[] parts = String_.Split(xpath, "/"); + TraverseSubs(owner, parts, 0, rv, args); + return rv; + } + static void TraverseSubs(XmlNde owner, String[] parts, int depth, XmlNdeList_cls_list results, Xpath_Args args) { + int partsLen = Array_.Len(parts); + if (depth == partsLen) return; + String name = parts[depth]; + XmlNdeList subNdes = owner.SubNdes(); int count = subNdes.Count(); + for (int i = 0; i < count; i++) { + XmlNde sub = subNdes.Get_at(i); + if (args.Cancel) return; + if (!String_.Eq(name, sub.Name())) continue; + if (depth == partsLen - 1) { + results.Add(sub); + if (args.SelectFirst) args.Cancel = true; + } + else + TraverseSubs(sub, parts, depth + 1, results, args); + } + } + public static final String InnetTextKey = "&innerText"; + public static Keyval_hash ExtractKeyVals(String xml, Int_obj_ref posRef, String nodeName) { + int pos = posRef.Val(); + Err xmlErr = Err_.new_wo_type("error parsing xml", "xml", xml, "pos", pos); + String headBgnFind = "<" + nodeName + " "; int headBgnFindLen = String_.Len(headBgnFind); + int headBgn = String_.FindFwd(xml, headBgnFind, pos); if (headBgn == String_.Find_none) return null; + int headEnd = String_.FindFwd(xml, ">", headBgn + headBgnFindLen); if (headEnd == String_.Find_none) throw xmlErr; + String atrXml = String_.Mid(xml, headBgn, headEnd); + Keyval_hash rv = ExtractNodeVals(atrXml, xmlErr); + boolean noInnerText = String_.CharAt(xml, headEnd - 1) == '/'; // if />, then no inner text + if (!noInnerText) { + int tail = String_.FindFwd(xml, "", headBgn); if (tail == String_.Find_none) throw Err_.new_wo_type("could not find tailPos", "headBgn", headBgn); + String innerText = String_.Mid(xml, headEnd + 1, tail); + rv.Add(InnetTextKey, innerText); + } + posRef.Val_(headEnd); + return rv; + } + static Keyval_hash ExtractNodeVals(String xml, Err xmlErr) { + Keyval_hash rv = new Keyval_hash(); + int pos = 0; + while (true) { + int eqPos = String_.FindFwd(xml, "=", pos); if (eqPos == String_.Find_none) break; + int q0Pos = String_.FindFwd(xml, "\"", eqPos + 1); if (q0Pos == String_.Find_none) throw xmlErr.Args_add("eqPos", eqPos); + int q1Pos = String_.FindFwd(xml, "\"", q0Pos + 1); if (q1Pos == String_.Find_none) throw xmlErr.Args_add("q1Pos", q1Pos); + int spPos = eqPos - 1; + while (spPos > -1) { + char c = String_.CharAt(xml, spPos); + if (Char_.IsWhitespace(c)) break; + spPos--; + } + if (spPos == String_.Find_none) throw xmlErr.Args_add("sub_msg", "could not find hdr", "eqPos", eqPos); + String key = String_.Mid(xml, spPos + 1, eqPos); + String val = String_.Mid(xml, q0Pos + 1, q1Pos); + rv.Add(key, val); + pos = q1Pos; + } + return rv; + } +} +class Xpath_Args { + public boolean SelectFirst; // false=SelectAll + public boolean Cancel; + public static Xpath_Args all_() {return new Xpath_Args(false);} + public static Xpath_Args first_() {return new Xpath_Args(true);} + Xpath_Args(boolean selectFirst) {this.SelectFirst = selectFirst;} +} +enum Xpath_SelectMode {All, First} diff --git a/100_core/src/gplx/langs/xmls/Xpath__tst.java b/100_core/src/gplx/langs/xmls/Xpath__tst.java new file mode 100644 index 000000000..3a484d714 --- /dev/null +++ b/100_core/src/gplx/langs/xmls/Xpath__tst.java @@ -0,0 +1,44 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.langs.xmls; import gplx.*; import gplx.langs.*; +import org.junit.*; +public class Xpath__tst { + @Test public void Select_all() { + String xml = String_.Concat + ( "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + ); + tst_SelectAll(xml, "a", 2); + tst_SelectAll(xml, "b", 1); + tst_SelectAll(xml, "b/c", 3); + } + void tst_SelectAll(String raw, String xpath, int expdCount) { + XmlDoc xdoc = XmlDoc_.parse(raw); + XmlNdeList xndeList = Xpath_.SelectAll(xdoc.Root(), xpath); + Tfds.Eq(expdCount, xndeList.Count()); + } +} diff --git a/100_core/tst/gplx/GfoMsg_rdr_tst.java b/100_core/tst/gplx/GfoMsg_rdr_tst.java new file mode 100644 index 000000000..373564288 --- /dev/null +++ b/100_core/tst/gplx/GfoMsg_rdr_tst.java @@ -0,0 +1,57 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +import org.junit.*; +public class GfoMsg_rdr_tst { + @Before public void setup() { + msg = msg_().Add("a", "1").Add("b", "2").Add("c", "3"); + ctx.Match("init", "init"); + } GfoMsg msg; GfsCtx ctx = GfsCtx.new_(); + @Test public void Key() { + tst_Msg(msg, "a", "1"); + tst_Msg(msg, "b", "2"); + tst_Msg(msg, "c", "3"); + tst_Msg(msg, "d", null); + } + @Test public void Pos() { + msg = msg_().Add("", "1").Add("", "2").Add("", "3"); + tst_Msg(msg, "", "1"); + tst_Msg(msg, "", "2"); + tst_Msg(msg, "", "3"); + tst_Msg(msg, "", null); + } + @Test public void OutOfOrder() { + tst_Msg(msg, "c", "3"); + tst_Msg(msg, "b", "2"); + tst_Msg(msg, "a", "1"); + } + @Test public void Key3_Pos1_Pos2() { + msg = msg_().Add("", "1").Add("", "2").Add("c", "3"); + tst_Msg(msg, "c", "3"); + tst_Msg(msg, "", "1"); + tst_Msg(msg, "", "2"); + } + @Test public void MultipleEmpty() { + msg = msg_().Add("", "1").Add("", "2").Add("", "3"); + tst_Msg(msg, "", "1"); + tst_Msg(msg, "", "2"); + tst_Msg(msg, "", "3"); + } + GfoMsg msg_() {return GfoMsg_.new_parse_("test");} + void tst_Msg(GfoMsg m, String k, String expd) {Tfds.Eq(expd, m.ReadStrOr(k, null));} +} diff --git a/100_core/tst/gplx/GfoTreeBldr_fxt.java b/100_core/tst/gplx/GfoTreeBldr_fxt.java new file mode 100644 index 000000000..134952ac3 --- /dev/null +++ b/100_core/tst/gplx/GfoTreeBldr_fxt.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx; +public class GfoTreeBldr_fxt { + public List_adp Atrs() {return atrs;} List_adp atrs = List_adp_.New(); + public List_adp Subs() {return subs;} List_adp subs = List_adp_.New(); + public GfoTreeBldr_fxt atr_(Object key, Object val) { + atrs.Add(new Object[] {key, val}); + return this; + } + public GfoTreeBldr_fxt sub_(GfoTreeBldr_fxt... ary) { + for (GfoTreeBldr_fxt sub : ary) + subs.Add(sub); + return this; + } + public static GfoTreeBldr_fxt new_() {return new GfoTreeBldr_fxt();} GfoTreeBldr_fxt() {} +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_dir_basic_base.java b/100_core/tst/gplx/core/ios/IoEngine_dir_basic_base.java new file mode 100644 index 000000000..ba07b4c55 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_dir_basic_base.java @@ -0,0 +1,79 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public abstract class IoEngine_dir_basic_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + } protected IoEngine engine; @gplx.Internal protected IoEngineFxt fx; protected Io_url fil, root; + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + @Test @gplx.Virtual public void CreateDir() { + fx.tst_ExistsPaths(false, root); + + engine.CreateDir(root); + fx.tst_ExistsPaths(true, root); + } + @Test public void DeleteDir() { + engine.CreateDir(root); + fx.tst_ExistsPaths(true, root); + + engine.DeleteDir(root); + fx.tst_ExistsPaths(false, root); + } + @Test public void CreateDir_createAllOwners() { + Io_url subDir = root.GenSubDir_nest("sub1"); + fx.tst_ExistsPaths(false, subDir, subDir.OwnerDir()); + + engine.CreateDir(subDir); + fx.tst_ExistsPaths(true, subDir, subDir.OwnerDir()); + } +// @Test public void DeleteDir_missing_fail() { +// try {engine.DeleteDir(root);} +// catch {return;} +// Tfds.Fail_expdError(); +// } + @Test public void DeleteDir_missing_pass() { + engine.DeleteDir(root); + } + @Test @gplx.Virtual public void ScanDir() { + Io_url fil = root.GenSubFil("fil1.txt"); fx.run_SaveFilText(fil, "test"); + Io_url dir1 = root.GenSubDir_nest("dir1"); engine.CreateDir(dir1); + Io_url dir1_1 = dir1.GenSubDir_nest("dir1_1"); engine.CreateDir(dir1_1); // NOTE: QueryDir should not recurse by default; dir1_1 should not be returned below + + fx.tst_ScanDir(root, dir1, fil); + } + @Test public void MoveDir() { + Io_url src = root.GenSubDir_nest("src"), trg = root.GenSubDir_nest("trg"); + engine.CreateDir(src); + fx.tst_ExistsPaths(true, src); fx.tst_ExistsPaths(false, trg); + + engine.MoveDir(src, trg); + fx.tst_ExistsPaths(false, src); fx.tst_ExistsPaths(true, trg); +} +@Test @gplx.Virtual public void CopyDir() { + Io_url src = root.GenSubDir_nest("src"), trg = root.GenSubDir_nest("trg"); + engine.CreateDir(src); + fx.tst_ExistsPaths(true, src); fx.tst_ExistsPaths(false, trg); + + engine.CopyDir(src, trg); + fx.tst_ExistsPaths(true, src, trg); +} +} \ No newline at end of file diff --git a/100_core/tst/gplx/core/ios/IoEngine_dir_basic_memory_tst.java b/100_core/tst/gplx/core/ios/IoEngine_dir_basic_memory_tst.java new file mode 100644 index 000000000..532d9ff0b --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_dir_basic_memory_tst.java @@ -0,0 +1,24 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_dir_basic_memory_tst extends IoEngine_dir_basic_base { + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem"); + } @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_dir_basic_system_tst.java b/100_core/tst/gplx/core/ios/IoEngine_dir_basic_system_tst.java new file mode 100644 index 000000000..8ae66e7be --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_dir_basic_system_tst.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_dir_basic_system_tst extends IoEngine_dir_basic_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + IoEngine_xrg_deleteDir.new_(root).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_system.new_();} + @Test @Override public void ScanDir() { + super.ScanDir(); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_dir_deep_base.java b/100_core/tst/gplx/core/ios/IoEngine_dir_deep_base.java new file mode 100644 index 000000000..0a6b0bf17 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_dir_deep_base.java @@ -0,0 +1,126 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public abstract class IoEngine_dir_deep_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + setup_paths(); + setup_objs(); + } protected IoEngine engine; protected Io_url fil, root; @gplx.Internal protected IoEngineFxt fx; + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + @Test @gplx.Virtual public void SearchDir() { + Io_url[] expd = paths_(src_dir0a, src_fil0a, src_dir0a_dir0a, src_dir0a_fil0a); + Io_url[] actl = IoEngine_xrg_queryDir.new_(src).Recur_().DirInclude_().ExecAsUrlAry(); + Tfds.Eq_ary(expd, actl); + } + @Test @gplx.Virtual public void MoveDirDeep() { + fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); + + engine.MoveDirDeep(IoEngine_xrg_xferDir.move_(src, trg).Recur_()); + fx.tst_ExistsPaths(false, srcTree); + fx.tst_ExistsPaths(true, trgTree); + } + @Test @gplx.Virtual public void CopyDir() { + fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); + + engine.CopyDir(src, trg); + fx.tst_ExistsPaths(true, srcTree); + fx.tst_ExistsPaths(true, trgTree); + } + @Test @gplx.Virtual public void DeleteDir() { + fx.tst_ExistsPaths(true, srcTree); + + engine.DeleteDirDeep(IoEngine_xrg_deleteDir.new_(src).Recur_()); + fx.tst_ExistsPaths(false, srcTree); + } +// @Test public virtual void CopyDir_IgnoreExisting() { +// fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); +// engine.SaveFilStr(trg_dir0a_fil0a, "x"); // NOTE: this file is different than src counterpart; should be overwritten by Copy +// fx.tst_ExistsPaths(true, trg_dir0a, trg_dir0a_fil0a); +// +// engine.CopyDir(src, trg); +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(true, trgTree); +// } +// @Test public virtual void CopyDir_IgnoreExistingReadOnlyFile() { +// fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); +// engine.SaveFilStr(trg_fil0a, "x"); // NOTE: this file is different than src counterpart; should be overwritten by Copy +// fx.tst_ExistsPaths(true, trg_fil0a); +// engine.UpdateFilAttrib(trg_fil0a, IoItmAttrib.ReadOnlyFile); +// +// engine.CopyDir(src, trg); +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(true, trgTree); +// } +// @Test public void MoveDir_IgnoreExisting() { +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(false, trgTree); +// engine.SaveFilStr(trg_dir0a_fil0a, @"x"); // NOTE: this file is different than src counterpart; should be overwritten by Copy +// fx.tst_ExistsPaths(true, trg_dir0a, trg_dir0a_fil0a); +// +// engine.MoveDir(src, trg); +// +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(true, trgTree); +// } +// @Test public virtual void ProgressUi() { +// Console_adp__mem dialog = Console_adp__mem.new_(); +// engine.SearchDir(src).Recur_().Prog_(dialog).ExecAsDir(); +// +// Tfds.Eq(dialog.Written.Count, 3); // 3 levels +// tst_(dialog, 0, "scan", src); +// tst_(dialog, 1, "scan", src_dir0a); +// tst_(dialog, 2, "scan", src_dir0a_dir0a); +// } +// void tst_(Console_adp__mem dialog, int i, String s, Io_url root) { +// Object o = dialog.Written.Get_at(i); +// IoStatusArgs args = (IoStatusArgs)o; +// Tfds.Eq(s, args.Op); +// Tfds.Eq(root, args.Path); +// } + protected Io_url src, src_dir0a, src_dir0a_dir0a; + Io_url src_fil0a, src_dir0a_fil0a; + protected Io_url trg, trg_dir0a, trg_dir0a_dir0a; + Io_url trg_fil0a, trg_dir0a_fil0a; + Io_url[] srcTree, trgTree; + Io_url[] paths_(Io_url... ary) {return ary;} + protected void setup_paths() { + src = root.GenSubDir_nest("src"); + src_dir0a = root.GenSubDir_nest("src", "dir0a"); + src_dir0a_dir0a = root.GenSubDir_nest("src", "dir0a", "dir0a"); + src_fil0a = root.GenSubFil_nest("src", "fil0a.txt"); + src_dir0a_fil0a = root.GenSubFil_nest("src", "dir0a", "fil0a.txt"); + trg = root.GenSubDir_nest("trg"); + trg_dir0a = root.GenSubDir_nest("trg", "dir0a"); + trg_dir0a_dir0a = root.GenSubDir_nest("trg", "dir0a", "dir0a"); + trg_fil0a = root.GenSubFil_nest("trg", "fil0a.txt"); + trg_dir0a_fil0a = root.GenSubFil_nest("trg", "dir0a", "fil0a.txt"); + srcTree = new Io_url[] {src, src_dir0a, src_dir0a_dir0a, src_fil0a, src_dir0a_fil0a}; + trgTree = new Io_url[] {trg, trg_dir0a, trg_dir0a_dir0a, trg_fil0a, trg_dir0a_fil0a}; + } + void setup_objs() { + fx.run_SaveFilText(src_fil0a, "src_fil0a"); // NOTE: automatically creates src + fx.run_SaveFilText(src_dir0a_fil0a, "src_dir0a_fil0a"); // NOTE: automatically creates src_dir0a_dir0a + fx.tst_ExistsPaths(true, src_fil0a); + engine.CreateDir(src_dir0a_dir0a); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_dir_deep_memory_tst.java b/100_core/tst/gplx/core/ios/IoEngine_dir_deep_memory_tst.java new file mode 100644 index 000000000..98e78c6bf --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_dir_deep_memory_tst.java @@ -0,0 +1,36 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_dir_deep_memory_tst extends IoEngine_dir_deep_base { + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem/root"); + } @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} + @Test @Override public void SearchDir() { + super.SearchDir(); + } + @Test @Override public void MoveDirDeep() { + super.MoveDirDeep(); + } + @Test @Override public void CopyDir() { + super.CopyDir(); + } + @Test @Override public void DeleteDir() { + super.DeleteDir(); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_dir_deep_system_tst.java b/100_core/tst/gplx/core/ios/IoEngine_dir_deep_system_tst.java new file mode 100644 index 000000000..9c4e73508 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_dir_deep_system_tst.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_dir_deep_system_tst extends IoEngine_dir_deep_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + IoEngine_xrg_deleteDir.new_(root).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_.Sys;} +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_fil_basic_base.java b/100_core/tst/gplx/core/ios/IoEngine_fil_basic_base.java new file mode 100644 index 000000000..cce461bc6 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_fil_basic_base.java @@ -0,0 +1,176 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.core.texts.*;/*EncodingAdp_*/ import gplx.core.ios.streams.*; +public abstract class IoEngine_fil_basic_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + } protected IoEngine engine; protected IoEngineFxt fx; protected Io_url fil, root; + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + @Test @gplx.Virtual public void ExistsFil() { + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void ExistsFil_deep() { + fx.tst_ExistsPaths(false, root.GenSubFil_nest("dir1", "dir2", "fil1.txt")); + } + @Test @gplx.Virtual public void SaveFilStr() { + fx.tst_ExistsPaths(false, fil, fil.OwnerDir()); + + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil, fil.OwnerDir()); + } + @Test @gplx.Virtual public void SaveFilText_autoCreateOwnerDir() { + fil = fil.OwnerDir().GenSubFil_nest("sub1", "fil1.txt"); + fx.tst_ExistsPaths(false, fil, fil.OwnerDir()); + + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil, fil.OwnerDir()); + } + @Test @gplx.Virtual public void SaveFilText_overwrite() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + fx.run_SaveFilText(fil, "changed"); + fx.tst_LoadFilStr(fil, "changed"); + } + @Test @gplx.Virtual public void SaveFilText_append() { + fx.run_SaveFilText(fil, "text"); + + engine.SaveFilText_api(IoEngine_xrg_saveFilStr.new_(fil, "appended").Append_()); + fx.tst_LoadFilStr(fil, "text" + "appended"); + } + @Test @gplx.Virtual public void SaveFilText_caseInsensitive() { + if (root.Info().CaseSensitive()) return; + Io_url lcase = root.GenSubFil_nest("dir", "fil.txt"); + Io_url ucase = root.GenSubFil_nest("DIR", "FIL.TXT"); + fx.run_SaveFilText(lcase, "text"); + + fx.tst_ExistsPaths(true, lcase, ucase); + fx.tst_LoadFilStr(lcase, "text"); + fx.tst_LoadFilStr(ucase, "text"); + } + @Test @gplx.Virtual public void SaveFilText_readOnlyFails() { + fx.run_SaveFilText(fil, "text"); + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + + try {fx.run_SaveFilText(fil, "changed");} + catch (Exception exc) { + fx.tst_LoadFilStr(fil, "text"); + Err_.Noop(exc); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void LoadFilStr() { + fx.run_SaveFilText(fil, "text"); + fx.tst_LoadFilStr(fil, "text"); + } + @Test @gplx.Virtual public void LoadFilStr_missingIgnored() { + Tfds.Eq("", engine.LoadFilStr(IoEngine_xrg_loadFilStr.new_(fil).MissingIgnored_())); + } + @Test @gplx.Virtual public void UpdateFilAttrib() { + fx.run_SaveFilText(fil, "text"); + fx.tst_QueryFilReadOnly(fil, false); + + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + fx.tst_QueryFilReadOnly(fil, true); + } + @Test @gplx.Virtual public void DeleteFil() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil)); + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void DeleteFil_missing_pass() { + fil = root.GenSubFil("fileThatDoesntExist.txt"); + + engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil).MissingFails_off()); + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void DeleteFil_readOnly_fail() { + fx.run_SaveFilText(fil, "text"); + + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + try {engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil));} + catch (Exception exc) {Err_.Noop(exc); + fx.tst_ExistsPaths(true, fil); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void DeleteFil_readOnly_pass() { + fx.run_SaveFilText(fil, "text"); + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + + engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil).ReadOnlyFails_off()); + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void QueryFil_size() { + fx.run_SaveFilText(fil, "text"); + + fx.tst_QueryFil_size(fil, String_.Len("text")); + } + @Test @gplx.Virtual public void UpdateFilModifiedTime() { + fx.run_SaveFilText(fil, "text"); + + DateAdp time = Datetime_now.Dflt_add_min_(10); + engine.UpdateFilModifiedTime(fil, time); + fx.tst_QueryFil_modifiedTime(fil, time); + } + @Test @gplx.Virtual public void OpenStreamRead() { + fx.run_SaveFilText(fil, "text"); + + int textLen = String_.Len("text"); + byte[] buffer = new byte[textLen]; + IoStream stream = IoStream_.Null; + try { + stream = engine.OpenStreamRead(fil); + stream.Read(buffer, 0, textLen); + } + finally {stream.Rls();} + String actl = String_.new_u8(buffer); + Tfds.Eq("text", actl); + } + @Test @gplx.Virtual public void OpenStreamWrite() { + IoStream stream = IoEngine_xrg_openWrite.new_(fil).Exec(); + byte[] buffer = Bry_.new_u8("text"); + int textLen = String_.Len("text"); + stream.Write(buffer, 0, textLen); + stream.Rls(); + + fx.tst_LoadFilStr(fil, "text"); + } +// @Test public virtual void OpenStreamWrite_in_place() { +// byte[] buffer = Bry_.new_u8("a|b|c"); +// IoStream stream = IoEngine_xrg_openWrite.new_(fil).Exec(); +// stream.Write(buffer, 0, buffer.length); +// stream.Rls(); +// +// buffer = Bry_.new_u8("B"); +// stream = IoEngine_xrg_openWrite.new_(fil).Exec(); +// stream.Seek(2); +// stream.Write(buffer, 0, buffer.length); +// stream.Rls(); +// +// fx.tst_LoadFilStr(fil, "a|B|c"); +// } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_fil_basic_memory_tst.java b/100_core/tst/gplx/core/ios/IoEngine_fil_basic_memory_tst.java new file mode 100644 index 000000000..c5d447ef1 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_fil_basic_memory_tst.java @@ -0,0 +1,57 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_fil_basic_memory_tst extends IoEngine_fil_basic_base { + @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem"); + fil = root.GenSubFil_nest("root", "fil.txt"); + } + @Test @Override public void OpenStreamRead() { + super.OpenStreamRead (); + } + @Test @Override public void SaveFilText_overwrite() { + super.SaveFilText_overwrite(); + + // bugfix: verify changed file in ownerDir's hash + IoItmDir dirItm = fx.tst_ScanDir(fil.OwnerDir(), fil); + IoItmFil_mem filItm = (IoItmFil_mem)dirItm.SubFils().Get_at(0); + Tfds.Eq(filItm.Text(), "changed"); + } + @Test public void RecycleFil() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + IoRecycleBin bin = IoRecycleBin.Instance; + List_adp list = Tfds.RscDir.XtoNames(); +// foreach (String s in list) +// Tfds.Write(s); + list.Del_at(0); // remove drive + IoEngine_xrg_recycleFil recycleXrg = bin.Send_xrg(fil) + .RootDirNames_(list) + .AppName_("gplx.test").Time_(DateAdp_.parse_gplx("20100102_115559123")).Uuid_(Guid_adp_.Parse("467ffb41-cdfe-402f-b22b-be855425784b")); + recycleXrg.Exec(); + fx.tst_ExistsPaths(false, fil); + fx.tst_ExistsPaths(true, recycleXrg.RecycleUrl()); + + bin.Recover(recycleXrg.RecycleUrl()); + fx.tst_ExistsPaths(true, fil); + fx.tst_ExistsPaths(false, recycleXrg.RecycleUrl()); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_fil_basic_system_tst.java b/100_core/tst/gplx/core/ios/IoEngine_fil_basic_system_tst.java new file mode 100644 index 000000000..842b31482 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_fil_basic_system_tst.java @@ -0,0 +1,58 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_fil_basic_system_tst extends IoEngine_fil_basic_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + fil = root.GenSubFil("fil.txt"); + IoEngine_xrg_deleteDir.new_(fil.OwnerDir()).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_system.new_();} + @Test public void ExistsFil_IgnoreDifferentCasing() { + if (root.Info().CaseSensitive()) return; + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + fx.tst_ExistsPaths(true, fil.OwnerDir().GenSubFil("FIL.txt")); + } + @Test @gplx.Virtual public void RecycleFil() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + IoRecycleBin bin = IoRecycleBin.Instance; + List_adp list = root.XtoNames(); list.Del_at(0); // remove drive + IoEngine_xrg_recycleFil recycleXrg = bin.Send_xrg(fil) + .RootDirNames_(list) + .AppName_("gplx.test").Time_(DateAdp_.parse_gplx("20100102_115559123")).Uuid_(Guid_adp_.Parse("467ffb41-cdfe-402f-b22b-be855425784b")); + recycleXrg.Exec(); + fx.tst_ExistsPaths(false, fil); + fx.tst_ExistsPaths(true, recycleXrg.RecycleUrl()); + + bin.Recover(recycleXrg.RecycleUrl()); + fx.tst_ExistsPaths(true, fil); + fx.tst_ExistsPaths(false, recycleXrg.RecycleUrl()); + } + @Test @Override public void DeleteFil_missing_pass() { + super.DeleteFil_missing_pass(); + } + @Test @Override public void DeleteFil_readOnly_pass() { + super.DeleteFil_readOnly_pass (); + } + @Test @Override public void SaveFilText_readOnlyFails() { + super.SaveFilText_readOnlyFails(); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_base.java b/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_base.java new file mode 100644 index 000000000..3f0459d3e --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_base.java @@ -0,0 +1,106 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public abstract class IoEngine_fil_xfer_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + src = root.GenSubFil("src.txt"); trg = root.GenSubFil("trg.txt"); + } protected IoEngine engine; @gplx.Internal protected IoEngineFxt fx; protected Io_url src, trg, root; + DateAdp srcModifiedTime = DateAdp_.parse_gplx("2010.04.12 20.26.01.000"), trgModifiedTime = DateAdp_.parse_gplx("2010.04.01 01.01.01.000"); + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + protected abstract Io_url AltRoot(); + @Test @gplx.Virtual public void CopyFil() { + fx.run_SaveFilText(src, "src"); fx.run_UpdateFilModifiedTime(src, srcModifiedTime); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(false, trg); + + IoEngine_xrg_xferFil.copy_(src, trg).Exec(); + fx.tst_ExistsPaths(true, src, trg); + fx.tst_LoadFilStr(trg, "src"); + fx.tst_QueryFil_modifiedTime(trg, srcModifiedTime); + } + @Test @gplx.Virtual public void CopyFil_overwrite_fail() { + fx.run_SaveFilText(src, "src"); + fx.run_SaveFilText(trg, "trg"); + + try {IoEngine_xrg_xferFil.copy_(src, trg).Exec();} + catch (Exception exc) {Err_.Noop(exc); + fx.tst_ExistsPaths(true, src, trg); + fx.tst_LoadFilStr(trg, "trg"); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void CopyFil_overwrite_pass() { + fx.run_SaveFilText(src, "src"); fx.run_UpdateFilModifiedTime(src, srcModifiedTime); + fx.run_SaveFilText(trg, "trg"); fx.run_UpdateFilModifiedTime(trg, trgModifiedTime); + + IoEngine_xrg_xferFil.copy_(src, trg).Overwrite_().Exec(); + fx.tst_ExistsPaths(true, src, trg); + fx.tst_LoadFilStr(trg, "src"); + fx.tst_QueryFil_modifiedTime(trg, srcModifiedTime); + } + @Test @gplx.Virtual public void MoveFil() { + fx.run_SaveFilText(src, "src"); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(false, trg); + + IoEngine_xrg_xferFil.move_(src, trg).Exec(); + fx.tst_ExistsPaths(false, src); + fx.tst_ExistsPaths(true, trg); + } + @Test @gplx.Virtual public void MoveFil_overwrite_fail() { + fx.run_SaveFilText(src, "src"); + fx.run_SaveFilText(trg, "trg"); + + try {IoEngine_xrg_xferFil.move_(src, trg).Exec();} + catch (Exception exc) {Err_.Noop(exc); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(true, trg); + fx.tst_LoadFilStr(trg, "trg"); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void MoveFil_overwrite_pass() { + fx.run_SaveFilText(src, "src"); fx.run_UpdateFilModifiedTime(src, srcModifiedTime); + fx.run_SaveFilText(trg, "trg"); fx.run_UpdateFilModifiedTime(trg, trgModifiedTime); + + IoEngine_xrg_xferFil.move_(src, trg).Overwrite_().Exec(); + fx.tst_ExistsPaths(false, src); + fx.tst_ExistsPaths(true, trg); + fx.tst_LoadFilStr(trg, "src"); + fx.tst_QueryFil_modifiedTime(trg, srcModifiedTime); + } + @Test @gplx.Virtual public void MoveFil_betweenDrives() { + IoEngine_xrg_deleteDir.new_(AltRoot()).Recur_().ReadOnlyFails_off().Exec(); + src = root.GenSubFil_nest("dir", "fil1a.txt"); + trg = AltRoot().GenSubFil_nest("dir", "fil1b.txt"); + fx.run_SaveFilText(src, "src"); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(false, trg); + + IoEngine_xrg_xferFil.move_(src, trg).Exec(); + fx.tst_ExistsPaths(false, src); + fx.tst_ExistsPaths(true, trg); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_memory_tst.java b/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_memory_tst.java new file mode 100644 index 000000000..24088905e --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_memory_tst.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_fil_xfer_memory_tst extends IoEngine_fil_xfer_base { + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem"); + } @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} + @Override protected Io_url AltRoot() { + Io_mgr.Instance.InitEngine_mem_("mem2"); + return Io_url_.mem_dir_("mem2"); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_system_tst.java b/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_system_tst.java new file mode 100644 index 000000000..d9913f7f8 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_fil_xfer_system_tst.java @@ -0,0 +1,28 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_fil_xfer_system_tst extends IoEngine_fil_xfer_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + IoEngine_xrg_deleteDir.new_(root.OwnerDir()).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_system.new_();} + @Override protected Io_url AltRoot() { + return Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + } +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_stream_xfer_tst.java b/100_core/tst/gplx/core/ios/IoEngine_stream_xfer_tst.java new file mode 100644 index 000000000..642feef31 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_stream_xfer_tst.java @@ -0,0 +1,49 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_stream_xfer_tst { + @Before public void setup() { + srcEngine = IoEngine_memory.new_("mock1"); + trgEngine = IoEngine_memory.new_("mock2"); + IoEnginePool.Instance.Add_if_dupe_use_nth(srcEngine); IoEnginePool.Instance.Add_if_dupe_use_nth(trgEngine); + IoUrlInfoRegy.Instance.Reg(IoUrlInfo_.mem_("mem1/", srcEngine.Key())); + IoUrlInfoRegy.Instance.Reg(IoUrlInfo_.mem_("mem2/", trgEngine.Key())); + srcDir = Io_url_.mem_dir_("mem1/dir"); trgDir = Io_url_.mem_dir_("mem2/dir"); + } + @Test public void TransferBetween() { + Io_url srcPath = srcDir.GenSubFil("fil.txt"); + Io_url trgPath = trgDir.GenSubFil("fil.txt"); + tst_TransferStreams(srcEngine, srcPath, trgEngine, trgPath); + } + void tst_TransferStreams(IoEngine srcEngine, Io_url srcPath, IoEngine trgEngine, Io_url trgPath) { + srcEngine.SaveFilText_api(IoEngine_xrg_saveFilStr.new_(srcPath, "test1")); + trgEngine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(trgPath)); // make sure file is deleted + fx.tst_ExistsPaths(true, srcPath); + fx.tst_ExistsPaths(false, trgPath); + + IoEngineUtl utl = IoEngineUtl.new_(); + utl.BufferLength_set(4); + utl.XferFil(srcEngine, IoEngine_xrg_xferFil.copy_(srcPath, trgPath)); + fx.tst_ExistsPaths(true, srcPath, trgPath); + fx.tst_LoadFilStr(trgPath, "test1"); + } + IoEngineFxt fx = IoEngineFxt.new_(); + Io_url srcDir, trgDir; + IoEngine srcEngine, trgEngine; +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_xrg_queryDir_tst.java b/100_core/tst/gplx/core/ios/IoEngine_xrg_queryDir_tst.java new file mode 100644 index 000000000..6a2fa8f21 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_xrg_queryDir_tst.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_xrg_queryDir_tst { + @Before public void setup() { + engine = IoEngine_.Mem_init_(); + } IoEngine engine; Io_url[] ary; + @Test public void Basic() { + ary = save_text_(fil_("fil1.txt")); + + tst_ExecPathAry(finder_(), ary); + } + @Test public void FilPath() { + ary = save_text_(fil_("fil1.txt"), fil_("fil2.jpg"), fil_("fil3.txt")); + + tst_ExecPathAry(finder_(), ary); // default: all files + tst_ExecPathAry(finder_().FilPath_("*.txt") // findPattern of *.txt + , fil_("fil1.txt"), fil_("fil3.txt")); + } + @Test public void Recur() { + ary = save_text_(fil_("fil1.txt"), fil_("dirA", "fil1A.jpg")); + + tst_ExecPathAry(finder_(), fil_("fil1.txt")); // default: no recursion + tst_ExecPathAry(finder_().Recur_(), ary); // recurse + } + @Test public void DirPattern() { + save_text_(fil_("fil1.txt"), fil_("dirA", "fil1A.jpg")); + + tst_ExecPathAry(finder_(), fil_("fil1.txt")); // default: files only + tst_ExecPathAry(finder_().DirInclude_() // include dirs; NOTE: fil1A not returned b/c Recur_ is not true + , dir_("dirA"), fil_("fil1.txt")); + } + @Test public void Sort_by() { + save_text_(fil_("fil2a.txt"), fil_("fil1.txt")); + + tst_ExecPathAry(finder_() // default: sortByAscOrder + , fil_("fil1.txt"), fil_("fil2a.txt")); + } + IoEngine_xrg_queryDir finder_() {return IoEngine_xrg_queryDir.new_(Io_url_.mem_dir_("mem/root"));}// NOTE: not in setup b/c finder must be newed several times inside test method + Io_url fil_(String... ary) {return Io_url_.mem_dir_("mem/root").GenSubFil_nest(ary);} + Io_url dir_(String... ary) {return Io_url_.mem_dir_("mem/root").GenSubDir_nest(ary);} + + Io_url[] save_text_(Io_url... ary) { + for (Io_url url : ary) + Io_mgr.Instance.SaveFilStr(url, url.Raw()); + return ary; + } + void tst_ExecPathAry(IoEngine_xrg_queryDir finder, Io_url... expd) {Tfds.Eq_ary(expd, finder.ExecAsUrlAry());} +} diff --git a/100_core/tst/gplx/core/ios/IoEngine_xrg_recycleFil_tst.java b/100_core/tst/gplx/core/ios/IoEngine_xrg_recycleFil_tst.java new file mode 100644 index 000000000..481659ffc --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoEngine_xrg_recycleFil_tst.java @@ -0,0 +1,32 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoEngine_xrg_recycleFil_tst { + @Before public void setup() { + IoEngine_.Mem_init_(); + } + @Test public void GenRecycleUrl() { + tst_GenRecycleUrl(recycle_(), Io_url_.mem_fil_("mem/z_trash/20100102/gplx.images;115559123;;fil.txt")); + tst_GenRecycleUrl(recycle_().Uuid_include_(), Io_url_.mem_fil_("mem/z_trash/20100102/gplx.images;115559123;467ffb41-cdfe-402f-b22b-be855425784b;fil.txt")); + } + IoEngine_xrg_recycleFil recycle_() {return IoEngine_xrg_recycleFil.gplx_(Io_url_.mem_fil_("mem/dir/fil.txt")).AppName_("gplx.images").Uuid_(Guid_adp_.Parse("467ffb41-cdfe-402f-b22b-be855425784b")).Time_(DateAdp_.parse_gplx("20100102_115559123"));} + void tst_GenRecycleUrl(IoEngine_xrg_recycleFil xrg, Io_url expd) { + Tfds.Eq(expd, xrg.RecycleUrl()); + } +} diff --git a/100_core/tst/gplx/core/ios/IoItmDir_FetchDeepOrNull_tst.java b/100_core/tst/gplx/core/ios/IoItmDir_FetchDeepOrNull_tst.java new file mode 100644 index 000000000..c030353ae --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoItmDir_FetchDeepOrNull_tst.java @@ -0,0 +1,40 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoItmDir_FetchDeepOrNull_tst { + @Before public void setup() { + drive = Io_url_.mem_dir_("mem"); + rootDir = bldr.dir_(drive, bldr.dir_(drive.GenSubDir("sub1"))); + } IoItm_fxt bldr = IoItm_fxt.new_(); Io_url drive; IoItmDir rootDir; + @Test public void FetchDeepOrNull() { + tst_FetchDeepOrNull(rootDir, drive.GenSubDir("sub1"), true); + tst_FetchDeepOrNull(rootDir, drive.GenSubDir("sub2"), false); + tst_FetchDeepOrNull(rootDir.SubDirs().Get_at(0), drive.GenSubDir("sub1"), true); + tst_FetchDeepOrNull(rootDir.SubDirs().Get_at(0), drive.GenSubDir("sub2"), false); + } + void tst_FetchDeepOrNull(Object rootDirObj, Io_url find, boolean expdFound) { + IoItmDir rootDir = IoItmDir_.as_(rootDirObj); + IoItmDir actlDir = rootDir.FetchDeepOrNull(find); + if (actlDir == null) { + if (expdFound) Tfds.Fail("actlDir is null, but expd dir to be found"); + else return; // actlDir is null but expdFound was false; return; + } + Tfds.Eq(find.Raw(), actlDir.Url().Raw()); + } +} diff --git a/100_core/tst/gplx/core/ios/IoUrlInfo_alias_tst.java b/100_core/tst/gplx/core/ios/IoUrlInfo_alias_tst.java new file mode 100644 index 000000000..6ae71a8c7 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoUrlInfo_alias_tst.java @@ -0,0 +1,58 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoUrlInfo_alias_tst { + IoUrlInfo_alias alias; + @Test public void MapWntToWnt() { + Make("usr:\\", "D:\\usr\\"); + tst_Xto_api("usr:\\dir\\fil.txt", "D:\\usr\\dir\\fil.txt"); + tst_OwnerDir("usr:\\dir\\", "usr:\\"); + tst_OwnerDir("usr:\\", ""); + tst_NameOnly("usr:\\", "usr"); + } + @Test public void MapToLnx() { + Make("usr:\\", "/home/"); + tst_Xto_api("usr:\\dir\\fil.txt", "/home/dir/fil.txt"); + } + @Test public void MapLnxToWnt() { + Make("usr:/", "C:\\usr\\"); + tst_Xto_api("usr:/dir/fil.txt", "C:\\usr\\dir\\fil.txt"); + } + @Test public void WntToWnt() { + Make("C:\\", "X:\\"); + tst_Xto_api("C:\\dir\\fil.txt", "X:\\dir\\fil.txt"); + tst_NameOnly("C:\\", "C"); + } + @Test public void WntToLnx() { + Make("C:\\", "/home/"); + tst_Xto_api("C:\\dir\\fil.txt", "/home/dir/fil.txt"); + } + @Test public void LnxToWnt() { + Make("/home/", "C:\\"); + tst_Xto_api("/home/dir/fil.txt", "C:\\dir\\fil.txt"); + tst_NameOnly("/home/", "home"); + tst_NameOnly("/", "root"); + } + void tst_Xto_api(String raw, String expd) {Tfds.Eq(expd, alias.Xto_api(raw));} + void tst_OwnerDir(String raw, String expd) {Tfds.Eq(expd, alias.OwnerDir(raw));} + void tst_NameOnly(String raw, String expd) {Tfds.Eq(expd, alias.NameOnly(raw));} + void Make(String srcDir, String trgDir) { + alias = IoUrlInfo_alias.new_(srcDir, trgDir, IoEngine_.SysKey); + } +} diff --git a/100_core/tst/gplx/core/ios/IoUrl_lnx_tst.java b/100_core/tst/gplx/core/ios/IoUrl_lnx_tst.java new file mode 100644 index 000000000..889ca1885 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoUrl_lnx_tst.java @@ -0,0 +1,55 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoUrl_lnx_tst { + IoUrlFxt fx = IoUrlFxt.new_(); + @Test public void Raw() { + fx.tst_Xto_gplx(Io_url_.lnx_dir_("/home/"), "/home/"); + fx.tst_Xto_gplx(Io_url_.lnx_dir_("/home"), "/home/"); // add / + fx.tst_Xto_gplx(Io_url_.lnx_dir_("/"), "/"); + fx.tst_Xto_gplx(Io_url_.lnx_fil_("/home/fil.txt"), "/home/fil.txt"); + } + @Test public void Xto_api() { + fx.tst_Xto_api(Io_url_.lnx_fil_("/home/fil.txt"), "/home/fil.txt"); + fx.tst_Xto_api(Io_url_.lnx_dir_("/home/"), "/home"); // del / + fx.tst_Xto_api(Io_url_.lnx_dir_("/"), "/"); + } + @Test public void OwnerRoot() { + fx.tst_OwnerRoot(Io_url_.lnx_dir_("/home/fil.txt"), "/"); + fx.tst_OwnerRoot(Io_url_.lnx_dir_("/home"), "/"); + fx.tst_OwnerRoot(Io_url_.lnx_dir_("root"), "/"); + } + @Test public void XtoNames() { + fx.tst_XtoNames(Io_url_.lnx_dir_("/home/fil.txt"), fx.ary_("root", "home", "fil.txt")); + fx.tst_XtoNames(Io_url_.lnx_dir_("/home"), fx.ary_("root", "home")); + } + @Test public void IsDir() { + fx.tst_IsDir(Io_url_.lnx_dir_("/home"), true); + fx.tst_IsDir(Io_url_.lnx_fil_("/home/file.txt"), false); + } + @Test public void OwnerDir() { + fx.tst_OwnerDir(Io_url_.lnx_dir_("/home/lnxusr"), Io_url_.lnx_dir_("/home")); + fx.tst_OwnerDir(Io_url_.lnx_dir_("/fil.txt"), Io_url_.lnx_dir_("/")); + fx.tst_OwnerDir(Io_url_.lnx_dir_("/"), Io_url_.Empty); + } + @Test public void NameAndExt() { + fx.tst_NameAndExt(Io_url_.lnx_fil_("/fil.txt"), "fil.txt"); + fx.tst_NameAndExt(Io_url_.lnx_dir_("/dir"), "dir/"); + } +} diff --git a/100_core/tst/gplx/core/ios/IoUrl_map_tst.java b/100_core/tst/gplx/core/ios/IoUrl_map_tst.java new file mode 100644 index 000000000..6270cc5de --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoUrl_map_tst.java @@ -0,0 +1,31 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoUrl_map_tst { + IoUrlFxt fx = IoUrlFxt.new_(); + @Test public void Xto_api() { + IoUrlInfo inf = IoUrlInfo_.alias_("tst:\\", "C:\\tst\\", IoEngine_.SysKey); + fx.tst_Xto_api(Io_url_.new_inf_("tst:\\dir\\fil.txt", inf), "C:\\tst\\dir\\fil.txt"); + fx.tst_Xto_api(Io_url_.new_inf_("tst:\\dir\\", inf), "C:\\tst\\dir"); // no trailing \ + } + @Test public void Xto_api_wce() { + IoUrlInfo inf = IoUrlInfo_.alias_("wce:\\", "\\SD Card\\", IoEngine_.SysKey); + fx.tst_Xto_api(Io_url_.new_inf_("wce:\\dir\\", inf), "\\SD Card\\dir"); + } +} diff --git a/100_core/tst/gplx/core/ios/IoUrl_wnt_tst.java b/100_core/tst/gplx/core/ios/IoUrl_wnt_tst.java new file mode 100644 index 000000000..80cf41404 --- /dev/null +++ b/100_core/tst/gplx/core/ios/IoUrl_wnt_tst.java @@ -0,0 +1,98 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.ios; import gplx.*; import gplx.core.*; +import org.junit.*; +public class IoUrl_wnt_tst { + IoUrlFxt fx = IoUrlFxt.new_(); + @Test public void Raw() { + fx.tst_Xto_gplx(Io_url_.wnt_fil_("C:\\dir\\fil.txt"), "C:\\dir\\fil.txt"); + fx.tst_Xto_gplx(Io_url_.wnt_dir_("C:\\dir\\"), "C:\\dir\\"); + fx.tst_Xto_gplx(Io_url_.wnt_dir_("C:\\dir") , "C:\\dir\\"); // add \ + } + @Test public void Xto_api() { + fx.tst_Xto_api(Io_url_.wnt_fil_("C:\\fil.txt"), "C:\\fil.txt"); + fx.tst_Xto_api(Io_url_.wnt_dir_("C:\\dir\\"), "C:\\dir"); // del \ + fx.tst_Xto_api(Io_url_.wnt_dir_("C:"), "C:"); + } + @Test public void OwnerRoot() { + fx.tst_OwnerRoot(Io_url_.wnt_dir_("C:\\dir") , "C:\\"); + fx.tst_OwnerRoot(Io_url_.wnt_dir_("C:\\fil.png") , "C:\\"); + fx.tst_OwnerRoot(Io_url_.wnt_dir_("C:") , "C:\\"); + } + @Test public void IsDir() { + fx.tst_IsDir(Io_url_.wnt_dir_("C:\\dir\\"), true); + fx.tst_IsDir(Io_url_.wnt_fil_("C:\\dir"), false); + fx.tst_IsDir(Io_url_.wnt_fil_("C:\\fil.txt"), false); + } + @Test public void OwnerDir() { + fx.tst_OwnerDir(Io_url_.wnt_dir_("C:\\dir\\sub1"), Io_url_.wnt_dir_("C:\\dir")); + fx.tst_OwnerDir(Io_url_.wnt_fil_("C:\\fil.txt"), Io_url_.wnt_dir_("C:")); + fx.tst_OwnerDir(Io_url_.wnt_dir_("C:"), Io_url_.Empty); +// fx.tst_OwnerDir(Io_url_.wnt_fil_("press enter to select this folder"), Io_url_.Empty); + } + @Test public void NameAndExt() { + fx.tst_NameAndExt(Io_url_.wnt_fil_("C:\\fil.txt"), "fil.txt"); + fx.tst_NameAndExt(Io_url_.wnt_dir_("C:\\dir"), "dir\\"); + } + @Test public void NameOnly() { + fx.tst_NameOnly(Io_url_.wnt_fil_("C:\\fil.txt"), "fil"); + fx.tst_NameOnly(Io_url_.wnt_dir_("C:\\dir"), "dir"); + fx.tst_NameOnly(Io_url_.wnt_dir_("C:"), "C"); + } + @Test public void Ext() { + fx.tst_Ext(Io_url_.wnt_fil_("C:\\fil.txt"), ".txt"); // fil + fx.tst_Ext(Io_url_.wnt_fil_("C:\\fil.multiple.txt"), ".txt"); // multiple ext + fx.tst_Ext(Io_url_.wnt_fil_("C:\\fil"), ""); // no ext + fx.tst_Ext(Io_url_.wnt_dir_("C:\\dir"), "\\"); // dir + } + @Test public void GenSubDir_nest() { + fx.tst_GenSubDir_nest(Io_url_.wnt_dir_("C:"), fx.ary_("dir1", "sub1"), Io_url_.wnt_dir_("C:\\dir1\\sub1")); + } + @Test public void GenNewExt() { + fx.tst_GenNewExt(Io_url_.wnt_fil_("C:\\fil.gif"), ".png", Io_url_.wnt_fil_("C:\\fil.png")); // basic + fx.tst_GenNewExt(Io_url_.wnt_fil_("C:\\fil.tst.gif"), ".png", Io_url_.wnt_fil_("C:\\fil.tst.png")); // last in multiple dotted + } + @Test public void GenRelUrl_orEmpty() { + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_fil_("C:\\root\\fil.txt") , Io_url_.wnt_dir_("C:\\root") , "fil.txt"); // fil + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_dir_("C:\\root\\dir") , Io_url_.wnt_dir_("C:\\root") , "dir\\"); // dir + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_fil_("C:\\root\\dir\\fil.txt") , Io_url_.wnt_dir_("C:\\root") , "dir\\fil.txt"); // fil: nested1 + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_fil_("C:\\root\\dir\\fil.txt") , Io_url_.wnt_dir_("C:") , "root\\dir\\fil.txt"); // fil: nested2 + } + @Test public void GenParallel() { + fx.tst_GenParallel(Io_url_.wnt_fil_("C:\\root1\\fil.txt"), Io_url_.wnt_dir_("C:\\root1"), Io_url_.wnt_dir_("D:\\root2"), Io_url_.wnt_fil_("D:\\root2\\fil.txt")); + fx.tst_GenParallel(Io_url_.wnt_dir_("C:\\root1\\dir") , Io_url_.wnt_dir_("C:\\root1"), Io_url_.wnt_dir_("D:\\root2"), Io_url_.wnt_dir_("D:\\root2\\dir")); + } +} +class IoUrlFxt { + public void tst_Xto_api(Io_url url, String expd) {Tfds.Eq(expd, url.Xto_api());} + public void tst_OwnerRoot(Io_url url, String expd) {Tfds.Eq(expd, url.OwnerRoot().Raw());} + public void tst_XtoNames(Io_url url, String... expdAry) {Tfds.Eq_ary(expdAry, url.XtoNames().To_str_ary());} + public void tst_NameAndExt(Io_url url, String expd) {Tfds.Eq(expd, url.NameAndExt());} + public void tst_Xto_gplx(Io_url url, String expd) {Tfds.Eq(expd, url.Raw());} + public void tst_IsDir(Io_url url, boolean expd) {Tfds.Eq(expd, url.Type_dir());} + public void tst_OwnerDir(Io_url url, Io_url expd) {Tfds.Eq_url(expd, url.OwnerDir());} + public void tst_NameOnly(Io_url url, String expd) {Tfds.Eq(expd, url.NameOnly());} + public void tst_Ext(Io_url url, String expd) {Tfds.Eq(expd, url.Ext());} + public void tst_GenSubDir_nest(Io_url rootDir, String[] parts, Io_url expd) {Tfds.Eq(expd, rootDir.GenSubDir_nest(parts));} + public void tst_GenNewExt(Io_url url, String ext, Io_url expd) {Tfds.Eq_url(expd, url.GenNewExt(ext));} + public void tst_GenRelUrl_orEmpty(Io_url url, Io_url rootDir, String expd) {Tfds.Eq(expd, url.GenRelUrl_orEmpty(rootDir));} + public void tst_GenParallel(Io_url url, Io_url oldRoot, Io_url newRoot, Io_url expd) {Tfds.Eq_url(expd, url.GenParallel(oldRoot, newRoot));} + + public String[] ary_(String... ary) {return String_.Ary(ary);} + public static IoUrlFxt new_() {return new IoUrlFxt();} IoUrlFxt() {} +} diff --git a/100_core/tst/gplx/core/stores/GfoNdeRdr_read_tst.java b/100_core/tst/gplx/core/stores/GfoNdeRdr_read_tst.java new file mode 100644 index 000000000..fea0d3ff4 --- /dev/null +++ b/100_core/tst/gplx/core/stores/GfoNdeRdr_read_tst.java @@ -0,0 +1,48 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.core.gfo_ndes.*; import gplx.core.type_xtns.*; +public class GfoNdeRdr_read_tst { + @Test public void ReadInt() { + rdr = rdr_(IntClassXtn.Instance, "id", 1); + Tfds.Eq(rdr.ReadInt("id"), 1); + } + @Test public void ReadIntOr() { + rdr = rdr_(IntClassXtn.Instance, "id", 1); + Tfds.Eq(rdr.ReadIntOr("id", -1), 1); + } + @Test public void ReadIntElse_minus1() { + rdr = rdr_(IntClassXtn.Instance, "id", null); + Tfds.Eq(rdr.ReadIntOr("id", -1), -1); + } + @Test public void ReadInt_parse() { + rdr = rdr_(StringClassXtn.Instance, "id", "1"); + Tfds.Eq(rdr.ReadInt("id"), 1); + } + @Test public void ReadIntElse_parse() { + rdr = rdr_(StringClassXtn.Instance, "id", "2"); + Tfds.Eq(rdr.ReadIntOr("id", -1), 2); + } + GfoNdeRdr rdr_(ClassXtn type, String key, Object val) { // makes rdr with one row and one val + GfoFldList flds = GfoFldList_.new_().Add(key, type); + GfoNde row = GfoNde_.vals_(flds, new Object[] {val}); + boolean parse = type == StringClassXtn.Instance; // assumes type is either StringClassXtn or IntClassXtn + return GfoNdeRdr_.leaf_(row, parse); + } + GfoNdeRdr rdr; +} diff --git a/100_core/tst/gplx/core/stores/GfoNdeRdr_tst.java b/100_core/tst/gplx/core/stores/GfoNdeRdr_tst.java new file mode 100644 index 000000000..111332010 --- /dev/null +++ b/100_core/tst/gplx/core/stores/GfoNdeRdr_tst.java @@ -0,0 +1,189 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores; import gplx.*; import gplx.core.*; +import org.junit.*; import gplx.core.gfo_ndes.*; +public class GfoNdeRdr_tst { + @Test public void Subs_leafs() { + root = + fx.root_ + ( fx.row_vals_(0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ); + tst_NdeVals(root, 0, 1, 2); + } + @Test public void Subs_ndes() { + root = + fx.root_ + ( leaf_("", 0) + , leaf_("", 1) + , leaf_("", 2) + ); + tst_NdeVals(root, 0, 1, 2); + } + @Test public void Subs_mix() { + root = + fx.root_ + ( leaf_("", 0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ); + tst_NdeVals(root, 0, 1, 2); + } + @Test public void Subs_rdr() { + root = + fx.root_ + ( fx.row_vals_(0) + ); + rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr rdr = rootRdr.Subs(); + Tfds.Eq_true(rdr.MoveNextPeer()); + Tfds.Eq(0, rdr.ReadAt(0)); + Tfds.Eq_false(rdr.MoveNextPeer()); + } + @Test public void MoveNextPeer_implicit() { + root = + fx.root_ + ( fx.csv_dat_ + ( fx.row_vals_(0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ) + ); + GfoNdeRdr rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr subsRdr = rootRdr.Subs(); // pos=-1; bof + DataRdr subRdr = subsRdr.Subs(); // MoveNextPeer not needed; implicitly moves to pos=0 + tst_RdrVals(subRdr, Object_.Ary(0, 1, 2)); + } + @Test public void MoveNextPeer_explicit() { + root = + fx.root_ + ( fx.csv_dat_ + ( fx.row_vals_(0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ) + ); + GfoNdeRdr rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr subsRdr = rootRdr.Subs(); // pos=-1; bof + Tfds.Eq_true(subsRdr.MoveNextPeer()); // explicitly moves to pos=0 + DataRdr subRdr = subsRdr.Subs(); + tst_RdrVals(subRdr, Object_.Ary(0, 1, 2)); + } + @Test public void Xpath_basic() { + root = fx.root_ + ( leaf_("root", 0) + , leaf_("root", 1) + , leaf_("root", 2) + ); + tst_Xpath_all(root, "root", 0, 1, 2); + } + @Test public void Xpath_nested() { + root = fx.root_ + ( fx.tbl_("owner" + , leaf_("root", 0) + , leaf_("root", 1) + , leaf_("root", 2) + )); + tst_Xpath_all(root, "owner/root", 0, 1, 2); + } + @Test public void Xpath_null() { + root = fx.root_ + ( leaf_("match", 0) + ); + rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr sub = rootRdr.Subs_byName("no_match"); + Tfds.Eq_false(sub.MoveNextPeer()); + } + @Test public void Xpath_moveFirst_basic() { + root = fx.root_ + ( leaf_("nde0", 0) + ); + tst_Xpath_first(root, "nde0", 0); + } + @Test public void Xpath_moveFirst_shallow() { + root = fx.root_ + ( leaf_("nde0", 0) + , leaf_("nde1", 1) + , leaf_("nde2", 2) + ); + tst_Xpath_first(root, "nde2", 2); + } + @Test public void Xpath_moveFirst_nested() { + root = fx.root_ + ( node_("nde0", Object_.Ary("0") + , leaf_("nde00", "00") + )); + tst_Xpath_first(root, "nde0", "0"); + tst_Xpath_first(root, "nde0/nde00", "00"); + } + @Test public void Xpath_moveFirst_nested_similarName() { + root = fx.root_ + ( node_("nde0", Object_.Ary("0") + , leaf_("nde00", "00") + ) + , node_("nde1", Object_.Ary("1") + , leaf_("nde00", "10") + )); + tst_Xpath_first(root, "nde1/nde00", "10"); + } + @Test public void Xpath_moveFirst_many() { + root = fx.root_ + ( leaf_("root", 0) + , leaf_("root", 1) + , leaf_("root", 2) + ); + tst_Xpath_first(root, "root", 0); // returns first + } + @Test public void Xpath_moveFirst_null() { + root = fx.root_ + ( leaf_("nde0", 0) + , leaf_("nde1", 1) + , leaf_("nde2", 2) + ); + rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr rdr = rootRdr.Subs_byName("nde3"); + Tfds.Eq_false(rdr.MoveNextPeer()); + } + + GfoNde leaf_(String name, Object... vals) {return GfoNde_.nde_(name, vals, GfoNde_.Ary_empty);} + GfoNde node_(String name, Object[] vals, GfoNde... subs) {return GfoNde_.nde_(name, vals, subs);} + void tst_NdeVals(GfoNde nde, Object... exptVals) { + DataRdr rdr = GfoNdeRdr_.root_parseNot_(nde); + tst_RdrVals(rdr.Subs(), exptVals); + } + void tst_RdrVals(DataRdr rdr, Object[] exptVals) { + int count = 0; + while (rdr.MoveNextPeer()) { + Object actl = rdr.ReadAt(0); + Tfds.Eq(actl, exptVals[count++]); + } + Tfds.Eq(count, exptVals.length); + } + void tst_Xpath_first(GfoNde root, String xpath, Object expt) { + DataRdr rdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr sel = rdr.Subs_byName_moveFirst(xpath); + Object actl = sel.ReadAt(0); + Tfds.Eq(actl, expt); + } + void tst_Xpath_all(GfoNde root, String xpath, Object... exptVals) { + DataRdr rdr = GfoNdeRdr_.root_parseNot_(root); + tst_RdrVals(rdr.Subs_byName(xpath), exptVals); + } + GfoNde root; DataRdr rootRdr; GfoNdeFxt fx = GfoNdeFxt.new_(); +} diff --git a/100_core/tst/gplx/core/stores/xmls/XmlDataRdr_tst.java b/100_core/tst/gplx/core/stores/xmls/XmlDataRdr_tst.java new file mode 100644 index 000000000..ffe4a851e --- /dev/null +++ b/100_core/tst/gplx/core/stores/xmls/XmlDataRdr_tst.java @@ -0,0 +1,102 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.stores.xmls; import gplx.*; import gplx.core.*; import gplx.core.stores.*; +import org.junit.*; +public class XmlDataRdr_tst { + @Test public void Read() { + DataRdr rdr = fx.rdr_(""); + Tfds.Eq(rdr.NameOfNode(), "title"); + Tfds.Eq(rdr.ReadStr("name"), "first"); + Tfds.Eq(rdr.ReadInt("id"), 1); + Tfds.Eq(rdr.ReadBool("profiled"), false); + } + @Test public void None() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find/>" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "no_nde", "no_atr"); + } + @Test public void One() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find id=\"f0\" />" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "find", "id", "f0"); + } + @Test public void One_IgnoreOthers() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find id=\"f0\" />" + , "<skip id=\"s0\" />" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "find", "id", "f0"); + } + @Test public void Many() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find id=\"f0\" />" + , "<find id=\"f1\" />" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "find", "id", "f0", "f1"); + } + @Test public void Nested() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<sub1>" + , "<find id=\"f0\" />" + , "<find id=\"f1\" />" + , "</sub1>" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "sub1/find", "id", "f0", "f1"); + } + @Test public void Nested_IgnoreOthers() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<sub1>" + , "<find id=\"f0\" />" + , "<skip id=\"s0\" />" + , "</sub1>" + , "<sub1>" + , "<find id=\"f1\" />" // NOTE: find across ndes + , "<skip id=\"s1\" />" + , "</sub1>" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "sub1/find", "id", "f0", "f1"); + } + XmlDataRdr_fxt fx = XmlDataRdr_fxt.new_(); +} +class XmlDataRdr_fxt { + public DataRdr rdr_(String... ary) {return XmlDataRdr_.text_(String_.Concat(ary));} + public void tst_Subs_ByName(DataRdr rdr, String xpath, String key, String... expdAry) { + DataRdr subRdr = rdr.Subs_byName(xpath); + List_adp list = List_adp_.New(); + while (subRdr.MoveNextPeer()) + list.Add(subRdr.Read(key)); + + String[] actlAry = list.To_str_ary(); + Tfds.Eq_ary(actlAry, expdAry); + } + public static XmlDataRdr_fxt new_() {return new XmlDataRdr_fxt();} XmlDataRdr_fxt() {} +} diff --git a/100_core/tst/gplx/core/stores/xmls/XmlDataWtr_tst.java b/100_core/tst/gplx/core/stores/xmls/XmlDataWtr_tst.java new file mode 100644 index 000000000..fada26334 --- /dev/null +++ b/100_core/tst/gplx/core/stores/xmls/XmlDataWtr_tst.java @@ -0,0 +1,95 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package gplx.core.stores.xmls; import gplx.*; import gplx.core.*; import gplx.core.stores.*; +import org.junit.*; +public class XmlDataWtr_tst { + @Before public void setup() { + wtr = XmlDataWtr.new_(); + } + @Test public void WriteNodeBgn() { + wtr.WriteNodeBgn("chapter"); + tst_XStr(wtr, "<chapter />", String_.CrLf); + } + @Test public void Attributes() { + wtr.WriteNodeBgn("chapter"); + wtr.WriteData("id", 1); + wtr.WriteData("name", "first"); + tst_XStr(wtr, "<chapter id=\"1\" name=\"first\" />", String_.CrLf); + } + @Test public void Subs() { + wtr.WriteNodeBgn("title"); + wtr.WriteNodeBgn("chapters"); + wtr.WriteNodeBgn("chapter"); + tst_XStr(wtr + , "<title>", String_.CrLf + , "<chapters>", String_.CrLf + , "<chapter />", String_.CrLf + , "</chapters>", String_.CrLf + , "", String_.CrLf + ); + } + @Test public void Subs_Iterate() { + wtr.WriteNodeBgn("titles"); + for (int title = 1; title <= 2; title++) { + wtr.WriteNodeBgn("title"); + wtr.WriteData("id", title); + wtr.WriteNodeBgn("chapters"); + wtr.WriteNodeEnd(); // chapters + wtr.WriteNodeEnd(); // title + } + wtr.WriteNodeEnd(); //titles + tst_XStr(wtr + , "", String_.CrLf + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "", String_.CrLf + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "", String_.CrLf + , "", String_.CrLf + ); + } + @Test public void Peers() { + wtr.WriteNodeBgn("title"); + wtr.WriteNodeBgn("chapters"); + wtr.WriteNodeEnd(); + wtr.WriteNodeBgn("audioStreams"); + tst_XStr(wtr + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "<audioStreams />", String_.CrLf + , "", String_.CrLf + ); + } + @Test public void AtrsWithNesting() { + wtr.WriteNodeBgn("title"); + wtr.WriteData("id", 1); + wtr.WriteData("name", "first"); + wtr.WriteNodeBgn("chapters"); + tst_XStr(wtr + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "", String_.CrLf + ); + } + void tst_XStr(XmlDataWtr wtr, String... parts) { + String expd = String_.Concat(parts); + Tfds.Eq(expd, wtr.To_str()); + } + XmlDataWtr wtr; +} diff --git a/110_gfml/.classpath b/110_gfml/.classpath new file mode 100644 index 000000000..d858d25a6 --- /dev/null +++ b/110_gfml/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java new file mode 100644 index 000000000..08ea30206 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java @@ -0,0 +1,34 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.gfml; import gplx.*; +import gplx.core.texts.*; /*CharStream*/ +public interface GfmlLxr extends Gfo_evt_itm { + String Key(); + String[] Hooks(); + GfmlTkn CmdTkn(); + void CmdTkn_set(GfmlTkn val); // needed for lxr pragma + GfmlTkn MakeTkn(CharStream stream, int hookLength); + GfmlLxr SubLxr(); + void SubLxr_Add(GfmlLxr... lexer); +} +class GfmlLxrRegy { + public int Count() {return hash.Count();} + public void Add(GfmlLxr lxr) {hash.Add(lxr.Key(), lxr);} + public GfmlLxr Get_by(String key) {return (GfmlLxr)hash.Get_by(key);} + Hash_adp hash = Hash_adp_.New(); +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java new file mode 100644 index 000000000..10f605c1e --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java @@ -0,0 +1,217 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.gfml; import gplx.*; +import gplx.core.strings.*; +import gplx.core.texts.*; /*CharStream*/ +public class GfmlLxr_ { + public static GfmlLxr general_(String key, GfmlTkn protoTkn) {return GfmlLxr_general.new_(key, protoTkn);} + public static GfmlLxr solo_(String key, GfmlTkn singletonTkn) {return GfmlLxr_singleton.new_(key, singletonTkn.Raw(), singletonTkn);} + public static GfmlLxr range_(String key, String[] ary, GfmlTkn protoTkn, boolean ignoreOutput) {return GfmlLxr_group.new_(key, ary, protoTkn, ignoreOutput);} + + @gplx.Internal protected static GfmlLxr symbol_(String key, String raw, String val, GfmlBldrCmd cmd) { + GfmlTkn tkn = GfmlTkn_.singleton_(key, raw, val, cmd); + return GfmlLxr_.solo_(key, tkn); + } + @gplx.Internal protected static GfmlLxr frame_(String key, GfmlFrame frame, String bgn, String end) {return GfmlLxr_frame.new_(key, frame, bgn, end, GfmlBldrCmd_pendingTkns_add.Instance, GfmlBldrCmd_frameEnd.data_());} + public static final GfmlLxr Null = new GfmlLxr_null(); + public static final String CmdTknChanged_evt = "Changed"; + public static GfmlLxr as_(Object obj) {return obj instanceof GfmlLxr ? (GfmlLxr)obj : null;} + public static GfmlLxr cast(Object obj) {try {return (GfmlLxr)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, GfmlLxr.class, obj);}} +} +class GfmlLxr_null implements GfmlLxr { + public String Key() {return "gfml.nullLxr";} + public Gfo_evt_mgr Evt_mgr() {if (evt_mgr == null) evt_mgr = new Gfo_evt_mgr(this); return evt_mgr;} Gfo_evt_mgr evt_mgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return Gfo_invk_.Rv_unhandled;} + public GfmlTkn CmdTkn() {return GfmlTkn_.Null;} public void CmdTkn_set(GfmlTkn val) {} + public String[] Hooks() {return String_.Ary_empty;} + public GfmlTkn MakeTkn(CharStream stream, int hookLength) {return GfmlTkn_.Null;} + public void SubLxr_Add(GfmlLxr... lexer) {} + public GfmlLxr SubLxr() {return this;} +} +class GfmlLxr_singleton implements GfmlLxr, Gfo_evt_itm { + public Gfo_evt_mgr Evt_mgr() {if (evt_mgr == null) evt_mgr = new Gfo_evt_mgr(this); return evt_mgr;} Gfo_evt_mgr evt_mgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return Gfo_invk_.Rv_unhandled;} + public String Key() {return key;} private String key; + public GfmlTkn CmdTkn() {return singletonTkn;} GfmlTkn singletonTkn; + public void CmdTkn_set(GfmlTkn val) { + String oldRaw = singletonTkn.Raw(); + singletonTkn = val; + hooks = String_.Ary(val.Raw()); + Gfo_evt_mgr_.Pub_vals(this, GfmlLxr_.CmdTknChanged_evt, Keyval_.new_("old", oldRaw), Keyval_.new_("new", val.Raw()), Keyval_.new_("lxr", this)); + } + public String[] Hooks() {return hooks;} private String[] hooks; + public GfmlTkn MakeTkn(CharStream stream, int hookLength) { + stream.MoveNextBy(hookLength); + return singletonTkn; + } + public GfmlLxr SubLxr() {return subLxr;} GfmlLxr subLxr; + public void SubLxr_Add(GfmlLxr... lexer) {subLxr.SubLxr_Add(lexer);} + public static GfmlLxr_singleton new_(String key, String hook, GfmlTkn singletonTkn) { + GfmlLxr_singleton rv = new GfmlLxr_singleton(); + rv.ctor_(key, hook, singletonTkn, GfmlLxr_.Null); + return rv; + } protected GfmlLxr_singleton() {} + @gplx.Internal protected void ctor_(String key, String hook, GfmlTkn singletonTkn, GfmlLxr subLxr) { + this.key = key; + this.hooks = String_.Ary(hook); + this.subLxr = subLxr; + this.singletonTkn = singletonTkn; + } +} +class GfmlLxr_group implements GfmlLxr { + public String Key() {return key;} private String key; + public Gfo_evt_mgr Evt_mgr() {if (evt_mgr == null) evt_mgr = new Gfo_evt_mgr(this); return evt_mgr;} Gfo_evt_mgr evt_mgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return Gfo_invk_.Rv_unhandled;} + public GfmlTkn CmdTkn() {return outputTkn;} public void CmdTkn_set(GfmlTkn val) {} GfmlTkn outputTkn; + public String[] Hooks() {return trie.Symbols();} + public GfmlTkn MakeTkn(CharStream stream, int hookLength) { + while (stream.AtMid()) { + if (!ignoreOutput) + sb.Add_mid(stream.Ary(), stream.Pos(), hookLength); + stream.MoveNextBy(hookLength); + + String found = String_.cast(trie.FindMatch(stream)); + if (found == null) break; + hookLength = trie.LastMatchCount; + } + if (ignoreOutput) return GfmlTkn_.IgnoreOutput; + String raw = sb.To_str_and_clear(); + return outputTkn.MakeNew(raw, raw); + } + public GfmlLxr SubLxr() {throw Err_sublxr();} + public void SubLxr_Add(GfmlLxr... lexer) {throw Err_sublxr();} + Err Err_sublxr() {return Err_.new_unimplemented_w_msg("group lxr does not have subLxrs", "key", key, "output_tkn", outputTkn.Raw()).Trace_ignore_add_1_();} + GfmlTrie trie = GfmlTrie.new_(); String_bldr sb = String_bldr_.new_(); boolean ignoreOutput; + public static GfmlLxr_group new_(String key, String[] hooks, GfmlTkn outputTkn, boolean ignoreOutput) { + GfmlLxr_group rv = new GfmlLxr_group(); + rv.key = key; + for (String hook : hooks) + rv.trie.Add(hook, hook); + rv.outputTkn = outputTkn; rv.ignoreOutput = ignoreOutput; + return rv; + } GfmlLxr_group() {} +} +class GfmlLxr_general implements GfmlLxr, Gfo_invk { + public Gfo_evt_mgr Evt_mgr() {if (evt_mgr == null) evt_mgr = new Gfo_evt_mgr(this); return evt_mgr;} Gfo_evt_mgr evt_mgr; + public String Key() {return key;} private String key; + public GfmlTkn CmdTkn() {return txtTkn;} public void CmdTkn_set(GfmlTkn val) {} GfmlTkn txtTkn; + public String[] Hooks() {return symTrie.Symbols();} + public GfmlTkn MakeTkn(CharStream stream, int firstTknLength) { + GfmlTkn rv = null; + if (symLxr != null) { // symLxr has something; produce + rv = MakeTkn_symLxr(stream); + if (rv != GfmlTkn_.IgnoreOutput) return rv; + } + while (stream.AtMid()) { // keep moving til (a) symChar or (b) endOfStream + Object result = symTrie.FindMatch(stream); + symTknLen = symTrie.LastMatchCount; + if (result == null) { // no match; must be txtChar; + txtBfr.Add(stream); + stream.MoveNext(); + } + else { // symChar + symLxr = (GfmlLxr)result; // set symLxr for next pass + if (txtBfr.Has()) // txtBfr has something: gen txtTkn + rv = txtBfr.MakeTkn(stream, txtTkn); + else { // txtBfr empty: gen symbol + rv = MakeTkn_symLxr(stream); + if (rv == GfmlTkn_.IgnoreOutput) continue; + } + return rv; + } + } + if (txtBfr.Has()) // endOfStream, but txtBfr has chars + return txtBfr.MakeTkn(stream, txtTkn); + return GfmlTkn_.EndOfStream; + } + public void SubLxr_Add(GfmlLxr... lxrs) { + for (GfmlLxr lxr : lxrs) { + for (String hook : lxr.Hooks()) + symTrie.Add(hook, lxr); + Gfo_evt_mgr_.Sub_same(lxr, GfmlLxr_.CmdTknChanged_evt, this); + } + } + public GfmlLxr SubLxr() {return this;} + GfmlTkn MakeTkn_symLxr(CharStream stream) { + GfmlLxr lexer = symLxr; symLxr = null; + int length = symTknLen; symTknLen = 0; + return lexer.MakeTkn(stream, length); + } + GfmlLxr_general_txtBfr txtBfr = new GfmlLxr_general_txtBfr(); GfmlTrie symTrie = GfmlTrie.new_(); GfmlLxr symLxr; int symTknLen; + @gplx.Internal protected static GfmlLxr_general new_(String key, GfmlTkn txtTkn) { + GfmlLxr_general rv = new GfmlLxr_general(); + rv.key = key; rv.txtTkn = txtTkn; + return rv; + } protected GfmlLxr_general() {} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, GfmlLxr_.CmdTknChanged_evt)) { + symTrie.Del(m.ReadStr("old")); + symTrie.Add(m.ReadStr("new"), m.CastObj("lxr")); + } + else return Gfo_invk_.Rv_unhandled; + return this; + } +} +class GfmlLxr_general_txtBfr { + public int Bgn = NullPos; + public int Len; + public boolean Has() {return Bgn != NullPos;} + public void Add(CharStream stream) { + if (Bgn == NullPos) Bgn = stream.Pos(); + Len++; + } static final int NullPos = -1; + public GfmlTkn MakeTkn(CharStream stream, GfmlTkn textTkn) { + String raw = String_.new_charAry_(stream.Ary(), Bgn, Len); + Bgn = -1; Len = 0; + return textTkn.MakeNew(raw, raw); + } +} +class GfmlLxr_frame extends GfmlLxr_singleton { GfmlFrame frame; GfmlLxr endLxr, txtLxr; + public void BgnRaw_set(String val) {// needed for lxr pragma + GfmlBldrCmd_frameBgn bgnCmd = GfmlBldrCmd_frameBgn.new_(frame, txtLxr); + GfmlTkn bgnTkn = GfmlTkn_.singleton_(this.Key() + "_bgn", val, GfmlTkn_.NullVal, bgnCmd); + this.CmdTkn_set(bgnTkn); + } + public void EndRaw_set(String val) {// needed for lxr pragma + GfmlBldrCmd_frameEnd endCmd = GfmlBldrCmd_frameEnd.data_(); + GfmlTkn endTkn = GfmlTkn_.singleton_(this.Key() + "_end", val, GfmlTkn_.NullVal, endCmd); + endLxr.CmdTkn_set(endTkn); + } + public static GfmlLxr new_(String key, GfmlFrame frame, String bgn, String end, GfmlBldrCmd txtCmd, GfmlBldrCmd endCmd) { + GfmlLxr_frame rv = new GfmlLxr_frame(); + GfmlTkn txtTkn = frame.FrameType() == GfmlFrame_.Type_comment + ? GfmlTkn_.valConst_(key + "_txt", GfmlTkn_.NullVal, txtCmd) + : GfmlTkn_.cmd_(key + "_txt", txtCmd) + ; + GfmlLxr txtLxr = GfmlLxr_.general_(key + "_txt", txtTkn); + + GfmlTkn bgnTkn = GfmlTkn_.singleton_(key + "_bgn", bgn, GfmlTkn_.NullVal, GfmlBldrCmd_frameBgn.new_(frame, txtLxr)); + rv.ctor_(key, bgn, bgnTkn, txtLxr); + + GfmlTkn endTkn = GfmlTkn_.singleton_(key + "_end", end, GfmlTkn_.NullVal, endCmd); + GfmlLxr endLxr = GfmlLxr_.solo_(key + "_end", endTkn); + rv.SubLxr_Add(endLxr); + + rv.frame = frame; + rv.endLxr = endLxr; + rv.txtLxr = txtLxr; + return rv; + } GfmlLxr_frame() {} + public static GfmlLxr_frame as_(Object obj) {return obj instanceof GfmlLxr_frame ? (GfmlLxr_frame)obj : null;} + public static GfmlLxr_frame cast(Object obj) {try {return (GfmlLxr_frame)obj;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, GfmlLxr_frame.class, obj);}} +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java new file mode 100644 index 000000000..d9a19a05e --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java @@ -0,0 +1,29 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.gfml; import gplx.*; +public interface GfmlObj { + int ObjType(); +} +class GfmlObj_ { + public static final int + Type_tkn = 1 + , Type_atr = 2 + , Type_nde = 3 + , Type_prg = 4 + ; +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlObjList.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlObjList.java new file mode 100644 index 000000000..12f8d0e82 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlObjList.java @@ -0,0 +1,25 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.gfml; import gplx.*; +public class GfmlObjList extends List_adp_base { + @gplx.New public GfmlObj Get_at(int idx) {return (GfmlObj)Get_at_base(idx);} + public void Add(GfmlObj tkn) {Add_base(tkn);} + public void Add_at(GfmlObj tkn, int idx) {super.AddAt_base(idx, tkn);} + public void Del(GfmlObj tkn) {Del_base(tkn);} + public static GfmlObjList new_() {return new GfmlObjList();} GfmlObjList() {} +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java new file mode 100644 index 000000000..dd431b5d4 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java @@ -0,0 +1,46 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.gfml; import gplx.*; +import gplx.core.strings.*; +public interface GfmlTkn extends GfmlObj { + String TknType(); + String Raw(); + String Val(); + GfmlTkn[] SubTkns(); + GfmlBldrCmd Cmd_of_Tkn(); + GfmlTkn MakeNew(String raw, String val); +} +class GfmlTknAry_ { + public static final GfmlTkn[] Empty = new GfmlTkn[0]; + public static GfmlTkn[] ary_(GfmlTkn... ary) {return ary;} + @gplx.Internal protected static String XtoRaw(GfmlTkn[] ary) { + String_bldr sb = String_bldr_.new_(); + for (GfmlTkn tkn : ary) + sb.Add(tkn.Raw()); + return sb.To_str(); + } + @gplx.Internal protected static String XtoVal(GfmlTkn[] ary) {return XtoVal(ary, 0, ary.length);} + static String XtoVal(GfmlTkn[] ary, int bgn, int end) { + String_bldr sb = String_bldr_.new_(); + for (int i = bgn; i < end; i++) { + GfmlTkn tkn = ary[i]; + sb.Add(tkn.Val()); + } + return sb.To_str(); + } +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java new file mode 100644 index 000000000..24cffa73c --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.gfml; import gplx.*; +public class GfmlTkn_ { + @gplx.Internal protected static final String NullRaw = "", NullVal = ""; static final String Type_base = "gfml.baseTkn"; + @gplx.Internal protected static final GfmlTkn + Null = new GfmlTkn_base().ctor_GfmlTkn_base("gfml.nullTkn", NullRaw, NullVal, GfmlBldrCmd_.Null) + , EndOfStream = GfmlTkn_.raw_("<>") + , IgnoreOutput = GfmlTkn_.raw_("<>"); + public static GfmlTkn as_(Object obj) {return obj instanceof GfmlTkn ? (GfmlTkn)obj : null;} + @gplx.Internal protected static GfmlTkn new_(String raw, String val) {return new GfmlTkn_base().ctor_GfmlTkn_base(Type_base, raw, val, GfmlBldrCmd_.Null);} + public static GfmlTkn raw_(String raw) {return new GfmlTkn_base().ctor_GfmlTkn_base(Type_base, raw, raw, GfmlBldrCmd_.Null);} + @gplx.Internal protected static GfmlTkn val_(String val) {return new GfmlTkn_base().ctor_GfmlTkn_base(Type_base, NullRaw, val, GfmlBldrCmd_.Null);} + @gplx.Internal protected static GfmlTkn cmd_(String tknType, GfmlBldrCmd cmd) {return new GfmlTkn_base().ctor_GfmlTkn_base(tknType, NullRaw, NullVal, cmd);} + @gplx.Internal protected static GfmlTkn valConst_(String tknType, String val, GfmlBldrCmd cmd) {return new GfmlTkn_valConst().ctor_GfmlTkn_base(tknType, GfmlTkn_.NullRaw, val, cmd);} + @gplx.Internal protected static GfmlTkn singleton_(String tknType, String raw, String val, GfmlBldrCmd cmd) {return new GfmlTkn_singleton().ctor_GfmlTkn_base(tknType, raw, val, cmd);} + @gplx.Internal protected static GfmlTkn composite_(String tknType, GfmlTkn[] ary) {return new GfmlTkn_composite(tknType, ary);} + @gplx.Internal protected static GfmlTkn composite_list_(String tknType, GfmlObjList list) { + GfmlTkn[] ary = new GfmlTkn[list.Count()]; + for (int i = 0; i < list.Count(); i++) + ary[i] = (GfmlTkn)list.Get_at(i); + return GfmlTkn_.composite_(tknType, ary); + } +} +class GfmlTkn_base implements GfmlTkn { + public int ObjType() {return GfmlObj_.Type_tkn;} + public String TknType() {return tknType;} private String tknType; + public String Raw() {return raw;} private String raw; + public String Val() {return val;} private String val; + public GfmlBldrCmd Cmd_of_Tkn() {return cmd;} GfmlBldrCmd cmd; + public GfmlTkn[] SubTkns() {return GfmlTknAry_.Empty;} + @gplx.Virtual public GfmlTkn MakeNew(String rawNew, String valNew) {return new GfmlTkn_base().ctor_GfmlTkn_base(tknType, rawNew, valNew, cmd);} + @gplx.Internal protected GfmlTkn_base ctor_GfmlTkn_base(String tknType, String raw, String val, GfmlBldrCmd cmd) {this.tknType = tknType; this.raw = raw; this.val = val; this.cmd = cmd; return this;} +} +class GfmlTkn_valConst extends GfmlTkn_base { + @Override public GfmlTkn MakeNew(String rawNew, String valNew) {return new GfmlTkn_base().ctor_GfmlTkn_base(this.TknType(), rawNew, this.Val(), this.Cmd_of_Tkn());} +} +class GfmlTkn_singleton extends GfmlTkn_base { + @Override public GfmlTkn MakeNew(String rawNew, String valNew) {return this;} +} +class GfmlTkn_composite implements GfmlTkn { + public int ObjType() {return GfmlObj_.Type_tkn;} + public String TknType() {return tknType;} private String tknType; + public String Raw() {return GfmlTknAry_.XtoRaw(ary);} + public String Val() {return GfmlTknAry_.XtoVal(ary);} + public GfmlBldrCmd Cmd_of_Tkn() {return GfmlBldrCmd_.Null;} + public GfmlTkn[] SubTkns() {return ary;} GfmlTkn[] ary; + public GfmlTkn MakeNew(String rawNew, String valNew) {throw Err_.new_unimplemented_w_msg(".MakeNew cannot be invoked on GfmlTkn_composite (raw is available, but not val)", "tknType", tknType, "rawNew", rawNew, "valNew", valNew);} + @gplx.Internal protected GfmlTkn_composite(String tknType, GfmlTkn[] ary) {this.tknType = tknType; this.ary = ary;} +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java new file mode 100644 index 000000000..5c0210251 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java @@ -0,0 +1,114 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.gfml; import gplx.*; +import gplx.core.texts.*; /*CharStream*/ +public class GfmlTrie { + public String[] Symbols() { + String[] rv = new String[symbols.Count()]; + for (int i = 0; i < rv.length; i++) + rv[i] = String_.cast(symbols.Get_at(i)); + return rv; + } Ordered_hash symbols = Ordered_hash_.New(); + public int LastMatchCount; // PERF: prop is faster than method + public Object FindMatch(CharStream stream) { + Object result = null; int moveCount = 0; LastMatchCount = 0; + IntObjHash_base link = rootLink; + while (stream.AtMid()) { + Object found = link.Get_by(stream.Cur()); + if (found == null) break; // found is null; can happen for false matches; ex: 2&Dl986+HT#}{z&$uTKqafZg`^C`i_~^cA-3}!eBqlzJHFdeSQ0GBa``! z;Oti2*ww>D*A&Mli%kkq$c zvCihyYqZwPK>0}%W5Gg`cWwF$bk>tJsZjcSaJXvsrS71yX|J!+L%eBkXGNed80@z6 z0i~?Te@dmZB8~<1!M~A8_`H8{)WE5wo4Yed$EeHVPUPouN9s0C%dL{oS%;lOeTYe? z4!+5MN@)|Mi}O~S5BR}l47$FH0boBXXFRK{* z4$w-d;>;@3wYcSC-$}Plv4+*$pE;Q%r$TEk`|Igu#fog!(Rbq6cWS50vaM01)tR-2x{_ISE>Wyk@BwJT6Z7bQiHwdfBgsZ)Ga z*O~d^)t4>{kaZXE+jGsB&K_RhK-U|-i+I~Te? zuLi{8B9T7F7@G2QJK*DHnQVevdM=S)^22st&s?9hz8oHsWI?<9a+@kNtTR?JHMse* zTHPiy{)eJ74~O!7+ju3kF_ogMDN7}deP>9Lh{{i78?xuiWH*?Zq>^lvvYR1E_C32H zTe6IO>?6wz2E&XQW`Di^KL0$&bKK{B9p`;L_w_kHNz#{DNBjZ1ovy%|N$tOp36RqjBquO%Y;L z$V94PDVVj=m*Jo(lXgR*O8s+^T9mrhx_3(NBr9Jy01F%Pw47-qb*8&U>J$>V=@Spa z-HAVBN$>2P9h?r_8;su4MG7=cuPxASDB)~;-HXP4DtiQ(L7N&TotN5zo$J8zg~L`1 zv=`bS{pqucuR$)8C$E1)Z$xm6NswpJ6WQ zFNZBu5EzdIwyhn1Nr}^j=)M+Py(!E8y9LkiD*E60Z6<5Z`%f<;E58;gp8e8F<*x-i zV5a=N>8TjU@+Lp1o3~Oy;_vztsk{6A6n`(7m7hNIeS_wzt=5?~#6|*LcyB;za*8^$ z$1yJMYps$Okn>+Bm&T7(Wx^)`$jiRit>jjB>?GU%VEJ_Yl13`fOs!N||9a%4)*ffH zx>;LivhFuPV}G=1i2jVEaI+aaqvW*r_umJP71U9y)8_>dxKvTUX1TT5LGy*C3f!zSj z$zcWMqc?dl{>a~uZ?6Hgzxlvhr7xRD=C|+(V`1h9Z9>;O{q*soRv)Q?$X_AyG+NZ- za2fWK`+fh!y!NM_p&E-><6&c#DZY%JEbGnUAfR5ROU-?^uBe!946&dpu`^gcu}rQ1 zl5_3%!S-j>1%R7EIyd-(C4<7~Vy;uQDUoALscGkBgYcQ{w`~~yMZFO?pKH2jjx?G)tR9m<2Z2Am9m<0!qv_ zeEm`4H{ZF+{gCSHZO_nF+KZ6L;A>_JXE_hZHjot7NZ$vyzEd@pi4Qp~`1O}%=5r=_ zOpFao*Y4|?YE|H6YUF0w>2=f#wx{0Lo2bjJ?C;%G%cu$eJ!Zj-c^N*I_~SRcsW@cz zX_?PNq?dcIv}ySk>-=LS<^nTF2$kf+_Mj#4T+A>=ifr6;94>JTq@~)r>;;Y36f*Ph zL#7Y2MOtzquk^2eCO@*BSk`?45WSH0E-g!J11mXMLeX<8b>r+L;Tc(eqhCFRFYMh2 zt(|M_`b+ifOZdiP_S)$$|E3yz!`mMv`3L6xwT)SAKbrf;!8eAeTL&A2v!6fMU z!00vW8d3C6gPWUkjs$#n&y@=fjLde7Y*8e1H=8voDHB zGjO$th4u>iU8;4io4WEcq`ZcrtJW24_~{-5^XP9=1iXi8P+$w6FT zt&+a*R8v1yq#^BhUo=e$uP3*3E#1%P+f{~YNC$;!*&Pz=>1(=IuH7CtL9}gD1v79Zv~W3mn#RWE#=>!yl8~_&c&F&Q~ts*R-P+aj_594ZHXQ zGj~z!a}afD*5O>WLztN66dDKj>Zo&f=x=(2PTOi_=4cyukB{8STXtrvdY;>F4P{-= z3RUm0*S#5@H(_6qJAXUhO^vmA@*Ox+UpAHrR?X07f&-weg$nt7=%IRx6Qk^eUf{a=s3|RhVgK;;4F7iL%&ta zbt9!CVa~aRK&Pgpq@EW`q7}$u~ogI7q;h_LRM62Q# zBa5|kcZyj2WeCKzueEjzZy)a7xsQuNx3Bs55pBpZKd) z(?P0u0($MshJmf!`2bRd@8d<1?NHp}*y7qAqHxlamb#$B< zD)xkQ3YmUCP}gq0l{0kZice7Zi~l+97Xx!P6tfFg7WBk_(e~a8I1)p5t(l1wy1TM0 zh5Yw-)5n{$lwS-4K*ZoM&2s-<{=f}&DU{7^(U+0`Jzu<2cz29AO<16cH=`7oAu@ZO zV!UQU^@9G@fRhTO(&k{#{2_`)$(OS8b&}uLgw>#1oRZw{uJ-Ar>w$1%a=t*nf9*g2 z^4pOcL08=>W73f9*pv1J{x#+5@AwTW!}YcPn@)8EP5)$z0D1YJiQvYcz@0?XXmcGm zU~BJNr6(yfu?nlmn^EDa>f+R~%rG~pA;q>SAIG$wWBI)D3Y}u3dk@_f1eUL=?;TI; z)mOfiVQ`HbJR05kS=Ep5D-*}qe(y$r@na$`sOIMu{oR+Y-}JFhq8-e{%mA3ll2Nk% zH85)$rk(+Y%FpQeJRj>Tns25R=>J8N493}`ABxkHC&vuhYTLHdL;n&*il!&dp^ter zJ-jI{Yloi&wJ|4ko6XI)K|+$`M-ELd38iV~tr|Bf;zO-c_#Ay<|N3Uo*UA8}^(wUo z%xd-oYV;|s$eOQy0<-QYJL}d|4mPcT#S1jn%#KiQJr!qL5Np#olj)zaoW34T>N~W6 z=@*Q_qo@)&|3n+?yTYXCG-UqnHKCkz(baF%^t#vKn@3k^RzZPS><#Nmq})=eQyNm!yvFBj*VGwc0zt({uFp#9hwhvQys7>9@9z+e5d_O&+un$NCZ z%oNthbDc2i>=6CDKC$uHEuLRLxCZG~docICcEl^>bjU;BJK#Yy!3p-Qc|W0p5vG{j zy^cPWWm~YL?EC9q%e&0qAL330QMOreAuxAX?n}lC-5he2l!Lm!>@JP?PF`$}t;?^u z-CF;1{YgkdDC65Jei6R%3~*4?OYTVH(2(k&nM z?pUUDOz?{Rpw>mo)jra41O--@tY_54F$lFy+o!Uh<}$S@*xqdnW-1u)LJJ?UlLG#` zwKZwlBubsDuKEdo%puby-Tby`_H@R%LM(jsef&|$?hb+0oC)LNp04y?bn!xKwQhLN z-x+J`aw*51jZSF_T2dbCZV4vnSbP1Quw4*FaV{Lj1WbvXv21?%?0HDl5~ajU^%hCt zVPly8mw=t0z>|%MKw#qJM4QGl=f{P%!>4?Ch7IO!Vj*0hp5azmq@M;|4B z;nIHJ!&4IA5&XeU#dE43DO9UruQ=n2mcElIi#?u#nXW#N96b;M>W0413>W~m6248o ze$~Op)acq`c%Ii-H#YQMo&Sb92`sTreTs$#aKd~@t<;=IKHU~Ux;JO63S%v+%D-Et zyX*8+^wx~tcAm$^r9|KU?MaU_?*mR=T2aRhH2m{v9Q)Ws*WDKwYPdLK!+pRtITaLI z>~IeOZZL4v@t~wJF0S+68yZsz9di6{@2XB0hfuTMNWsM?e&Lh%chvaGC=CHJiQ7L~u8*6uz(#9o*;9*=k4l400ahlBE5hcfYihW1VqAYUR3Z`9K4=2u2h$*?wzp{hC)zmq*EBuV^S^ zN;8js`W=dgVei_@Y&V@V+15f6FRpWTj3f>gb-; zjj}?4VYmf5fnP^E8LdZ|9F6ciM%6QiLKFl%H}xqub{1^`m&3 zs@~wmj9a_=d^j?Tm`EdH7=Kd&bV1qBWZ4@oKRj0PdASV3;#UJ*K171pTS*U#5VRNI z)U0*FS3@!RVV106-FY8_DuA-S563aL*g`+}^{)ET)!y0d8UQ&N2WqBoc_p11l&8EO ziOR&C+o-YD&6E;kO#KCvp! zOlb@q3Q%J(WxUq=r!b;`Q(}o12vR$`;W890c=>zulEO zc8DU)qdP>uP^D!I=tc@tW^ImILy`>l!7EZEFN1)VEn@wrN!3qlggG~A0!6z^V$V6Ovclvc|+o@<2}AuHtS)kN+xc{<1FOu8s26UD#R^brp^%IL_z zgJf6LpEJaURr9^!`WdDSa&qs68YeVN*StUW*hIhkB+yV-f4SbR#A`I7{0MC5K)T{p z)7=r4zx&+8&TRBGX%It>f= zn7owcYQezmBtY+rQL?OIA@v8n&9FXw2V=^_lbI(wKl`*@9!_`Rzz-?%=!M%VLki3Fjdw7Q_3G4tq_7B|yc8Za{H*`rJ z=WIoZ{Yb47MX=u#lPR`gg>+>@kjk>Ah`0SwC%_Lwv=Zet@9)+(($1lqvY!Dy z!<{ixCG@)(XAVb0Cn*)c$0nc31WBwF6tOtQr~(!hN4Wq4+vYl-reg!@(;N7!>Qe_= zten~8*>@npnp>#JWnQ(=vj~J8`C}^L7$eMgj4VTCQB5+X`1ZzhIU(Qv(?Mka6CQ(@ zH4jMq0S~1|WunjiymK9mRBc`(e+(HLJ7`jofyvb@?6 z{gnO$-yNZaUuU^3f4g??GjFOazuzl))Wf+S-R39Fp}EMQIg^DxY`-rWtQYm6J@SA~vVUJP=Qg<>8ExtNkODmBH`XQPm8!~#5gndP0Jn<&917rsJZs_O z>Zzyfj^Tf|wA@Hd#R=9zZXwv0VBe`9wO$Olz$2gk-isYG9J61;xF*2b_|%x%+;Fqm z&Oe<&7bRCA70-J(;n9axx3)!euO!Gq93t{4r?*xQ3ho5|aQXvJWDD6lpBn!fHyg(H z;&Uem*kDRY-fvgoFfI^A$TE_VvAc+O$Mi9Kv)xLPL6FnenfF-7fpTrFLf#(~1_7@a zQQLPY*iQ{cLR<=MZUBc$%m|NEA7=NahlmgJ#9z|o3BK%x(d3c%y{Ax(R&u?(EYCAP zSgQMndDozJ(Y8x?jCDP?_1-QBf)<)>^G>{l#>P8?jwL`dv5e4HHP-*o-M zXAc)0UT$L1b~rv9CId=I?ro0s;aKI$CV&)4h}U>pLWcO?<6XKFtM zmZZL)%s*B8v5FvrL{|wZB98P#9&+xi4m&QYh)>iRUr(n=o(+UX4e%b#D<^{?&oqKL zTq*=D9)ipvn|oJ$^VHl?!I)#T|(b52UmflXWa%oE41?H!Mf(E4;aj7=k^RVMGESr-9;hv zmnMEw%H0|AyQ*%V>8l{YB@#*)$)Zxy;$m`M1>P?8n0(eQ)wM^tN`~>3#kTa8?ep6s zOTB8o_&}?H+Hc}!Q+A1erzW`czJZA7uGTI4DXZ!9P8>=~8G1M7Rgc6f7+dN|R=e-$ z?R$D(K%szIaZGgncUvbeh_w~Y(I|^u30qZ$M#w;RVkHL=M{DJ400n}37it8Xj;nqD ze5i@~(Wvu4Sl<+#Vu<>+Xj|$&c>|Km9pC=ZUvt7+*OU9DOEs<5rT0lj$ddIiw=Esa zkvJ3zbfYc-hio}M{~et5gEHvK?yt!TD)EjD_iuax9&2vFH|R~#ODw+jFq*=jE(y$4kc|2Ei+ zk;$}Ls?AAn5RYSOD?#tNOQB_w@hMU7uE>GEtb{a(f1{fjB2<|3cQGM7AepLjxcyq> zvY~N0?5yc^?z`vq$jz{{{eS!X0}fO58{I20bukTzvTpTpQXa5(I7HZWi5YG zPvO6_6bQG-e#nEwW?aU0k?K5`l)4Ohm7qv#Gq^+wa9_|Hu8?U(D;k{H>E(A;*&ThF z3ySF@UERIzD`)ktt25EzQWz*b1%~{RLSYPx`uRKH*{@b{6);>Y+24)|u2ENQN-jYbFhnZ)<)XvAM zxo)mJ8g?t}#W~BtS?uZRx2)sFrX0?CSC=A)2iChHGS4Rp{I)7`Z2r$?R*K%Y_l7^R z+i?R4`(F(Qt#*6OJa7FY$s1Ydc>&fCA#c#Q+b-lcaXJ6FlRNu3)#sOSpP@2Y+9DqT zG`fHg@G4U`5l3IXkFlRho9^30r$9G`MKf`NYXSJi`;NEc`f6l%-yHYE#U4;F8Bv#> zV=e|C^fJE*Z+1TF8T!b^{qs6lrJ%^kK+_)N$EXB#3$6@vMj@TO%H0Y}+riZfrGU)v zMbQa4m+#T)&^y>!ha2eDn;xttwcJk`ESh8cN)CrtCvlh3r?WS9QI30jb3)A%d6gd6 znL*z>Hhc!pI-$)~Nsdd|=VkL+EKra9MBTQ4$16UP@|19W$hzDEW}If!We{2g_k>iz z9}n6sMP zw3tiC#{PMt=nDxFj!fr|cDQM*=a;5Td+mH;Mtb=Fv;?;F8XZ5FGE}3)Z+Jcbp?+ zgPapqrf+U8){8l1z!s>6IT?D&HsK3LJm+5=3nn{ff-d8Urw0tK#6c2tjVOIk%xht? zu;ga$Dw9WIa3$m;v}V&l5iB^Krj0ULnBAUw?KJ2{d)r5!RQa*+r^)5Wp%5rF66vtk zd%67?>=V^V-u;_GpI{4M3-PRM#K}V;JVbxtHrA#yq?gy4k0C&|UPD3W(6y(>r0(lM zb%fAYnT=r?ErVxr!L+d`?LCyP;$~W%*W|pVAw-Fp_Z`usLeMeO{Zaq1UOTG~C9^e~ z_zW!)jqFfN!1WD8xu1yR%^1-3{Y(monTA}YU$ni1PRfpD|Jf8X;48-d){6MEDgPPD z;;AXu$Ut4TCWpO3@_p~tU zo(NuCJR@u=gDTReQ0_id@;PK$$del(F|}nn5JZh3i+j$zbu2uT|Ae?)#j9-W{n~$0 zrm~7H*yoaC2!o^Z{hI_yz8C%3z-!P8fVNSV<8?3QJr1teslsH?Wl*U4@W=x-@FB_}voGHIE&00S3btaX zE6qO|NwUax^pgibWD0d}0`MvT~WvX=HXr4W3>T6QHoB3r&7;pLj(#(U)76v?V zx)ZAG6@^Fga@#9>p)P=BKnm>ajUeZBSQ*`04%W(-Ds8jHr{WrF3C(HD`wzdyTZy4|ba*ar?78 zXaOp(EL|D$)1Vyh!|UpVbiI{&!;nh|_|>&O_XYktG_0o*EhRgi)v;oJmYLg7|5i(W z8T1hbb&!L^9+)g2Ixs*wb(}j?r@Tub45Jj&8;%#nid<$fZ=|KeY(8t+{hl)WY&)HC zIeDd}=OR8zp$~AwYZ4s$>h@eeE~!wELh`*{dlvAleG{0n+wdtP8E>TPZ%dHE@`H9} zy#(YC2?@CKJ%U1L`}jDXj}6aMf-FB|r-4(taNA|};kY3|PjFVY6{52%BaW^2`+zGm zL{E*Qe54iuDGPjomNWrMn`j_>C0H3jk5nPTRKUH;aILtMyUGE4Dv+DXbPX9K{_m94 zQoXp>WX*SuSbC6vI~Xr51R8%sjX%5^oP0nxFU{;P`rGGn?GNt(f3`Au9yTB!aM4E# zpO(<`E_(nA;PeMsul{maM&4VxID4&%b@PA+br-XF^A_+8=;{3p1mYIHM8~yFV+TQW zX*%@+{V-LBf$PU@%@XgAN-dXq(s&fQ=?_|FS_jgpau-KP` zRX-Ia9RI-}BDN?4cLdRu%9_@T^-m||#L&AC#P6|m{c0uDRG$cT2NojwQaeuna%4U| z+g#UDM9np9ipTz@W$>F_z{$s+AbKIPwaCtrr z*)o9M9}=lyHr)|_bo@CFy!VR^pK*VofcSXX;K+>QF8cKh7ya+BUde7&TKsxMzg{9; z!}mf_M2fvj+uhhnikbipyM1LIaYkL4d*>AVHd(j>2u}xnlW?*uc~T0}Jx`P;=G!jV z8Jy)@S1KZ8@hc&)%8WL*5+!ttI3*5eH^obts9A?!3YEasdtIAPl73*<_G+ClNmK%P zp42b5x`Q6v>12PG+L7PxAnOChR@KlkhlQK_QHvYjnhw0#PIhOtsrRys8iVN~5zA!6 zW*jy6IOm(&aFTRzByUnLYic{)YO)4?zLe{Iiro@6`<(82)6V$}d#mI`e?=@qno`=R zh{uY!8C*6s6RJYW>aUS49`&-6?q^H0UuKH#3H{Jx&$_hTyNOR}Y3uo9XN7QMG!6}R zM|n6jp%7Au)LV!c>c6%TRs?6akWF^|$i zq27DMb@-RO`_h{7Jf*$AJCkNqp!fedvNR!+N#NV zzDJm>=xqq^Y$fY?m_2-2_7*85^)@DPLJ2Oh#)%T&EadqD;yHl*hz5^mZDSyB>=pzz z&-dw`()&Qq>)wNHbqK1pOV!KM7o2}RLR!|s{k(BINgw_P<+E{V3zopA=v@cK6D?EZB?u$}voLvpH z3ti4oHI8f~W&@p7#3{|V?x@e0nxgX{2wP{yO6wSnaB@m&GzkoR#eQavO->WTi-Z&~ z`{qi*4Zbpm;kmwilW6MMGBp1HxN#s#x#27k;Li0#0nm>+NZti+FQxe@N`VT^pYb*Mj1i%E^WP+2SCSmPxD=qL+fBwq zj8L_gj~<9k1VaVCpKh<{k(FkWOm-aoRNJBA#tyClhJOC5(VTknYVH6~EyJGhR~ z{XCsnV{b#2a$3`^$Vy=`;FZ5~`x}Y_Tx&C|Rjg&DWKn0tediB0H&W|w6|4ffJ6P4J zG>CZ~*Q>qh){z5~VFXQ;-FtlU8E@s#RoZtm{(c7~V$RokWpKnfqV#S#pU40Zet(wm z?RqJ9)5YXL+gW^tf3r6ehd+`Ek$ANlAB_N>+q{&3OKHa*e4tve-uD}mcWXNYHCywr z%do=doF^A&q~6B{{I4q^KcLdzM&%o`Y%;R6@R}-Wn*)?&?m`k0?ta)ocZg43<0h?A zUxAKK7M}MG3*0sf5SipErA=~!B~zsa4&3H_t5~l~YSE7?k57URiws`Uua zFS8|k+jjqopj&q0s#zOO`E)u~!<7^Ck)$b3+}Hd@MMcwR>V))x1WrAd%7rJx2uJPU z^#)x+i*4$Q@?A=7sKL}$l1sfAC%s(cz-fM+Y@zX+oC0yXgBp#Om;F=^C(Di@`g|bb^eK+ z)x(_v_Q%`#%4ng|us`r7o>F2^o}N3mfA}m#Xn&Ntjt3B{hwMfrHXecmmhMa1arxGq zuSpNRbdf6Sro=4eq!#-;L1p8tJ9}$Clh6pb~CXbPGKAmi-!B ze$ckk>ecNckB!ceA43TwHw6$l1%m~pJ(mhq6e)jFe;$jP;X9b_A+^)FzWqs8P~Wzjdp$jr18psKk8x> z*rO-875AR*dpYtFANkn51wlsgv`r~O?n_M;0FSmVL=pFO9FY%9oAg9nTW@52K$#1& z7|wQ>zu~taMhn*NCqoTmw+SbU)47dtzie?@rKZVoX-RKj0@T&?v6Ba?fR3ipkJ|)B zrIcFZMg8(-M?P*!9FHFMb;!44sqzG0op!w}_2i_^NTM5@slUfkx8xz-V43gN2mDP( ztcyq-9$qDh86Pc~fNL&j!?bB&I!}NM1`y2cOKN8@1;5Sq3vEIt4Vz%Lo$%h%a+V=d zJ%>GRo$cM5l9c!*CDe+n!JC73N~vuXW+(8P(<95~$B6kVYakh*8sSzd_I)08JO;hX zl*Xq(H3V6+NKHJ;_tKdYNGujvu@D9w#Ynve3oix?l*os%AK+G;n`ySes@&h zLRYEcK|xV4Is^%Ty8s;}1BY%O$EUfZgYP4BLj{Q#*9}*pse&lEg&{K~?(IEIvSW$W zbM}jZU{Edm46QMfW~JhSEVH{e+xjCgonJv27gsvAT*dP7(y;Z6g~`zKqI#~e=W761;l)UloYj*hDHj{JOdANKJ+uAYze zZ#e;^7p$L3;$<$b=*^huzOUFAA?~LOq$9@S%42z3t2jT8MIdm&+Nq@8+W7Tjw-Bc| zhR3IYYgNMikpg*G_2zc8&Kb_dXJIH_=5QlONK>Jmb)zHQYAE2i%p&ut{30{y4Ltn> zr8FT9AsmgWTsbn&9lOHOON|3T#@fj~11%W<&cvCWin}ktvUb356?nt3uCWIgSA7`- z{@OwG2SB^ZD`dQf9b6y<@`yhmlCn_Csf7*WPOU+u=g;&j)aS2mO}<7*KA7buj-=yW z+{UL+(sx}eonP`n?JDY+iF7AXpXPwWF1b5!27b>d>Ic6Nst@s&zWmSRMsuFz85Qa6 z74tMOGMcUeX#&;9K>dCnX#x$rWnVz_An-0Y(4!cd>O)tQ{$sI2+q-RWio&m-kNqlF zl#bXs;>2y{|Ht%H-HjH1KWiIWuoc>yv(sWQpmPF0wrs%_zxt&(@nCiHsVdkq)tP=Ro0NSru8CUii z&wsmzwZ8LwCUCK0i$CD|^9WePAw4cy3G0n28v(%K0ZxiSc>-$<%H7U5< z1w@O~eX{eSkf7OF6&jg(FX{LW+ z2Hiwx>844{8bOCQ;6NaHuZ8Y?3SYl&21>j7#cL~a2E4&XMwPBH)ZVn@7WA%#bm?g0 z3z^Y5@^H?*0V!btF_SM#Qo#+U8Uq^zOntBleDO8zVf4i<)$Z>3P{Z^>=hDk zt;A^>F2Ol1?h5$1d<_^Xycty>S0*n+Zh$L89g`{{bw2HrL9M4nq54st8DOF0z z5XmIE46s{lNgJ5Q|GA~eN!zaNQyi+|kP(i!#&&$mUxmD86wC%6(bbNxN->CUZ<`*W zW)!}s7V428F*4Gt7oX}B^xMD@=c70G4!Eb`BacGEhZO)E9qX9L4Z~-bag5qM%aGpD?YVe}H#5N(ipLJ@=l~dsVyPt=q4mjGv9jZW))*p&NIwyesNUm?vBoVCW$I1<9%+aoYP}xjn%&f{B}=^EI|Xj* zJpiTYxam_fYvIzRO!u=OX+*D4@ds+HEBeNc zUOq9v5FuX@QK1*aODB&br*T%!N13;HeA;ey8BZGNy;<4y4DKdt=Au%ds*QsE1gHFd zoaq^+0eT&7cbY!in$FtV&h;8l*BP2T!bF>04rYa_?4{`TRw2(l;>KUWZMsS8qYG<0 z$WkW3Fvd$e&aOr&>L5Br+Uh@n6!}!!lT^gJNzyU;<-Qj2J&7>k_+H)2PK)Yto9` ziJo$!e~Gt4W|OA8U(qvwYX`-A10wfGJFqw~Lv%73Y3hTJi@J!H=N~1`y{0ti@eNSg zV&I<#_EKP#sD{)yh-N(73zrW2CVN)XdNh9-_3tCvwPOXc77bSZu&Xd9qzrDmOo@~F zcA80|wbjCiVSN1{7gjxfAE`kp%h2o0=G|#gO8VoS^OKjjd%+%lVg(b{nm|oqw_Wgf z@1{gmx;RArGOk3?TIq#*lHGttZAM2*Ywj<^Yp$dq)=lgC1%Rc!ruVUhJ_2}5+nrg= z7xB`<2;s>Zu>*R+l!I06O)AwU0Ky9A!7g^BSJzFTAjJvn{F zQup~4wj*Z|=wx0%(KG;dN3{k7x0JN=(UNtBw{F8qk^4{@0brZrJ)R@&s`&;=S12qX@ue zh4z211CB;SU9tR$={o6k!HD(!&Fy5Kfold=XQV3MQb(pQ49GLT5(bpHKq1YAllyh# zJR<#`RTMa;vw`IfgW>on*OEp;JB2q19OYW{4D9mMzkpiA>KXb))b>ASd2B?(zf%Em z5jqPEp(>@Yh7nf%IH$U~7VUaw z*>?FV3>R1l(sinWpHJK7fm+vDfUMq7;&?KmoNf&ClQN+6eN#?g<0E?VlANbYBS1UA=iTMy$vC(z~O4rk64v_tZ6x!3eaF zh^8!Om!4anyROHH*2|n1`r07#^Kn3YLw?VDrl_p4+)rg-oU&2k2WD8w#(1lh4KBK< zX>gy-#aV^p?t^To+0EGwAsgdG)|!vF+VM0d%W;-Od1H>7w=6cmnFpK(uuXHqvhB|v zY%xoyICN&uX~hMO$|!1(Q#NGnIU2FsX+FUi^A<;}|8D|0$JydLjOE%Yq9mDI&czz78EcUw-JUl4Tuq_ z84rlk=r)4p{|__~?T#zY4VifR24uvj5&HNDAv?Y5F z$L+*gL82dU$Mz&wmL83oy_19GF5Y8opV|luQ!iUC1wGpdJ`exr9*qe%s%w^el-+J~ z!Rk8Yj-v@DI^N(ZRyQ^2d%(vfl!y4tF?-02>8zB_XOEpBRY5xqI@XqSBv#dl(j?h!PtrDq8pl@($CA}E+Pi{t}MY3!eD8u?=DT?qx z{A87*jRGoHHEq~dklW#*BTKb7=UY1uAKc^uIakAnhL`FxeQ;y0*f@sDVICs*2mQ4$ zfhAEbZ|XT7GBpAcp-N@s(EvQPLy?;k#ZJ3gTUhVhfV<^f{B<}vkIH+S((Pmlm0Z9X z!-hz!$2q=(N%|=aR4clGRbW zl&95V^4mqUl6kMkpr_{>2GCUrtQ}_J6X=g8;zNjfTNHP>){3w#HlbCbMwbnpaVSHm zu6JtG*c?u@^b8*Lo16Q%)CZWDJUkU?HR&4&Q&HFE@}ZCDs&!J~{`wu1;$H>}=D7Y< zd-&H|TT(vX*w-%q-p_|^WBsol1_Q2sh2TQJ(31eO=44!uW z292KMR$tr7XVIdf`{@VCeGqM@EHKI}bo>{#i2Wp*QNXTc-%Yb_Sz6^ki6jYeBFPv~ zWJSXe=`*tBA+GzY+Uq|g58DqET)4Tql%;%kYt&nYls0^R@A|Z@R|&@T9CbE%{>sj$ zJvb>hd>r?50{T}S`vgrm)CumbIlEIN{snjVLFmjezt#i&4O?CH=A?(jOYl@-2-mlo zxXpbu!4;?bX&6{8s;)D*%EpUcZDm7a2#R$k!THAm_I9lxnY=Zlb7RnSfnCrpa-QRm z2^kuuQE><7)?dHvR+5B4~nGQX~SGuNob!He8T*P{)`0w%GXKON?sbp zO>DNa`v9~lzZdAH&r4f4&|L{^#C@x_@7X8XnFuo7&LC=A_e`GlG1g+JL?@6i?V zmqSOaGiUZ?Qp7W! z?t2it(e198sA2?9bxIvI4KZx6YWl}Ag_#Td9{y?oG7f$8m9-TaQq1PoEcY$g{n+S} zb+2$Pm@ctZ8*OoU^8sSTZuc~?u?n5G-hmjgXN})LEaAUman_`;(;o<6J)2d;_A_#9 z_V<~@`p4p%k(pA91Z{>h?djGx>hO6FyoDRF<({6-%U13j^Wi6FW4@>V7s3#>xYDtFQyU75OT{tR!`X=3CexI z4QERvR&UiZ6YU!icUtZv?Aw+m{n#|&w9{8^@JuY{*DnBDbq4YkZL$SOQ#{K`ev(}{ zm$rMgn{sROF%==dfq3W3#xPbu=rHq%h3hE@w-Q)4_0O&^Pjy<`&uR0-mDss}H|?v8 zMT8H#e}dMbZ^7)Up9lZ>y8EW^QQN06h&3Bv-A9VpDcA_h)V{y7NBTZfk~P*q`^UdQ zjVdu&RH)srR#?XvXL1hb6iEC@Hq0s~@3vuc3U4EhphU)?Y1zd9MBzcAsptd<nw5 z^JCDMl*fvOI9D*Bsgh>2!e69C<-d_ZKT`oKCLaKf0k!P~T*QS{=)uI}js3cWp8P(0 zgL0GagllU02qeCd-OwIj_+z$q`@?KTFI*F{%2zkgbc$JL|c5hyDqsej1~Sc>98$+q&*XWW6q7FxTu&B~9EF|FS`s}-nmgVE3B#rT&=K4NR zP%|FY7>gH~U69$p-DHQ1qg(T-kaeNsRZ_RZVH5|Ij94(RVriz>*MQXkTqP2$tCtO7imUl5;}emra& zWJ+!7iAp7pFfdL(ah&(8*+8!4J4T-|M=pG&i2|v=ra!XeDL-sb2V^-*0FghadkMdg z@T04((_kX*-iX$LhoaU06w0#DT8|~r9FyI8F~qGM@pZz}+T<%?&F!m=Jm^B$SgU>Z z0_TEMUDhOG`#yZe^p8{>|38Tf=!j-d(^kU>j`c$f_Bb;Ud&fnRC2}z-)`aaF$Gs~G)R?_50St5pL2A*(^19B78%)h2_*Z2$DW9=D1piF(&h;(R zk5EgET(&NDlp`?>(X*oD@5+}zLo^Pzabi{pw$U2BczKu>#C}JvpUZ=V5gsG|4}d^_ zzv{ng{-2}Bpa1%=1ixIL8b*+hw{?p!L1dq&Pvl%^~S(F!UJmI4$ZutQ%UT*ab0&gj(_-nM^ z=W#7w^mXe0&t0kZ|J#v0y!#T>{%oT7y>2aj?AhTVwQ3(Or~P_6YjH&9(epp!Qo$ql9So=Z02^uXOE1%Y#tX@=kni#Puc(&7D68My-RKM;?v>$Lo%DW}^5kEq4Cifn$j>|95 zJalU=kjB1pUZ)ViuyBmQSWy$t>a(QroNXpis$+7J7ibObybRQx$-mhIlm$O z3|f3k*Hlse1&YIelJK+1zTKVn*T^`ZOKIF0eV@9n)H;&-VJ$DpTQu&Rt(_xqHTkQr zxka7V&!+GFak6J*T*?1dy#qj>A8JM zaps*`ocT30ufM)5>dSF_L-GU9r1_~lKjI0>n|HLfzVmR4sQ-s&RQ=q7;gjcQl- z)B5|1+Iq(ZG*7vwL)C{CnjdX_sOo3lnfz%F^axz~ss6LRz_KK*Se&+{|1KUAK_H;Up~i|458dbiD@?`2DYUq^VE;=sT6q}ngtM{#b~T_WU; zApS;*quA`*s=Ydy;+gl*zPl?aKhHy-spG=ow2xr6wvS*UzlI$eenb0WHYEHF$_My; zZD08jl&5;!6>8kqzPpS18*HcQ!?_bh{Ue`J_4#tjKm6!Fs6Ub8KJCBwWSQD;{DaoJ zodxQ1=qLZi88pv&kMKiqoxnh$=>SLGf^ z@s=mNsP+&0(R}SOZNByc(!&iu(B@4vj(l~IIzK(1;^;or;yoUHh3Y?}>hs;d68!IJ z>nR@P{kT~3^Xz=G!0*%MLC;ZK{p&QJlIMZGP5UNh(>`K(o!_K9C&M+n{avy{F4po_ zkEXm{4HPH5C*gl2f7B7$bq>&cY6RtHTTb{Q^5=XsPrd#{BL)5x<)xAF498KvqieK$ zM=g|xVd))ee|7S9qJDK_)&Kb(&9A3v^Xm%v|L=K4)#q&}FVhX<)%x$yy7N)gZ_E5P z?@^qg0+Aq|3#6*g# zT>6B-eQAGEoZ$$~u6~%}_WyF1Ixe5FlB{heRUeG zCvLQ>I<9R;{-~$4yqA~K`6efvsow7fWIw#C`PrVO{S?JjYFwX3{?bprtLp#WU{o&RWNAcfPsz2wFTLs>Ar|REuUl;hQ=T&>= z1B#!SrNz%2zBP@5>ybZ!@Y`rz?|{+jbGVb{p;zVAe$OZW+bC_G-bwo0{jl2qG}3(P zpr_RLvXu5k=E2e?n)Cqj8ke)LJ7-LAzyXKCEIkH#Ij ze`FYqv-@h_^%Ba%JW0zp(n0I6kJ5gq;|bq#sle0ssn>JoT*1>p_JBP1c{Ig&7N1t{ z>-IfF{TZ5n;Bm6IFF#Avm+|>W(|Y|#ZGP8FcK9jhiu!*eeLIKpJr4Xq;PM=qz35z< zk4{ka;Tg(Hu&4HXJ|_Ei&+XLlB}3<-+;E6$PhLjj=0nd5{9=;(G3|$XLdz#Sm-4z~ z>D(?EC$JCM;m)pVe>j}>@4TVq4cMOi25!5Gf0yETr@SlfOZNYFQCv_;%hz<#cv1f! z+Iqn-is#%&+h@8D+4F_JtKa+dcc_1UOMUOZqWH#PTAt5cX?}W_wl2H@*%fzd`vGtH zqrgvmMID#dr}d+EwDacPC4Z5t*?XsuUA?Vy`+1hO@AyA7|2<6G-_%EOW#?;gWgF8zj$ss6CdbWVXdU)_T8EYX=iMky`_q4^ z^RK5UujW3~Ps)9upV0o(C#$MIp@H;j*h2!piR`gwcMDS1Q@tpdcFT9cbC)$3Z^C&O(z4xo*_?OP2d6Tve{S%6Jt!n!hfRSFHzou zKWKRmPNF<@j~uD_ZD_yAmRcUv`)IxLF|u>yd}Mu!BR%Imp|>(W)$^1mr=aDFIF0nUx%JcIVLr^x@XknqhYf93|esq@hPkYBe@QSJ6?$^UcyFcr^||MvTo z-=Q+}9jA5i_a{)kxW8+04#iny zM{TJ-x9iDI+i|-3-Q7d`JH|bz`psUZ_?d&X@$S3SZ){HehCBy>rNLzQH?(o^I+{OT za*5#Co1Swk-otF zvNtq4|9cewe9F%1d;cfpE!;{w|D{0rhrMmoe(x0`HLH2<59;PqQnYqC6q5YIzbipnVxzFH+;Oiu7Gf)b<@*LF+Ylk>5}5 zcesW6jZq({_Ti~yryWWRa-UH*tuOyjTVFnp{G}IYc5esSOWV_ZNq=UE#^sbYE_<}T zyz)bJ9RDNjbFbz^eYu}>1Ih#Z%e~b1br8kX>~fg;UVcGw6#He==h?r5xXusB|19@` zUP*C87in>c-=Vn8L$tWf*))!yK;yVvNBAbq_Z#x+d^}6@j(=G|%K#A zP`}pVpiZQHPmMnk*ZB(J?^8V6v1HF2PWUJ~UvG+LFP%x_%Zu9hGK&1VFKgpen#Px% zwejVfw4V5#*VOsi{iKJ*3F`CTpVm!QtyaIsZ7IL#m}kZP%K7qgir@H9izi%7cKbEj z_;oY+0VbWS+UE~a{Ma9~__4Y4oj$Qz{Z3yb|IaB;sq@)?)4IUNe^dPrf1`cA9_8bm zMfLwd>-8^a`?#;9@o|SoRQ_+#I_%veRX^$~+NU`}Tc`Lj<*9SDIJZ~HU;0%o@BQf% zcm2Tc#P!H^)ekBDr%^j!@9(r9Z4WKa%!xFw{2|4U$^4%OkU#%Nw0}^>u^&(N|N9@S z&v|Ex=X^oSuXPT^!9MkKHGbwp%G2Jg`5FFAdGgQH@{r$7aY0v1RrUE%S{ELptqcE& z){ic@Q@zf`)Ia}R>!1Ha-|55JIU1+YKK-+=Rma(VXny^NtyG@H6d(2SM(T4olKQuk z`qchyf3n*T*8B+%&^p&=w9X~hsb8SDi*IOg4qvBzYMW^56gyD<&xf`ApGVTX__jAx zfA9OWANgiFKThVcxq;RZTD5hA>nM);eeIl!?Z`hlm;93*gy$$e{Czq%a6Wx^H&XoD zCR#qj#S|ZM)gJ2jwK4gh|N6Y;F`q*OpP^ zE#IT{zG2!qcR!(X0Y0PiZf+!dc!S>w{1!bg4dszrZ)>$b98UhpMUM(x<}>*|?ble+ zs9yh0@P_9{D%k)yBu))3|fg?xMcj z_f#Oi->=F4FXN@ZPy0LWq;YvH;m6Vap7*pmUw)9*p$^pY-F=_V`|#|t}WDkwU<(y!-`z}*=ZSr3(*Nz%ac5uhTjptf;h%)hrgewiv~`D_$)2COvo>F*bE*1h|G4bW zHzhlKZ*3pVbL3xoR@*nUn)U&Wp?ois2tS|BJ9|ru`)#9s^-3)t;O;cv-!!d0hx^Gd zy@%$PUcG|sD=nYuOSDgIW9=NeXUK2!iRQP-&^S2pAXU%Hl#k+%w7x9;S}#nb@AoOS zpS+vu|3=%dk)nLR55KG0QR8X6-a{L&_ole(>6fbgo=f|__t5rzpGfN*;JSzS0T3pk^WY2G?<#oA=`~Z7t`KT|T`PaX-b)_pPZZNCG4StXIgPeH1z>D-9 z9YFK?g<8LKA&nQiYv%!VQC^gNwR}^fDKFEpUFtY{8Leaf>n*_};~|&Ox^P)r7ygIr zH%?OHho@6Mv?<#6^;7blX@32*w%_3dy5H2l+SU>pn+|6T5LwQU951)OqyQ z@|#^bLA~EsslPf{d!8H8y2Gv7JmOqhXIy%wy3V*O%_Bx@^N7E~b87elIIYKj+;>dMn3S>Nj$;RJ*-E`%D{YpXoxv_aQ&zCz^eAq1?Z$ zokxEe&0E`EQRl6nQatmw-V!|WoWv{0kNwdR>hpY!@_>ySt@fYAjRpRzkJRTmkH*0P zZ5-^Uye_9}{mJGu-}w`r6Cw8_XUPxzf)-Ev4$aqIKUwWpe@gw;mo@$i>71#ZwR5Hl zbpFUPItTnA!bj8j4yn@wF4xVkq;;H4X&p!QpIcI%!xOY|?J+u+=P#N+YA^CbzI~Uf zpIsC`zd}3D=vDGBou~PiKBBz+&uRHxKO?`*79Xkesm-W=zUT?fFXsyUu@S2Ne}nS> zT&LyXe}v}IS8C6BG3oyWn*MvVZgPWm?%sVAw|UyWs-9G7KiX-Omp;s=P3KTt-J-4| ztfq0W^oeTMy+!8XRJWO=T{{j`t%{;gD=Z78qfy@#p&Np`8Y&N)f93qbMGGM_MnI`!!CX_4<>4C2+YPY6iu(U!uxyS3`I_3Fv zH&K7GzqXHKZ?Y?9l3gKr($vqNxkBK-Cwv(7=TGVDl9b=_`s>vGr-kNaH)!*+9*UFy zE&1~$&wVs+U3!w5&*26-$LHkx)phWrXg|nHkEwo=KhyaLo!a>bO=Rb<(9U=MGx-}o z(EN>ilbyLr^BdnN`_;5MU;8KZ!>jIA*L|K}CGcOYQsw#-_r9F`T{0ih%`~r>s;w_H z()quyf1>)&OZ0wwss22||3vR)sg`f#ee(DAk-zsrn(uEy`z|;AUv+$+M(e-3Yxzm8 zx>Df#Y3JM>Me(->lKsDo@Ok8S`ukSudwGZax*KmQc;x$i=4FAOq^f8W*T`$eU!hFVpPyG_7Bs_&rhoGkOj;Q~u|>wEgDq(fZeiqgDHAJMzyg()=@N zT9-M4)@2SQyh3?kUe(TXd7Ji^ok9D{wxjExMC<0qY5Uqvr}^yFl-EM83!F!Bww^Yg zx|G+b;|J>ZxI2AcW$m1-F~mP`v5+hCL0v-UQ>>!%jO09JF6}3ML(}scD1LI``s(xC zgW`>^(Bh4&6kmPPJE~vnCi2rhuz{*4%f<-)zwM~%)z=AsM#HzHdHrRYzjOk{7i~m& z#btcat`z5U@z3b`>Ak!_=X_1q&iPtFdF0O1^zCr++dC@+eiHHY)4u*^wDa2jLivsR zR;&H$wG_vDB*pQ{xZF$Wyu7=#_guqWRw_}?V_aT?b)(&EP!P(13dR;c5}4%Ba) zq~*K5k>cu~`m*ZhcPNf}#uKW4>1vuUG`%VKWggDE$nLs(wd!BJg!1q-II8_{(-VUK zj)#O?*?+E2c~myh@&Npd{AQ18ezPMfZux)qQ~S3`w4S>tZO3jw|ULnLD-pZWq&h zbL3qDKZ36Ra_SF1qW)0E5uHzY(GR^{oi8_}QeIynpDI_JQYDpYNcH%6KLFGH4*RE* z^G|adQd8%oX1JbTuDGSgy%Q-@Z6 z^2DQ3jV;HuOzTWFb{^l}V%}$Hi}XBxB%o=Ia@V`HjuVrxfxThsAFY}(3z9w+U# z=6edM#&YoV3q3<@+g`}!CQUSK)EQiBQ*(1mTdJ|GX#)Gtgb%&I&HEwRYwx4 z;JME@K6sEvry855PwQ;y>NIFPqiNdYmOyA*>%^8BjBBEOG1okSml7 z-+ZQbV#)EE3jy9=F>c#3$IVxo@}}xW4JTA9m0&QnYTH~Yl*@kJvuoYbUn~SwtlCa0 zq^o7SrboG!PwMDG#kYM@?WID|EmbTfcce?fKxx-@rk~Fg=5|z^ishp=ZZ+90^tlz| z?l03jWAcP1^G0sE;^e)Y`Q3>p(^RRLey)|?rp&3;a;4Aw=>$!4xkB2p(4|5zWr&IFWN=x4STMOC-&#kp$p-r77*EMh09(33I6yFcDG zxY0g~W^K+up;~G4^ENU?;~7;$eGqL6o_T-9TUvIbGdkOXNyFr}=@XjTOj@)xoiN1t zrKmq`s59BFz!0(+wz{m^a0AAsB{yH1v%v$mYA*Vmc0XOImfTRk4g0Sy3;#lwm3JYS zfjGI`Q0q?a2=0g3lsPwV+@@S94Y>`U_;yZj4*t{JG%c9%+q9g1)R0?6lQTXo0 zIA%$gue~~snbHOSsJYhwv%6JJrrjwcv$|11man-o>)R})*?=&>Y#RR2cL(}Y^!xjq zVxV?T-_XDQII^@bWP5UsH}r2sBP!SPs>+G3N`FpX+P-_pr^o0 zvW#1{r^xwUwvy-~`ut3y_QIFwICUUNKrlD!vzb={hW<*{Pxt170o$5lz>p%aBGY}v zq16qBo?OwXWQSI}&+Y3g%yCnxwl&tQogj=2OdV?65Pa!16(;#^E;Gd28|p>i{D#SA zasgWhB9%!ji9~Fji45o5icpEJTd~|vP%D?g>q;>9YoJI5tSe!W0|q6A=(-f!8fh+K z>r$j}aOpxhx(>unuJq3IGeIXO9GGLdR25t|QYZQSZYGk)f36!z=w#LIvZiR=Tmkvr z>79nax)BCT3*mi6*t(FJ={?RbSA$<9PySvvvLZ*Le4c=<8!2(@}Vp+6^CNqH6jX(@-iDUx0Zp12f znEY)bYLMA*GLJw4k47SIN-}3o0#A~CQ>Q#cSJ+T>B8tnCS`i}aLItOHpBk7oip2_$ zmBgt=6ZQtL4)gW3h^(35F`;#s->pTgKJ8&r>oDtCyHpIh5I3uW>qsokwn(1+WNYn` zVrE+;wLX)rwO;{FfJk)0`On&9>ZTo+n%uG*qS(;rL9q#Jo*{}2Ee{l%!0=eRSUDn- zK-&RXU-B>>lyw5bp|y7Xrp`|)3_Bm&${&^dy-r?>GD>lA>v)jT_KVOxDxis27EGCQ z(}nyTwqTR?`+}mzVj-y35ETve{bq8({BekDj9#W!u11$h69Rz&)>F-=S)Vavt*&-g z^OY)#Nyt~(`g@^&fZISrZC_-=Z6KX{jpvG83#T_#Jc%KH2q z4`wr7iWwSh(AV(@$U3l=gCD&v+tXF`vh}@SjoR{}(~i%U%gfoqT=~xI807mN`r?z$ zKsU)2p1IPL+;SmTwZA9e%e!+EbBM`{+v8MomX8R?(!qzy79!JP&S}RdNs_#_bhyhS zT4^p6jXI1$2)(3)4i6oL?~wal@xZ;MIk|3m(l7*O|lS8POSKog#ljeck+3m0Zs>JSL=K|1-xg``tOS z;1iX=$@yM_3#b$dxe9-|ruLjdQ)Uit`{pG#?fP?AD6L_~Q%e);lI5Fm!0uG#|24OC#{h{9ETXhow?+ zeX@U*ym^whD8hm4_1R0>O?ma{X2=%g6F@_2Vi2Ar&AB~d9`2{IxOryE&Pi|~EDFjf z=C+2RSaN&ZQpwG5ypmx!7i#Vs+6_69t;r=hWkh#6K$o zMbk$-7?~Lj9}R^WC3;@|Da`emNyU0Swr$FS(Q16P99~Oaw-_+8QljcqS=Z2K_Uea9F+{x4-D*P5X4& zIGuG3qgiCjZYSN#ymfgqTU|DHN+m-Boh>s4eQwzme$>3-A|=}xBF&JT7+N`I#1f33 zUdd&NG5FUnoTeq;%~u@b?<)$4WnmOK zy{=6+kaN0S$uVH=p2X(;$(oBrj=3eaJ`_yo&Gg3alX}5=(L!GU8=KU^Hmqb_)`jN^ zh2Cm#CEWvwsccrxfTdihmeTUxiagU#0v1zEQ(N~0GXb0C!YLI^b&}kO&Sk;j^E86F z0^7Qth{E`J1SXWQr2?bMu%W%ppG{yhW-X1ztd&njte2wgjYEJaHQtpyC~4iU&P#lC z>1?4;c1v)hNl3hUkXSz@pXp2Rzm@QQ%pZ!vk?ab*I$VM9UpN(3)q+NSyVa7Pm_sOG zFi=*RE5vZN5=Y3r5P?;c&zUWt$8m|LRu3B2^1Z1mVeFIg)FwhgzHbrs|>p zyyLRCGdZu9y@RmRExsU)h<=*QF(VGsksxa6*-IB09i^K+e_>}{dQM)vA)I_T?reA^9T+DmK3pRFWvkbTyVi0$ckc7T!+p7_|YNP2_ z6qr{G=BO3JrO!e5Li>i7CuF4S5(9>X;+S!m63+bkvnZhD;BY@0CPL(8` za~(sq4?e_vCS2;s_~pRx8ZZvcui>Iucp-24Xlpn!j^R)hG8ijz*s_mj_Oq8P*TfCA zHf+z|qqq#SSHMRT(ft?t3Z9#HO>(4$45m+xuN!i~HqDHs;jsoHzSJ$UkvJXL1f~Xc z4VhR-OHL*Nft_5{ObKczs>PzAbzehKujk`M&&Rk+9)zb(7nLi_P4JMotIDgE4DTqN zcFR1@J0+vWjlBSjBd0F+acIaSYqAOQmu24yX04XDopH)p+qSH# zmF=l9BvDzxVEpxm?JZ*EWqZ@k*qsB;7&;|2#z_dw z%~M7IW4T3J8$&|2G_`)Homp^i)X;3ttlG||8LcPCRsEc)osBdW4eiv)asU=hozl>^ zC}zTtg>WOMI3-MM7c_oUNThAab<9VmIKqruwwnROkvV3G;{=cNa?y!sWxiNj<_nNU zYqbiQGGQmDU?L{-S&5WB*D0Afm1IK;t53UQy856UA7yM@1(MPFQWW>Bm>^oXPZTz% zTrQ-2hkpj5z=%t#S2bU9&AMFKl80jm%ih9Q8P8(c{qv6lCUOJiifiUuh!}b(uYqxI z9#J^#Sa6$9mVyoVIa_88DG?#Rd)jSd(T~N^*u0Jy8jFV^SOUvk+Oc-cg-=8J8ikJ8 zI2si~o0j4dj9Pa46(!@LMc5r%UM=}fUIw>SgF1&CWZk?{HJOL%tqp^i^yYoeK)1c{ zWH+8ER84Fqy?OV5!K<%nTqxvRXXr|Y?OE)SpUadCa&SO%maDALW4IL;+Ka_`>n9&> z15Tnk=6i|t#tCKHtBx>P*!we$lF&)aRY;!B%TD1nXhxe?%_A1WcyVqD6iOBZS*OhM z1moC!tSu%O$f=cYCg=}tVM+B8te2aGnYF$ZHUoa%Qfwali0@*fS$b^az>1lsoGA0@3!>bz^ppvc6 ztz-+lQ=U_hI$m-s)l%MkfDnv#bU0d@V+_lY((md2Qcl+IRwPNIRttC+(rQvv_lbQ92 z&{~~|!oWRLx=gM-Fl6716a0hvqG&Hu$<6VeubdsaOF$vw+gfc{y2P1D9d4!FPY0c@ z+gWn*WtMEtnxQ$UJ;ONEf@t`8XUDlElLTDYw8b=g7d4(#a$UN9)0TcdLnqQ2rRam^ z9l^88IHimgL9@)eJ=AuS(#<^Xxv5y36buVZnardi2LY(Z$^_G|Ic@{PBqWfbQPJ?u zJhf~CftM0L#V^>WTfn>-Q^lyPh73(sW3IDT#CmqL9jhqM*bmk3!rjTP$^)O3Yx6CSm0P?6tHe zaqV%uJjA$$*5WiIl{Hc|WHr|_g|*C1*2#Od#Ihg>x6hBCXW2C!FL+m3%Kld?OH_>N znIs8}lgu_o`8@-*l4L;wZl=unnDVA%$j~%!5&mk-~~)X_?OV4JROG zM|f)D{fbC0!jo}SHO=CMyo?nk7r>{SgGex$j>d|h;}ha;ffXga122lojeSp{AYMmS zNh~o&??OpQ)J}+i5g*ADjQNl)DVVcq+`KF(VkLI;-XP<%mvDUc;>Tw%+4!uRg2-w= zK6`_X&tBs3S+_5d*xa~OSc`r?-w7N`E((*1;7LB%5Eb*lS(zwGB7jb1tm;Eo_~DDP zgd*a(ld#4bK3-zOM|4p^rCMT+CSi>+r(~y>>i3B=db3VR9cF6F#@vw*!|$zK@4Y(ey*C8C_v)$l-dgqE ztDoL`L(qG#o_ep(j^d@&Men_}>%CV;y^k@}RKm4!t~_}MwtESj|xBY>B&B1-|%9tt$a4^1LjO6CxiSxShp=wz9_ zQKzyG@&dd%yZ~=aFTk^Z0pa{l(ai|8P)gQVjwd*HahzcDaZ9jPS?Gmucxc2?i7hwl zMS9&zwp6Hk*&2Se)LMqf6ULf*gSgC5Ca-NPtK^q!9}@>{8O2A6(^!sk*k%rktlD*Q zFxhod!fTz>EDv@JZ|GlQry$kPOE;5akZ>tRh;V4suoG;s88R{K zWW`vMW2!@q9Ewbn*2=xn)qJ1R@AonDm*E5hYDd~r&E@DkYK0bX8)6YV!DGewVe7!n z<0n{&@hR*oIg2y(>EN7FjexMU$1tC}D#Wkk+!}5UT631%U=G6rpEcg7z?!?Ska2Q4 zuK=w%#TGQyY3szigcn$o%T#qjP0Gxcn4IaIOeh%=n%J<7Xksm!x+$5hr0B3C0CZup z60bv55=azzB5GuqA7u#{O|>q85IZtDc|>FvW<6F>rRL?LSd`j`;YMS`YTG07vrT}< z_^gvCD{yVcny6c)q%MVG&Yk1t6b0r*QHc#Z-@B9MM-l;XfD&lM8GOoYp%MuRvZ3R; z5$O}x@XfyxcjGQWKh5;|p zFd#VdE=%N@mfK0)QjGvvBdlO8EPl-K5|25;@5j${)_BbidX}8=7F0s}DdWa`kr8M^ z$@O`#o}CpY?bdns!&f+C#<{T2Zm<(apmVD(Cl*oR#4sU-SBYxa9w{beaWA^inAFtN zffG^(PMwO9M7hC4YAx)oQ|*X?F^YAxv#V@(ePB&YuJq0be)*ygF^gVQnMFKAjyBbD z)2!0l6aq=8`9%}At2|X!5*Ao-XT$zlFq&-Gths;n@gl9fSXpK-B0OMFr?)zJP~k4Q zB1s+VO=BN7pLXy%L6v0Boeyb_I7UkuHbY5NYBg?x5Wd*2Q5&DAs4$9cIWkwVo6*^+ zJS?^sw`v0;?HhMza#-fRFTH zX;^3_!zn7H2Kn`nhE1c;6y#_C+E{{uG3#fP+vUG)h*&non2Y1E{pAT0il&|luNzHC z?U@H1D#LL-$xta~OwmbvLUZe2?yT*|KkHAyTl0YE)nz~gA@Sr%SaIRDmL1k@?S=3J z*JYiYP-|A19!%u9*4ag=EVLE^rg6D`rm>(7PYBbm9ZGE2`GZAH0oYKbdAHc#`nd7^2 zS)Ls^ugN;xEx^;VunJ*0pWKL2qns^FNQLL3WmJ^+A?UjcbIUT}bViOnydJM6YVk_E zPR4WyLXk9Mb4qEE-@MwF*TjjL8?Qj+iDa=oxw#k-0#8Jpb)8_$E35saDn(YY=`3sH zMQ@0|vlwyV`(dvunJ|$>IYtH*Sd-H!3}OqOT-pQ?qMUETGhN>tOb#w9E^eFaIp~8fp37k(K_pz{IUYhKS!P^LW&z_2_D;L~LN|f8m zl1IbGJ~S#!_|W)30Ox?DbvOX?_I2<=+N?7DRu{%*N9;GHp2|^F=BYRQ!&{c z8!u=r-KJN^Zj&a1enuYTQ7ejEaQrVcLetI!4)2YebUODbr`MMQ2T%s6lzO zOc$jGl@)K{fUu+njv}qheFue7rpB}eiM*1iQ%9k}XvgxS*BB(a;HX}Gg!`wl?Oub+ z6yAA} zL6p}=mYdf-Feu42IBj$=IWkJJMuKUiWE{CigVSK2faeV734xG>$2@r*4D=SwcKA|-^ zN81`3PtNo<^D_*`)jGC zwwEGKr&v@5egP9|!782UVs$jf4|njD#fqp(Si>Zn+}eaWxKj4itpyz7h~b6; zZ29ssmMP*h+0GrqH&@N?l1j;t6_ha7Ccn#2gqa1bXk@jQY0V~lj?6uR%T&CK$5m2E zBKnM5M6)y`7q~4o&xfOunxx37;Y%0#Sm>QbDN$QiF(Sx}#VR+I86_+R0eoRROeM{4 zfyFIFLPQ0TOq$&aFHI^(`3_Nn-Pfp+!wIjkGE&9|zz!m5jbf4u5h1%WfC~6|$gW-N z6jJj;Dr2aP4zG--&c`gnMgsB)R$@^KCEE5&FE zfbobaiEx5Kn9b}YXv%f7Pwu}G=X>_K`KrJ}2`OHS@517BcokkFkw9is`h6^_)5%w2 zgazeMv8|*M%V_4Z_^Y+uUs$tVH|Y)Ksd{yJs@>H}C7j<>nO7yu1QyAoN~{j664gj3 zFXwc#eghX5HF2Vb&FbPH(gv53lwN%QYq%F)A@QwXXCT?lI|Pp())R9Vkm7-A>9=8# z?;&1ZbSE`(>T^Th+Hc59c0#9=1B5MHDDEQ+@&lAa3DqwFAWOKHiOj&5!1UU1x1Y< zULh>6gDiGKYm?_x>LhZIFVd^W7b$}7c*cSldMdCb7tO`zF4Mp8ge!U{Nfm>7RE=tx z3x-p>3Ozk#7qmM(vWp55kw^BnLTzqjb7J|R#2^y-yjU3(@nOW+=cSR0RRodrv{nEw zHOhz@Bczz)M`d8#J_r$|HnIDv5U0$O`vlFp3r;ChD~DHz%I!tbJ{rotBbS#!l+AoR zUcyw)Z%^b6sytqDaTTMBYY^!-Pf-#NX&%`?Bpu!fTb z;53~sP+~akxC9D{=V>M;@E;aCs>&6oQZ0w>aXpHXiiL;VGJrBhgjlXF`u)K>jFrPH zP`SM<`RtM1{MjR8O_(WC+eVnih>+=wV`YSeMnZ}#23|U0s>O^CJ8B78saA)g5+n9Py4Qtagnk=8ZlV!3 zQwFKHsbn^823l0{^Ikbz5$0h>Y)PybSX|U;$vR?nnAZp^*a@R}pD2xNSR?{|%yY<= zS(ZO>c16u9CQE=xQe-7{8IxD2gS7C~#>g>#&yk2|Win1gxF~5MBAlR!O+RuP6BTgW zbm~&W8^&O1#KwVG!ujiFBDYk0E~2m=q~W(sNi>MeVj<_J2L_c!#mNMPFIy8}{Gw}^ zh4mxwZi;%=rEnuCs!|VudcPef(YEuB7r9o|@an1>-da?{tCwnsTqSFNf!Vsg!0ZrS zVAlEtIyo15Y{fw2WwNWbu@8|Ptdoe>+%Dl}WQ{E=P&+FY86)#GVl+2gkh5U}O6LF0_QZZPT1FFG($y^>olv)mMNiKeIIM0u*h3Nm^y zEKMM$Sj|yNYlU9G33%l*S6EG-d9lO`L|h-5-?AXUZH(`bSA+)31nv^%5y7Sz+*MAN zJ49}zLRyI%GczZl-_HgECs**K9Z9p0NKA^?qDmt7BUP!7#G*spDCvE?P+m(^s*l({ zCtnS!H>RDU)9vT{itn!Jrm4D8yFM3^u9idwWp;~exB^+PXmB*`;G(0CNtb2yMd|e3 z5Om3_t1iKM8?)Ky3t6LRsw=D3=MvVg&R(6>*&C`Bd3Det&0A9Ym=#w^f=3%CNZeg{ zjnY!0N*yHf7s<`7!6mXfR3*8@LW!N{=)mY)BM_@37bs3S_Xoc}e$u&RSEtF!REs&< z*A*j(N|TG>yYegUU~*(}QbB|8C*xK7<##iVRdFUQL zGzIo`7mM^Lc{wi*caO8OWHQp#a;4B0CxA+l(8ti)^RWqN{M7~}NiUY-uw{}XAQy4i zriqPe$%9vg^ITN`D>R?(*2ox)z7Et`*Y~oOIC3gUMxXJ^#hf!R_$|l^$!;ZFRRVV7 z6(yLl$S~eQkq6j$J{b$)Y()g~2E|mGOiqbC=`~3b73v}jo?D!x=m92_Q)9%da;y#b z8bLX?XN}K}m)GUdbr&iX)^iUoi)tj57Im>9yLlH2eBN4=*{hQ>duvr@uWrihfihzU zuZR-mHr@OnlHxuauTGy0%2B%I7)e&0bFD0ftl{B#Zk?VTL)LUl!Oyzf(Hc4(uU?&w zx3*5lt5>Jvt-aIn5_LNHLdESa6nf=w$Ic>@XKI9He54CXOpnvh$VD!QaCfCb&&!N= zSn2}!0(7{^N)-^5C6G|-TU3NtyA&fa@(`U`0aTKJnFg7er)%N_+|GZAOx~p?PgfS z7{)%z5?gw2aC$Ehy|V1hA)6)dK8MBhW(s&XCdUHE{B%s#GNdqSDYlRdmiLyI+U32C zB{s}^8_Th7^4`XZGR%7$%VC%aOW}aLx9}z`^4=<}VcuKdG|qdgFhZ9KoEBZG!W!kh z1yb|8w+biZy#-Q>ytkCONSLbJ=%BLz>8x=ff>+`;E2|>T1C$jKNsCUXRb?b)HOuRX z1Y(D4xG^WXEM8!|F9)ebGLiv}SE9D8VIuOfMhpcO+;}cztu92zLMBiHIrxN>HY9v} zlvj~z>~#m`I60tYT1lM~yReS)H}?Aq@T ztfi;Dg3{}(i4dCxO z7+qajf*4(&`C6*EiXU#wjUH7>DKLL%IFFl?dN#-Z!jblS-^9R z1W_Iq8wuiFY>$%;tkMDYQ51Ta>lGuSc@c zG?+Y##T@ZT8Rx#l9ht0?&3jeZtHYMeR9Ill9s089!r4~%h+me9LQ$4bL_D_b zjT{PWxx*w?5v%cJ82S2~ezvNX_h>CLqGcsiU`y`j!)%G{iE09(QeJ2)B7xrPNMsL50$`-d3{Jz@tM4jt)?zCFE&&Rx;`Gg!(2QS)% zq@T}qEC%QzPH9J(E-2E6-o3BN$#=_Z?Qrb2dS)q`;qo)^aSl`M$*`0xeWw!+SCkYK zvs}+##=qkxd-FG&A{XnMQp3N#ujniHBYI8wqTl%`2umM5d1zMmU(mVC)#!``mB;Zl zT?4x_L8naH`%<-^eRNj3C@MMgm=GiuxSjKyxhX&UJL#~C`zeLl^`GUEn*IqY=*AN% zcB`_s{6eDF>>W%hRgH9Z`Y=B}lLVaKJ4O8Gty!#C-HV+hZ)*lVx1go)McZ=oUF`YP zXY%jFK2+&Ser}Rb7r3KuiIIe+*W2?=tn=Gxz-ozCdpO!|7 z6*IZmvFwzuYQ0OZt}QI{R9^p7se-zKz%y16dbStu=KU~aKAW~~F}skyD{4Fap!c@G za&Bq@qpHIJ;S~G*(Sq87gAcm61JW<8-+3D#X`EQ?75tuuXXw^;Z`xF9g*U#|e;Zx4 zz3+1IRQN@eY@72>@>8FZ#H)?Z2dkv9DqWsN)+Ln5T26?U#Z>X4^1TUn6w@M|r zq5euGygjvbAZ3?LTWU5=okJ(}#G10e3(?u;vKKE)nC1C6Upx>x>k!~>dhyFk+eMet z;B|}F%}jw0v}6O@oXsE-{pn1s!#{%&_lNn5znh~^i1qn| zImM^X8+#YKMSKZ6>GXr0zutRVQFTqOEcUvWt-Mw5d{Vb~aGX3LYS+6-W8!RtvSz38 z)Q?*SEqlzwR+|U3R|X~Gy=+xEuln9%&eGhF7F=cKoub5kJh&mPY5yzoOUKrd9sg*F zeaSmdHqPM(4i_7^)AeUfHMYLq>QHcVy6voe@9FyucDAobW+VbbX?mZw-*)X`2uV>- z^+c|7HoNGqgv7QN+d#~Fyh}TIgphVYMd*e01t0YjH>IdA`}76lLJp`O7Uo}mCfroa znf;z(R-L4W-W4;TS58b8uBSelLa3h;_@NnnXoBCVFJH2~cPHubnJ0a|3YX#zHuYaN zeo<-s?p8`#>)F7 zv?n)Aw2c=(zLPfN%PY(I$FKVQx1UtJaW_KZsaNHlS^Xp4?yc%p|9+TYQod6I_r2}O zW&7=2;#vS+M(@skVrg0=#`xuo`RVRM&z)p!V^}4a`iT0XI;nej>XE);@;P;13O|fVcQ`RvLU4Gek;Q-0aYEU4XZAp2kYg2 zs~mCfQhxj*x#8Tu?V{McJn%YcT1oh>+D>RU_`@uCrPR(qIJWzP*HxFVX3lBuevgmZ`=p)VTx>@G2|EBKg$3f29f`i`UNF*7kekYO@ZQeYC9Qw@$nv@rFU|CqEH@o4Y!8XeI@q2*9>C5`9uqii*o z!$qUtm}cyx^uGT# z+=kx$cjRH&TQ|RBzHX0nRiDrDV47dOus&09M2%~Sa7C-Ns+wo3U9jGoyS`IWqneQH z#?;P|Yn7B=DtY)fSMpEemnsJq-<0azP>gdfIrc(MH^YxLpqW{SiSWc;&gqh6?s0YX zpngle`dIb`-;7p6zVcV4N|(pF-u^&#s(AEY%R0l5RE)A#Xbo~Y_*BSAdo1IY?6u=; zNd)6Ne6_FJ98?88vDeLhRdjQ5H#vp#1-LmK%+0+PwswtDK3O-M^O^B&nBChVw+8hc z>s5{1#+O?!&kNO>P}Id2`%m(ZE}05%4#k`CWd1H+cx)jL-lo`XYoYc0^ta)01*f`C zpH{--a#sHR<8EwNKo5$D@IAm@`gs44pyl*drZW4>DTh}<7N=%df=1^1aN=tcB@ED)tst=@4^y-rN5tYloEBX2C6n|^=#Ryndp<*aA) z-2K09ZroIDh2j*sLzXdznM&0;EV-m4a@-DgN_;(aj(J}6~>Ym<7tN4;CzCL){T zTKw5)f9GFcaPk7EGb+K=sdaXH*9Qjn8IQV}n*I|nKO*(Z zOZ`!ek%1kWRP;^JkjqzEozTf*e@|jD%tdTR@_Mg}m7fPm2Tf zPx(_X9dn$W+!TFxluM`}WI`j~)DB0gE)sN{Jo1*~SF-uWsT?1-^YUu0%Ijuf6^81w zWwTzV;-B5Rhba^oOfT57`Sl7pabn8h%LQG5?3C+1LKoyxn;ST)a+T`}T9WZAQ`T=E ztbSTBHX0CVhzM5J$=_FDrtsxl_MQB>={&nA=2-5GDUZNEf5pN^4j*Z>mgUw))+SY0 z8wwv@&w2m!bY5AU-(922h1Cbtm}c5Cek|m$#F!0};+-PA(GVyCT6}h+hlbX>7H9}Z*Qx&GU1a%ZVKC%tKRMYSiPR?XDPds&b4yukfq?- zm*=7L#ybz)JS~}n)_HU9Vcbs}J`9(dRS;%PJ5`y&9Mq?FT#lovGUe2i`aQR!fg=|b zg8KeC|KwM=>r_yR3=B?__BT-ZW~6X2{?-9r%jn{UyP?rezPcBSY9`ATw^gey@KD9A zm5#Uk8ErFV=Y5nvccg^r-cK`LWidrb9gIUjTS$saYX5Z zFc1Gd9SJuknccK^tqAOs~H-7rEat=#?{jJSg_Af#+x_(#F8+ zYoqsmeE&U;=!BbhFM>BK<-UJXr=z1NWND8Uhz>3@c_REMI43-%zi<+V6vI`5mnJ(MQyecwqI+{@NHdB561AC&y&s;Ure;f}}^+r8?6?>}!HJKis~d zb$m6Y?NgK5xp)6gc=#V6*PbT%IIE>R;QFE+arTCq*H&oDq9Z+Pu>^P=63C0K)piLEr!d%fq&-DsU+pf zUiI9zUgmlh=J)qq_d&!|-O#abSjC1TXP1fJLkL}Rnfp`UU;?A}_dVGjJiD}XP@v|) z15Tk@$Cq;hX8(z5kKVbpIDapo%Z#;8asQR$$MzdE*3>uXfBt*#S^u}rH~b`yuH+~88J;Bi)$2|e?)ZRg@a zfgFm$9cvpVURZylM1MEWc8u z^`@rAwP>#@?lJfM z&FRCp!NFs3$4+b2Z?t5zn9VET!YZ7#wSv@J1h3~84qi3&=^Gk*GJvzVuJ)9ehP1-< znJJpFFIe{fB;CrypoZ?;K$iMy<7+k7>W)mzHopgMwlO_I;N2 z4gJJJ=Pbysq`>#K5=MW)U!*M7j@!mC52ia^@s-=wkrK91*LSafjx4ccdlJy)c-8cD z$DD+%_p1HTPUEtZ_Lviao%_lzIG6^gNJ_eRBZsbx%-S6w@Bdfiq@O5s&BZsVqOASA za*H^3+=DaA1)pVDyN3OdBX>7D3|#~J_LW_QJ%64BJsWP{6$q*cV^jP6vWLT#Rc7a~ zI8unetiU!(BrfR zsrY|io}W9)##mos#wU1j@S0)9S%(yim0#KU#>&$#pLqSVzVbA66MVu^GrKyd)^nl4 z%UZgFQ%Fwzsa~8CHA8i9rNTJe^K$=w1N#NVsk9Qm+;6?IPgOBy@8O3TJ*Jn_R1b(c zE=`|CyZj^lb3A9*(43azrHCI3aL(F)I?&%e;d7_CV`!)`o&PtkdrRd3K|@0?ci+Qc1C{D8k-++bZO`3;+4;#N5#(I0fI zttQ~dKB?0fnF|(IO*Uha&OUk7`}Yyg;`o!sM)tFP=G-+mwRR5WXrAFXxqiE{dJN5`|PVL*2J4nk5i~eb< zI&$$){V$y|OYlL@uk3x5b-Oa6w7^9gt1g;0w}zr`Q7*{P-gMCYoma6__>EEb8Txya zpYs~_Ys2W&0G+;kc?tV$Uu= zH?f?v*UuZDzwVE56if)lN&W{T$sr` zxLOuUMnz(H~+bTMGmNm-1PkWN{& zA@M44xBwX?wA>^`VlOm{5+SZVrl}L?tk0A<$p)cRQ-W^7+*e+*`(IPCAz>@|Zvija zmSi}LV}cC>n$L)pc!7&-*GyBuc`$6`BQXMobYGkY($I#HcD#9F28U%C zeT5|15Z}}?QEuF6;izSO7Jh`7`+2*`zz`c+&BcgjYTcT|wX+$@F$~1h(wPjoH_Q>^ z90QlQh)tc;Oh&XQDT@{DK`cOmNBSWiavur(n!$r>;F7RAisXYpF+MA2BZoE@VF*~Z zfpBT!TAaTT@#bUH93zeC4~p21EK*3+$)4AB<4C-gD~+P zBM}6c-lrV`8CxVz@Ct(PYENE$B3xkmiNrM4n zM8q2!hZ0syT9`?v&tc_>xd<6%vLHY(&Oz2Alp{gEQb1pn1aS%H zJQ$3iT&_TIk^*r42s}>|AL#~R7lB7i3>p%$;&#^RjEHlL;|QQU1B3?~`KaeUbtL6{ zMY||ziUD9dL&!&tGn2ImS*(-*s7rmK60!|R%4P6iCwJp&krY9KGh-35^-?Vxo|&Xc zkOEsf&P`?`4B@hvC=!~%nMgy%jV^{|q!ABEn$X3#j4)y*sRL-cxJX11BU+f4U;K-Q ztVWU&LaZjDoddO!qVjy!6Y#7lBe}srt40J_e6@o5IjFU|!HzB~5F2F67hFi)$t0ITc z=v)(HCP(7@xrh_YKz0&StwN-glT;NP9uZ|goM3I`BC#S{!$0T8sUi8L7kK5<74f#o1M!rChYJfii|1RGYEcnODM1yO-#MKckx3}|_x z27?g?NdzIoNQU&$%z^Z1N`!I-8b8n;T?~Y*2Md6QMHq6DJrDq$7FvUf3t^w48G_gw zKodL=6!0H28bPc@wCy9c!=N2SfS{d#K}(1D@?g-)KqM&0p9JL316RvLIZJRx&_Y0D z0K^hz=44e6wKQR3CxX7B$OksKshDO_%?nHgNRNxSf&j6x1wG{o1c(3tv1}l3$RL2H zWFZESG@fU!2paqF3M<7PcnVDncuENFdhZ&-gBK{MHtTOAE(8bvV*~sRFz#MrCrX^8 zmf=FUfC6<9K!G6$gB$w9&d^`Nq_YSRyM7>cfe!g-Qm4_8=Re?g1 zY8YhL$?WrV1X^zxaQGFM#fX*w9JU=K+A~b>02gV35hBF^BBX=##vd`k_Xn_zh(Zw! zF8xFj4H$tlUpz-g(2*clc$}iLQmiWIIIP%)3@}s;1loBRONca~vZ5nFVG9~dYzd8# zCIqg`i)oB)x}e+cW7>+$oJXS{1&J=54!Mz34SJF zs-;M`8CH;#{jiD!?Cb&9It?_^gEe3}N)@ch1T~`o9102G;5#DUh?3-N0oG$+(kV~~ zF$TaO@IHu{b7vqAX9H$vWd_YZX_9{9*xH2_zg)uEz;@c8ojn*V#1vepf(wX$JQ++e z2zuLe(qu>r-{_1q?fFi1!WbAG#R7&A1NS1~5>i1!K-zH`5~EfyE6E!+;MtG~O%HeF ziV^|Cb(PW>F|web1jfa{**ucw+L0vt=X6#|)8m0E1{-k0h;x^&#kcH9=Z%8Y1%UPH zfGJwQzb!D$GHk+IG!R9g!(>qps&~OZd>R!+d;VNz#*k;u$~9j z1BxiIaNamD$W|R#fMdEV3*`(Lj?T6A;1FQ|GHL{xqTt9WuxSQpst*gdK_Lqah2mYm zbl^9v0bt3@px^;@UOEQF7C|XspwsvuF|LEr`bz(a}M;QfuWjJmu0<(u3&9<_N zCe*?~U0_?Vb)*RZ0COBHLKha~K;bqVlpWkEgas%gS6JZQ#S*Z$cd(v`6Ht=;0P_~( zZa5PWDg&bgI@V88GZ7S!;|MEYF%)T7fFv4$LOuu#%;9v63xpMOP=x?hGVj2SRcQqS z443G@x0 zD9Ff5t)k3iESwtrKn`gOc9H??4t$_$;P44F8-g?JjsP8HpwIBZ0;m~hqV}Q*D7bz0 zVEekbT{xksn&N62KsxeH!1Q5bPRwd}NqK^>i2@>CU zQb`9xCuE7PNRY~bg(AqgkDcW}+5{(W@Y~GZ3==G*Yq$_jaulJ75zRpyV;tuJ&l!=3 zHo0Y2CXl)=5F(MSY~+627GoPHag71R3KpYrg1UyF%Ysx+hry`pNdXoBr7aJxTEJ@B zBwzuori~R8V&VFrk8Z(O5(G8CM`gtSZyyEYQ2{v<(1vAjfH_gnva2IXi-%+eOQ17m zKj^~>wB4pN->pvIg~Hnh?(v#ZyLMp&XoB+v%@Ni>-35X@kck66Vh$(14hMn6LMbqT z3#fSft(~l`+kZW9O+Z0LFb=va=u@(24lq#-=xzfgtqdBE!SIIRHUxV?c|^ckaPhxP z6KH50h$0Gx#vW8jQ1B;E`&rS@cg)}FwJ?zKCwDJ`{0W-?AQ&iECAcg|o)V}rVp)#V zCzb;!MQ?$sF8}}r;9q1ZO-6X~|I0Xw0KA{(yR0O80AxaVt{r5MXbA=wh=wpiWWd1D z04FCHMFaxR0Bk6LicfUjNPncWZyg;ge_C7M)U^(7KA~=ATYD|LeSaGFG|!+hTi=Q?GjcH;aR?a@BTmhfPc}Np-w1|Wb=3|b_?rK z8`zrCxwS^^?XBn%k78; zdKJ|imO9}b{|aztr8>RvkwZ^Y)nCxTe3&NVn}>P|N2mzW53&OXJilis5gKq z7GOu6VOs57UB|5@+aCX_InXvUhUVNe_Y_X#2AdkfrZ?b!Ya^g(%E*fzf7O`+*jEHj z>QMn^ps>RRHn5k+fACrbUf+ft@J>5xHkR8%umj$91$+0OKdeTT!)DPNl-j%PkBb1_ z{y#hTai?wYt4Gc~`4s>OMFEbXQ3a8hLL^o2mIqu!n**e%7Ty}L1J|@WWfcI7egp@{!#Qi6L8GF*%7nd2xZn^6 z*z+{(=?B@|Ri>ixc5S9`*{-m`9Gq+7$tL}B6}km>lY$5$;RF+qEOZmx@vS`+aDuiy z#x#^iJqqoNtfW9&ECffZg3Sumu6l2uGWMbFA#`iU$A@9S1A9Esv`^ z_R!CPewY&w+DW*m6u1Z(K=qI1KW$0iZxyhqWSP;H$_x*@Zwi5B?J@Xtlm55*pEs-! zxVy6ss^MmbJb5^mkoQjjR_}=oRW@v5(*SiET?gUW>`A!j7`^N+y%O$rI;l^N%!YaW){GCkoj_wC%x zx6z4qm^?Vo>p#>iyXEp{bcY`5$dv8PaV2CqEx=`@q@>13wYc&9w!nqp+NYTo#l{omSRtymx(F!92dMtXeK!lIMfdi%icj zHc{7)$0*$=J%gnFl4MzNb-vY8)25+UpQEU`Z#p*S@oq0%LK&#Jw>vf#@NP`OC+O42 z(EZfhwGQqD{6p5@Q}k)KQ12b3Ib7jql)i){xSkjGZgXiJi&$?YZjvBmM56Z;~#PapQTT)63^`@mE*?12x33)(Yj9)Z>-y7vZf58 z^o4&G(YkGkp*u?E@wK3Y+G3rb%~(u6*}Ks*mX#_xiAhI*N_ZWSVJ-Nu^5!-hUB zPvVAn$L8^poYH_HsnKGaV zlJ`1Y1Rw_6u|T}AZd1f#4R%UZFM-xQ)lm2+weMoKqcAN~jWf&z*&GqiM0mKkD zH-ysXe|Q=KMnb?MU|DPwCTcDt!R8D7LL(}Q4=UIOa628_aT|!23j;RSl;{sM&42+a z+a}Hd0kUF%z+MqWK#>uXP3!(nQ~>Jb!KBiOd5PEEl0F?l z`U?^FgFRIxb3=~X0T@A&D-=aL5VbtX0E(g=aC)8;3yMe$z^Od>CP15j0z9&jO`s}J zP!(kF9eVB(FD;SQJx;U%VE;WL&Onl}V3%b{b5Iu8lk{mci37@_{XcBjA+j1M;@Uw_ z9MvlXXgel8qoT#gt~*Npan4^*(d=XcAPyF+4ZZw3jpzt)vC@GU7l@Kj$Ie4Q$5GtW zTzdivP$$;97kAeoWIBke^Q0KaayW=H_HocFlr*qz!-EI`6Yq->fGig-wBKLi9XF=? zgpizel=7RV!U|OyNNK0SxXB@5j05KD60x}XRyjh1TfRN1k!H{V{#cpl`k<6v|`Q#X!uD$t?pdQCUo;}G-4an zcNd6&V|qZzl|&4bOD5QUj%JX`ayv!=D0VbR9#b7apGK~0cZ}D1 z1^)zKh^>-_@z6t>fFwB$f$XdZAS$A@$UHldAOVyD5_#Hy?1nm2Air6VUy>-ygD_aM zqtpRvLL@*fZ3wbW9TCKhB(wA$;5QJk&7OrAK?_*{6Ej4~Qbk@+J8~hx7#yuz!Gqlz zaH~ZI(IOHz1YNHPkJj_Eyu40A!#8vt~0MDaj|fmb|a!hl@0N{j}v$UFwTgSiL- z4ppS)c6M-r1t8r)(%4TD01IG1ou;RNjWdOr5M>G!p~0*HM$;k(K)`sAu@K}70BZss z?4mR|8b+iCSTqmW6jGcI_FA8;04<~)tV^Aw1}y{w(*Es^+ClnBU?D2dLTUl&$MkMP z#Jcl%Lum>CV^0VHyCOz83OQZ@J1_Y+ zN?saBQFX)(XnS*DH~CvZuSp^vfJKh)v8)KQx9$)YF(%UrR60(qfi__r3X&-J)IAUc z(>U05B#8%-90`WtZ3Q&FBK`tmMyvq-kLm$;`P2~$RY3t~t9nBFUS83Kd4+CG-vxQ) zCX72Ez<&{;2taHH7Ho?l8JrmH39LX6Tt*<7$dfKXpWp=0IB&qXa|4wYBtDo4v%tJe zXh8W%;uKT`{xSU~5Zh^Z+Sp6p;T`W`+~Hx|k!^r9a06fn88nFUXkt5{i<1VjpFS}h zpl11Bq5!9_{5U#TfG`;ebGHUSt<`*&h6U%jx|uj{8GWhr8Xig4k3${beiQ1_9&&v6 z%93gGajN6n7sB3Q+KDC8blOL=Q8TnxUMa5bdYm`EzI6IQPRY`bb)G?vL<@QIxH{9g zQVo6n^cr?azUffSM)^=Ca5DXtMwkSi=go;s5z%KzuVIx`!g}u}P!~X0J_89dXMhP< z&nrf}1uTt$6;L4a6If3)(G2>CE?6p0GtgLl$4TgW!Qi;zOY{S`kh*|q{!mWH zf75?-55T`Cj7rzNs03-RlK2(+9uEk7j%JYDb33lWs0;=ZM}>+)i7$aoVCDgaAy2*n z`8R;R_Y)j^P7TS1pxJVMFeL|k%ShYcDA3uFqnzU012xHIGP!T z^1`5u1^8Ki0_l7wDg$J!TR^kgqy~uB90VUb`545zRZM>a4AleT^#*|`B?&T!LnsQm zNEe7Z=AVF(ZsK2vw+%>n)e|HI?+!+2dzpK9%^+R@=pxd4`aTQs@|MuijVL{^fN^3W zJUCSn!AVsl0pzb&Y=(dmW@0hKJF#~<)r5FcfT^;g)gZs!kY8zVu<|8>fOyC(4bE^1 zMBrSlqG(p?%B9@|a{x^WtpuM}>_H0->AmL_EuebD1TZZh5_l?5idz9&W*7xd3f>)C zP~e8(P?=2xfg{ON1WpS3NFYpjkQ2ZR8AzvgCi*s6ZA!oc3mE|pPH0&@U|R2qAY9y- ztbx}dqO{=y7MzZ?2<*KZ=`etelL2YrXw)$v=meQPhJgY5$H^4KOb8A;?p&C3T4Wuy zLkBR(Qv|Rr5F-HD@jB4;3_*;j5TT{83;=QPldr*uoCAv!4g%x~Mj64(T>wzBYQV%2 zb%2_iK(GO8VW7xUSA2F8z|rcv1Vs=$VHpXxf@9Zv;@-M)R9(I3poivf8mnm7lF zoEWfy`a}^>5Y-{#3Gloa zMJj|Gx9@R028zgbptvYf3rsETK+nn~ZTL_)0nqD_8G$r#D0ny}%R$#eJ)tiF7g2(_ z1q&$5$zsv~4YkNRFt?yU5kUq~G?(BER>(_H00+|!4?qSht6bvfRBab*qsS*JgNnoag5Bmqh!N62Zj)81KF?Y!xkk6S>8KujGv)L1H{5G?wUZXO`CK?L*EMlL-0iz!1IOy z7@c<1ptlE(;)xR0a- zj~iKFUIuhLtqJ5SCFpBLFnjYdiGYw3MejXip@5bdjKGGWNic=S(E(kD$RVJJ8vsBg z-1bDV254rXfdyP3a)KhV4TSZ3qTS9!?|1qD=0KNyF zfQLG2_IwYl-61p(`d$hU7xPb`vuU$Zu zm=6h@JFLMw*Fi`=2T7rhh=6#z_V5BxcXqG?D?#w?MdaQ&gmn;mXiVmz1$~RV z!!f~uwTVerGk>F&w!UM?yV_%KC(C5lqgLk{-K)x(b{o$#`|_I>p^(b=tKvaKp2Z+Iq15-qlN6Ufve^q=W!QTUBo^U02!J>ynASKT8_kJPzHt&CT;Ywj{zLS7y`Sndy#FQ6L(>%39nP240uG}_??(tl+haVu8$p?T@)(kxTN&?-uH>Wfe-5Ge)V|ei-PD^ zWc{_qbJzL{_x-`&(N}zVEuO4$e%M_!SLKwpQa0DW%e{t;V~Y|me1&S7a`w$}A50C+ zKB>GMY1sYvf-*nJ6iG8~Sygy;ON6$O)*R8zM8Dnb^Tb=2%CB@}fo{iLv(tX}j16xK z2`gXlmcGMx{Ml>s2)|>(9=GU+!#Br&ee{vhRylJ=zpV1QTv*!n*8&HtgO!f5m59wF zS`*zKwU)Onp37K1$15*lAHO{Op-uSv5gEO?6E5C5Elja-Iwv)u0e;yDjH^{|(O?ET0KG_@%JXu8|b?((kv~C-TPw zKmEY_)pxVV4mo`o!w#DBfmvRbZ=>B=1N!&AT2L}EBd@ad>lc3Q*^Xn1 zt-Es+75Yw>MPq-A)c0&p`j}%mZsK`juUIkP+Xx$xU!6w@v@Gtkbt;IP#QUw5BeqpcVht2~v5-H|oVujNTFkB5YG zbWd3pHdi*(9&|%HpogQoTxTQmS3>D6q&NK&j<05So4a>ve+6G@YmIp;v#is(n=#V6 zSabZDjw`vLtDtPI*%9xQ8e^dQCvq|OPw8VLo0m+3z}m2h?_9mq*hMwUPQln(L9+=;zKGjLzP&kD)b#v$f85o{`0c2|;d^5CLw@as zI!`jGF%yTeh0RieT{X?zZmxPOI?_!UYUy_VZ3n}@%KAk|&#EtG*&VX>u=y?DEApy~ ztSv+jJ8(#SYyZ3W?W)$x6+ESfl|^Q(j`t`Q-fSl~=zO;M-7Wfe_v)s2!GdIR+UnUB z4qjf_s-JHcivGK!iK>g|SeiV|<}}|cZkF^rsU_^1HTqAw{Y(O}z;INmqLU{pM045j z@xy+bAL*=LCXF2p(=c;r;?s~$#-S%~M^I@tpUUs6sYI51p3svF`uw8b!mmP8w>Y_0 zP2^8kjkbe#;YNB|x^L-K)t`0aj?b2vcJGNZ9qj+2^!2ssqsC50?Wv#g zI<(!e?nUkX#?I{Y>l~FYxfP|AMXq$*OB*9xbPCv>F@Eomlh^VC6B7OEoWE{xy1VYf<9S)Zyqq zdVL|!J_P)pUQmreCF>cAmY*hhKK!sge&4^JTWqjT zxRh;dGWK@ayWG1d@Z#iHRQmH>$^M|FhW@$7!;h|mEgCOY#z>GC(`Gy z7d)`kbNHaFDixBidb6y@-XZ#n1N%c2%;9qJD**zBd@ptk>s?$XS{#U+jkH?+Y@yP$ zq1Iz+cj&G5NKZgd$ol~2CoH!cdvEc4c9c%&$pznH%zHX7wyZ~EtvClYak$}9n^zC3 ziEXr8`scs1fyE|#uM7E)ePdhFFhRZlY~xZ4emnfQ3GW-)QNEag5T|*aA)k4THlO*w zV#oS9e*L;Re<)^tmv6^bA>tX|u?xvYA?EYpH*_Mhf0T(|3TobwWOZwSL|Io`uMa~)x zOx?>XVn+BJm&)M>K3AB>^#_(Fd&r#|WZwO>a|*OqD%uzkF;wDeLJfK2=ZAc$LFX(F z-qnG{hTnA0cN07MQ**~N$*$76Q*=`Em{HrmYX{fUTutW{EHC4I#A4F8sCp&?b;dT= zgnnEPu^KE3PIT}$ecCYj+frpD&TjI_U7MjUwdJc{&PiRIA%+&eFq*K_*SqkvbwkYY zMt@Sz=Bp>gGIOuB9Q-!rKeYX7vvmvBDar2}5e&io)-(k_0$*3r=6{#11J&l^->OzsQ< z`%dQO)!QTiR(*=M2>q5J$@_yic8;&G@)PE;!$;v|yVr#WT4n`$c*+(%?T=~(H+Go^ zd0c!d^PjZsf3FRd!ACLe8dPQi^$qiF-~VMiwdHrYq%fJy*26U7(>awGqIX(b9%4{e z`^=!f_S0`k`;VE5yb&s~S$TQNo_jFW?xlulZT%?&iIRq&64y~e)n_A*UOVzD^>eOH z$yQn51&XUwQ_#dhm$e! z2n)@Vr$hhC_Pl@QIlDlRkHUPRd%%%ULww{AJT&qw{H-*(IrhZ@T%H?4<^sUeC|zat=<7sSk#l9NuC~lP+Ml z?K)$B80&ZBRQsdK)XlhgleGC+d6i=hY5isMK~@p}dY|epgkB6cDfzq(KGUOlc&lAw zrMyDSaOgGuLFL2W4};ezPSyV^P{*I7PB5)TN8ckmCX+=~Xz#rjZL%&<7wS~MYTGmn z{CV2K%09widVRakZiB4-bo}H<_Psx6vmECTkL#a3K#t@$spg+Cx&FZ6_?EH*J!aV9 zAo^w4NLc6e?_hxxiYqYlVJM+T#>!)r+hS(P6W-RV>FY*}1FLFq0)sJo7 zGXE~NHJ5eStBQrAE~l?&YH7bpn$1cC$)F7#SA_AG)w0ODwKB`#az~#1WrY?diZ6k zhz_>jOd0gJw4Pr*&)H)hnOsz!(#BUjr(C=>E^jiSVxpuKQ6hBg$=i3O>+@Z~f`el! zgLthH55>|^pNJ5(orRQNikRP*4z7D8(xOX+Vk-D{el>~h{7vE8`IOT4dF3@P^@;Rr zR%6uT#9*I9X=Rg%fzJZfpXGj~v^nxptyS!#gYv@S(X+J`3eIOoOPb8qvn|(|^-WUou6pU%CE?yNBVADv=@ z{?cEnzq7CfE=Z^c+ArVx8jQ{T+JSW|oG;I`PQ8Z2AU(95Rz4nk=a&Ya(xu4fK z7UKSVX=IAc)=B!1FJU$F{AEpc}`rcba?Wt)h)%rM!OYg*Eea z!6lzM+Qwtnt#27wr?!RR(xsJ3cQto>oz_w14u;<%ebQ)@F`0pOOUi;7h0$t;Iwv@e zwbq(y*DotsL=BhP7_a;Ot)4%gJ|FJ8UYTz6J5fT zC_HdiHc4Cg@F#vt$D}LUHF_baLw`Odm}i~380CsOfX%>M#PnoQP{WDX8~Prm#>kSd!&J=hmBe!7Z;0N+Lx{`%vq^9Q~-^1b@HZ#7r8 zR5`3ggl>+BA6_qR{9(uD#t3f}X4De08FlpV zEUtf>22_1F+vHsCODDffsVig%!SZ^OqmnUgg|uKVLw7B{Chgd@6I!`sIi4_gQ}DiUU>%k+LaW zz8{hlKYX`&zwpcM@XCqtSNCG;-^E;$?`RvC8agF%oO(szsh9^bnA(r0d6xEs^{#j^35 zN5P`ifY$f?6_XcM%R0GzttI=OZWQ?f9sj&@R`1m zm);~zy>M!RQX_Qy{Vf`;KApD4bZX#{ry1SPSRvA8oz%f}vy8=I+15c^LX}$9Blz^c z0@b+4v!St!eY^h0DfirE(N`5HHly97l%njd0j`M;924ibJ?!7yd{%2-6#o&A_BJgF zIOV=yjvk{SyYglKn+L`m0U~!6S@vc~L%g4{R#bUCBV(#sJ&YCZF-grm$ zZxq@IlUwgE7u#V;UJglW`*3sK;`sVSHtGZZot{bU^-k?|)D+)qpH{J*k5Ns`Ej;H?OA_AKH9SMS@KX|A}PD} zZjm2bZEV`xHD$w5{-Qs{p-K7l$>0A5qGPbDA;{VJzem_+3ZhG!0~=~?b6#D%d%5x# z>%-}r44Z0%;|y(D95OsN#$PAjeRKUf#^^r2OZS0(S0ztHmRw3NuR+Sw%zvA|H^ArH z`<4%>{%z5y>G{6^OF*>03hgZ`Ysn&3*1h9US@(=HyGFA3QQm;__9DSvY~$^hxD9Ar z?C0MAizE_^B?`Q~SXbfTQ-tx1(#n=GLzu?3%eV!}93r9`o+FIcTf5;?jlI%3sGn+_ zpnU2`J+tLiAzvl+`ZUv_hK!Vu6THGfPL2h`zEd}NyzLVeAcTY zeh3X^OWR~gGh^AwjdyFc+@hPP<>4!=THe@8)^fKeS<8WUqn7L6ZPs$lyX67c!B}=8 z;@Tk~>Nmj-Nq?a&OGGyIvkCT6^&tHh20^Rha`ITsWzVj{W$G7}%X2-Y%bT8{%RXb_ zGJ34(veVdqpG$WSJ;LD50rrh@Nev?S%HvhuZsC6J)eQvqxYPg!VkgguO z3$CW#Wx5)7mvq&NtKcW_SV%zZG0v4c34F1;+zelWd{-mw-_ll&G5Yvt7mR;?X1SW! z!x;ZuKv$oQfvZo)n69$M{Fi#Csv(%#9GWuxhzg3lCo-YvJWg*22ossD*Dvo3-%4XjuyhNE@w*&_){z z!8i=QNJ27p=H-tR?K7PHK%NAX0WWK|LEE2LW))kGdqh~$o|x-h56lOEj%}$ zwXiM)weVw#Sqm#t{ujL7$q(D zw9Z_&S!XWxW9597W}VM28lwpwj!0d)Mot-}kN*`2XW{++h^g50A3e z50Cm^=*kQ3&|Gf5TDZJIaT#j3JUCb0&)>oO`8%xp`8)m>T&}oXbLj{dE?a+Sxg69* zx-6YTmosmN%SUfFT~56Hf5GLxk($fP;4=CH%jIJc(q$9ra@k0Z?~Sy^_eTEvT$bMq z2rqP*Z*w(q^)?rBNzI6EqTA}wUf3G@zGds{&eB%wY})$cHrU#Kn`tZWHe=jR4uE+u zs^oHr%!5sOL{t5oVE-}3d?&8CHeROA?!W5g>FoYgalN@sfBp@_^Y1dN1~zmuo_{ms z^=1UGHzTa;&4|{{A@L%!-TqT6Bgze&-7`XSb6Y#%=DGJQH%Z3iTxvSqbfudvmYa|f zRbS%iqiYtn6`CQ6=9?X*=EYgm+?))}jmf6wcgda%)_t6;siZ^Y+jlLMrPoW97o^HV zRGDh31ddCt(tCP|x?Fw84OHZAHRRe$xgg3lwoF}7nJJg+<#o|l+hG0kJCYUv3tX^s2ex0ItP+_?XJlnHMgj)XT4Hxphpyow!+ zi08jyiTfGiD`rr<5yb;7@ymz(D>G7Z8@xFCk8!Aot+!~oG(avlDY+~&<^#iJE-&4J zTwc6I=8}-WB+tL%HY=UsyRM2zd38f9YXZe4jvF#3O%r5qAw^#6yc+=a@W zZ!ypF!MAvPp64)hEnP1hdSev5jqQxv$XL$y?^14%JlFv=k~AU$<1! z4VC=qRC$;x51A@~LN`w_F4bt;+oEWxCqg7D6pT zQTxD{IIAPoHV%Q>h9Tz5iQf)UUQV>StGibdEAOu9L-hF{B=p)+E1`t8#{ADiOelc~ z#Sd|I*TCLG%nq_Q38)N)$ znq0p&nCsUDo9&u9_}{&3qn(~%Q8TRnR5J87GW3!eo=W2VbCPxcoK(K&{eLlK(;`WK zQ@yFEm@8kkD(0WovSJQTVZ{{QjEXsQvsp2_ZvOvt%7!OI|C{yqAA|7zaX5E)rUuS0R(|M4~Q&x;8cib_4D}Jo>mfOvG9Bs_R z^$s{@Ru5-Hzjl*mv$&D4*r>>O+)N|@q&_DOJo0w z2bfzDb4xUH^NAUF=~gny+PYlNs68?otz`6T3*-G?nbDR6WV9*4tgY`8+|*VjJ1z~d zZ}PamiNk7QfTh>DwP|r#s9Frh=2H5EB=~hGXCoO8UGo^Uu?zCi2pYosZ%qeuhVm@hum&da*Hu? zYbSH_Wp0<7x%vDxz(s4c6s<9KQ8o7{xCCA7p#hqyYXgL-DbHA@<~5b3lJ29aegj}C zW`Job(v_)8x$Q!sO-t~ldLqFcPg@E8V9XhPI)Mo;w;{n#Y-WP5+p00{n4ycdh3F_n zG|dpbR*DX!=#7@>wN(|B`@89?pZE*aqQ#bKV31T@doNWF-vHHvH<+qBZ>Y|D@?YMd zsYXFHO;P=(iBwILsr$xRBKmN6{1J4 z*F*KtBug}Wa#z`-OdW#*nQ{*dgw+7eX|nU5hz1TGR{|0z=5hEyFXbu1QAM`F#+^Gj@X&Y>$~_4Q9s&0b)szT3c9|8y5s zAE)XZOLaKGZcMkQ9 z_8-Gc=f!Y6b&Q$m)R=$U18G-1wY9z?H7_N#9!6@vjAm*V`XaUSea)_Uw6FVDM$5I> z&c2%1tq^;9z9sghF^4r#iY=wstCrZ4s+cj6<7(985?m&F>#2TJOQc$8w^Hq9q`D@B zsroS0%gj`L{_JyUFB9cQ{EY$stUg+zK1j5WlIWs(GSQ|o(FuK!==eTnq9cq%Yx}L( zR`JwSC1X)B*wSaU^G)8e-?gRG?zD{P3)g8$Ctfa+ole`Ee{6!UGzeA`(0HoVTGiBEY* zdg)?aA$Ff4cG}Ol{!6jkXkP!L&7SvB+Y^T;);Tm?0Z3v$%p;)QUEzi2lA-nIdX|86tbl&^SyGB~)#K zsPa_byjDx~`O8GATjwZ~K6PcPAKuPX@4uG&^IYpp^{SCps_WAN%B8opl0lE|x_o2E zCn@sH4Eat{zO*;wFZ4FM#_`@2&A@v0HZB)?agFLm=Nw4c6<@17e{XzWx%bBJ?5%n1 z<}EzlKUG|xU%=gy{hxxxIDI>iPFb^ zYhe60!!kFijxqjw8_nGw1#`DWndSyYm7kI;cbV;@ixhid{96%eZ;1Fxkt--t%M$s! zSH(*u*cFRS zC*zM{{1hwxVE6Ga*}g@qd_3N_XqC2a(Z-(o^{=#sxc<$u%zahcxc(*6-19wQ?%AGZ z3(f6WjeU!}DqG#??k2>O)s0u`jvl=z9DOy@a^z(=dgfL-+R+1!w)HR_t?A)GpIgyG z6KMjG!HUQiS4ff0Qe-AY9yLV*C-$hQPh7(Lo923=lP-uvmpx`B+S*8T?r=GNb`8hR zuCd0?uDSI0H>|;DuF(?P^^Zubo|4#`zA~|6!{qsgiQQ%<7C7*lDlOIQuFH*sT-KwO z+^fcv(xXzYv^%d?-OYM9-o4^Sw4s9yv2UCgBInm}Oc;oW+)Q$?cs}i}<+1O)$RkL} z<2oacv$x3SQ|2+p%416RviCVceZ)xDrbp>w4?^t2M=Y^(#&nQfiiJ}w%oGc3(!JVG zIXwp$-!{8Z)P?%hMj6rh-L$NJEfHBYQnJc4vN}DKS-sf}S!H!ItKjKwRuvd4%FBLB z(uu+;w$f5p*Fy8WacQ2XTj*Zu!OM*^EH8(Rxu15@OJP^8-{@+s-{|^pd!Bx}tN#A~ zIguLI!#Ri5jMNqkVQM3p+6XhXz=W=z+`OZk_0pBdO|hj?QDptu>&{5JFj zCN?Cruk#zz#9c{(U;kpP0}7KDto3PK%5A}gB~)SfOav=c{Ls^=UBB6}HDrnWRmQiW z_zpX-v^0;AS>an+P;f>j<-n8brt@3UO}lBcB~@;-@fU zeV6%mshM0W=dGS|j~^{b`8G+ZT-MdI%(AYY&Klq=C1&-u#b+bD(pspWjnL}a`H~95 zhLs5$roO3y8^QXx*{f@QZ*}44{YsTDy+ZFBha+JUj)ZCGN;45@)*5uSYHZO|m5tv% z@bY-$w^?lC=R0X_9Q3DX;~`U&8REvWjUxuh^G7G1KRQ|Gk500U1I>-!)P0-&BRjuU zym)tQN6lcb)573`lP!ZI#B%}+4!Vg37j=ZeCpwx2AM0q`Hu|mDR>3-Rd8N8X7yG$b zh+VFTU15k7@J`C7E5*8)Vu2wY-OhKk&_h3f(C?>Np?|t4Lq9G<7k1$Fxr23m?%=^w z?)nb;^$#M~Dxp%&2UjcLEYAu7h$iSv#@T`$RsLbGttC7KHSDWtNyxN23t><)+ z_f86tKT?%pPebJU8!0l1BDb3&frGBD($hi@T`mf8X^LFYKTWO$`5 z>e1QEcK{ysaBZ~)xZ1jSrEwjuZT{xfZijKTGfG@6TXtUU{Q3E}ywqjO&B`uYwqBdW zz0=j<`u-;;MBNO3z^a=A=Vje=i)Y=OX^*-&-QKL51MOwq1SHr`;X>hWjEDK+#8=rH zkIuEp{$;K<{`!lPJUy1GVM-FJvoYqN_WJ&B$A#AklPs@w4X?HV^xA}88(Cg`+k0^O zC<)W&zmEx#T8cR>;Xlaf$G$%^UQPjLW z%sQUJl;i1gx7#YYJ#Dqs_4gbV`t=q4;&W2}#0}Ix*^cjTwX@#eYUkm7Y+2(g+iAk1 zAiU&$OZag^c#0H$oWgTV;lL^FJTmU?JF9Ne<@X;E@=X=_kB#Y@zpkfzW6C$QeAjB{ z(YN4Lkijo)wG8e+1~1-cWiZspV6x2M?Y79^&9-I+&$g}Fw`R@I^-dHCz4nS;=-;x! zeWc#?)Qhw9y0@+Jw`Mt|>*>;Ikp5_ba$x->rRVpj^xig*-qXgE&TUg|>9^Zx(&rpP zx~n4H$e7OZlhTtYeZM7rNA;z9>(Y-x`m1{_>4Rsb>u2KRd@-djv!wqFt+w=*P<{Uc zNJlHu1B^+bCQ^DyD5PHqHKpywTObH-cJbqv?9_M>!XN_VuRTU1rr!>wMN z`@GDs_IXLO_Ia6LZEhsDd~vz6(TlA$Zyy{M-VWWPRO=b(Ehm=VMz@ByJ6oIHl3L3{ zc7W@hjhuVH+-B_oGsM~hCc)a_D9(IB@%RFIJVZg^EK=MW6m5Rlc$~=dfGp(kBlJ>FX5f zRAZtjNJ<}R1!+es(`JgNzH-Ow!1BEPZ3U8yOQ?opH?45j{3=^83^vy|HCrbeH3Z>pXy^~nJtb#bgEm2Ax1tLR* zgH5U2V7JeKYlF4WLlOE7CG_LP(#2yk^dg3S!V3ME`_K>Tp-cY2^N$j`zY#iAhVIYM z{jAX4T!r@N1xL&b4zO<;X#X+6z7hBPzRqWa-sL92dH%u)F4%f7FyDf7zN6<5531IK zgM7u$d={`~$>oDXxxeP4#P%9$;EEnSyX`$9gTUzKSO*WW+G_i(r zyas38x88c1~N3)?mXn}@W*1|k9ywpN{ z*D`pZeLn`~f-x}HSU!WuF&s>P#)wBUnOX}x)HG2Ba;y~PfEz8hHeR@Fs=sh)`VUqg zej1l~_-S1B;m7qOQi8Us;A~nCe9G4=t+V1^Xl|`%YA#RLb*#lJ74072^1SV6rsa@; z9OkB2IV2f5q{tjTYla*?ZD!_>)vT)Dil+{>J*=x||1Q*h74_D~<@w`4lstb>y`QDt zt(iwdZ51bRxt>IKB$08amBiP_WDG9&&|Cz8B8tSxvOMnDvXO zi_N!Nbus;jtc&-1ur4Mv;d;|1)_PO%qdIPu)-b_-%KByxu40ixWXH;nO1apmtL%7_ zKK;?nINNq8bGEg@9I0nqU7R@`c^i)&{+D9oMb8Hm_x;BQ6pI>Z?f7**#=k~d?RehH zc>kljT))+b>$e)2?f6Ec|LTBZa8(8r>*`)N?!oxaZI;(x3uT}Es~f%UZV0bC8=A-O zx`u8~4XYdK@46uLsu5P`7mUy^%g_rMI@1b0!&PX{JC@B}C)7GQ7&nls)ZI1KmhC0o zJU`<7YM5)nP#^2sjrA|dR+X$bW`FEmStauWxxYi8*|Q!B zRPXqj473+IyO=GrTd2{;2FP2zMBeHhQ*ZT(tlX4YyNb^vU5w7yzRazg@;yOyWt|xt zYP@G`sNumG!p+@SjCD8mS_7@ZI_(k_Hse-hHJ~xgGCYzMc25KDSJ1%R(P?;tvO7A( zRlcK>XInesQtoVvJasPjkbW82;N|hRLoH<|`KG=;*RxZ2o;Td`JoTXTJf;ghd3 z8THLhl2%_`<$EPJ053(ixTN0+m0&;V@fSIs3((iP?GTPu46_`4b3i&;96?8`1K{X~ z0P{?+JfONyq;j)V=V_p3s53~E)m#^y&!aU%&Bsw+?XqV+K017i$5CI|rOv94qi40} zLSGk8wP@niHM9rX1M0euLoO~{^DBLP-KRaij@Hggqsp9@MwRPwUHK$BuAbIF>$Zso z3cST?pw55D20GP=4HQ`q4HQw&>~gKdk89&X+p)(r8;Au(E}lW>iBA|=xjZwCDt{Gp z`(zocF7u7xl+uUBXo<8fFEkU!6q2l4w zZv|Xc{`4u21V4YRqHoU=75%{wtD^ml*`9eFS^m%p>6^Kew0LHGcY7H$va6 zgr2ishVCdsKf%xotk93R3SH4kx1Iy+7v+)yji)AF(x;D2xopN%x{#LU*OGGc`&eC@qDeGK_D_1tzeUvQE9w$e9)g$dH8vk_+{GI1 zt!SYFyOn)7S!GUpmHTVXS`Y?78z82@N@4KWq2AN1o$AG05~ z_EAS++BuiSN8tPwh9{q3`KTf?_cz95IQ{0#)8*QDGvDCL%}Qu(EP2-^;UcUWaej1? z@zJm{9}Tl=Tzg(Bw~?y)n&UvX&t5L)>-@{i*ZHeYRdT+L?P|=|g(&lNJ#F$iX$+o| zCWI#9&>D_IYm_*&#<@Gm50~?G3C2nO!^`xw&OeIAOuoTt%$s(|#=It&jXCu)H0I>X z%uCT&@guH6gEMFIT@mz3@#7--W3c#fp8T<~_;IHE(O)$5G(6;s|4uSqs2_(Nmq*$6 z*vh<7U)H3DU$JFQaKLDP`({sWdMbI()_R{DFN)3a*6MBhYEf??*DLjAOjcZM!Fn6y zjd~mCZ5{;Ic^mK9#@Wxw`yLP8vkksPH$I+S|CiU$um3r?{`a>m-n`AY{!5E9YQW<3 z8s?4!_tsE%Brtc>j;nk}0=KUqsy(;0o?a5tTN-Djw=qwqH={YzJLbjxh`h|6`-_)y z6V;i$Pj=1WM%PR?M_LqjJ(py#u=@yFgQ-*X& zQ%b)tKEBT9J@Kz0y+r(2**trB)rWP}WT?IyYpGsssLqzE{i)i|QtehP)j(bK!1qEm zQc=y_B30{3)!#2d_1BB0>ZXg8t9me=e^JZgc4YBEjFrVOBa3-Kay*||+-+qs+{mI{ znWM4FODx*ziR}0e<3CCwj?KpSPZK$QZ~=)NzhEZv`-O_D!~N^m$NWl_*RMaipye?T zdA!uu%44#T$5ffeH0CkI%H!^Gc~tQF_0782zLi3(lOoo_5UVZ4no_KZC05%ItIGE* z9s5VqoB+-2K9*+oCaL*cV`_f+4>Z5{$JBiLpO8vlCaCNS)BX_;Tru`dypnFj`@I|F zqed_u?0Rbxg35DLyCaX{_f5-fW?()T-EKS>HPJoJ`&M||d7bhIzEOHy-iRLmI1i8e z&zm0e&WAXMR~#I%bAFMUkIl$56qi*S9%uRu$gCOqwKuQp&aa@5k38-18S%lrel6vF zW@A?$t?o`1JZk!$*NndQjWBvF+A`X}FuJ}Wjn<}7U(4vZlFCmaZZ8zuN;L5hh)+_) zKl)jUCrI&EN+AAHi7CFo5U<9CVw^7hPPULfbFC#EZ%A(ql=CB$jv z)w9LPb6OrPk;i>X9=kTkJO;=-vdo!nyL-{OuCn4;)riVwRU=&PnB>v4YKU&D9&B+voMG#; z-=(eI_2qcXU$9m5m)Tx>|EgwtX#-eqt^J=Fz^d_=e*Ig4>t8Rc!gl;*T>k=CVH?h( z!q%TP`$6{EkO~H{UjFfabpWgK`?g(Yw3>VS3sG}__ONR1({-}uw)nH=UO$7Hd+kh! z^HFNinULx|O39u+PM2>7`N@j>07L#}DIZDsh%;tSZ*|5^Pp^DE@P$7$=?^{^(k0he z(g)W{>7Bf-^I7*Nq<{R=={#_S__5sPdsX~)CQt5f2G}?Cm+S5`eF9d?p4b|-eVw+; zfO43HJ2H$0Y3gG83**okZ5%q6>(%hqXPE!$ZdJpnAC391x^g}4X;j0e)8?V`{b_mV z)Q^*2Q?(~fnJdZk8I_~l=ft4bU+eZhCj_67M~17<{OkQ*Y5kU`HQOI87q@CQ=H7{F6K14*zA!*ibMV{ zj(C~f*t|`@ah2JPSE=22ZJBQTsF$3gY3gEWUzt1giKn!>`|T4^cOAM~b(g$a)}3!1 z)?KetsJkAg%=QdBrQNA3J^0n{|8Mr-g(o$~M?V&hqau~nWI57tL*8im3_A(OLrGV-2>ai3v1%aW2U!CRYRlv01do>! zW(^EEA#0!&UNGiMHuA8Dv!62dN#Q7eV_6wCn;gm>gQ0BhBa?=GWM+g;64f%z!$$l2 zxR!Dcq&z3WO8Mw2WBtb!a(?wVQeJi3O!?E}#xO}jmD;QFG0Ta1Vy}KE5ivywDXV3v^#l_wnt;bycv7xsFu}|_eEBP z9j&YuekZe9bGba8nAPZ`=FQlUqb_d7WavaabRUFXsD!RzjM(2JLkBT*V=MF(?nC?N zp${y>_@@$jlQE=SSBBnv1fe$`F++cM#BJ#8BU)%XLhtHeg|aG@ z^!y@Mx7A!PwX;YIJsF`_Uu}hc<69Z}H5qy-L%(W;eo_rxZoO3HXV9^_{+V}$em_OO zx1rxs>PJw&ld131vZ$&zZskTkPdl^}Qjx-{a4UtMzA@f^xQr>RbRdOq9A*k1I4T@% zHQv9{MXrL#2u0*>V?e!!6d6a6v8G7iFh{kYjJoR5i{BB_``TO5CD~H?TW?BVD1`L+ zLi6l&v@pb)ziR2XVuSI7qb;monixGT~Paa{T@PgSz7qW-r8~&_l0zJ*FSW2%^z^q&2rX8b9N>3K4JJNzXJaEK3tf0^kT((beEqQytq%xYvHSS z|2^2s>)dkV{r7Xs>!*FlYwbQWuP^siJToY-m+$H-?VvJQRWSykZlRGDO|1m3yN z&0Su3Lj!fGZ(qUw7m8H0Vd&7`lxjq&K+DkO9vP~otK0&WH(OdNg`Y~5nNnrz?@-zN zyJ=|E@70>Rl6L{(tRv60BHhF98)J+%b*S9ta;V&<4wWk#*_0V7@BO>xZTic?+fOYl zZ_N#FZ~jGZKJ<2(>CNZQ-<(6`SPEY(PrQ8J;>hL3ZE2=Y=l3t!aAO6#x4XPAy!SWF z-*}9n7B;v1-Sdg@{LkAZpYFfGU)SHvwhR5uP1{v}OZ9y&wp8E#tCr?!41C5YY3}-1 zrrBJk`RcDo^W|U7W>_eGtj?C|!T;Jl@ucVhM}^*m@3Qjq4G(-sq(t2 z;^X(-d79yPg&w&hBER26Sq}DrvH!J#J{0^KLPr9~{O) z|J!c8{So^8MpkG)BlP1k^t%lGwiWt$_o1KGL+4@0F;NM9$|$}a25Jr zYkybQrGIwm?T<9yYiOkzWu!Uh1k+r*3u&&|Ww!qpyIi*aCAltH+n0{jvz&u*#mkf| z-+NDHiRUMbpEAo%R+cSy@g}Bf+n1IbLOr=t%c2xRjE@9bS@blrxK(D6y_4(RcA8E4 z-cEJkprZXNpVO6|g;IS*sqkH6|L0>=8bzhsEu}#_D;j9G>Crz$ZGO?fivGC~{V5qe zfYJTU=sq=e>f>E9s<$3>KZYIeP@+ySqV|?if8K$pxjW39zu8gU^?DWWexiQIVI{kt zsNZq8)C+gd(K|F#bzo|KeaqCl??_W)kJ3~vn({GC`JCMzQk~sTs`$-@RT)Qpc)RB4 z0vvr6U^zko96fV{j{0xsdjIWaGj-eU_LLW)ht5LikxJ-nBXlnrx?~$d|Gmu&?buem zTk)8x-MziIO-r*UhDuk|Q{H$m(p+4`G)FVdJIyqGlD285yg1|T?b2NG-0stt!0$K8 z(^7u~Lzn0Mt<-0{B~#z!VCrAxA@vn`&eX4ZC(o?m_1uoZR>4jKp6vwCAXHp&2HMvR zv>IW(x*LFt`wqX^^LcPZ=x#p7s9{INP4=78%@xv3$yWB4t!6_wwyF)$2&)VF%023E zm>3fl)qi;KaDhwJpOS1_HQ!%iOtHJ4<$L=Z#{7SwJpaDe(xtKY&=$?! zZVWu$QrEKQYuJlCOnd#dz+TK2(_ZA3|I_xCZPx6a!`R={t1Nr1jd9DthiETtGwh{q zHtmhx{GYW~@t3LZv%U^B{;t z^_Ifb*hTJd11ni=o2=(#$7o|MC8v!`Wlnz_U{0aTDa6bvu>K~u7c4(LbSH%V;7a8c z10(c28G6q~gxfEw8-@y;ICxkY`H#Gb6%eZEQPSd^xu9LP5*7>Tt^(}Db zZH3?L??pb^E9o$=&#E#+o-aTuf;m4lFa}R?givt-Yp|)Sdfoxp*7c+H^~Ozuo1l@RO{M zI(IbO6SQiI#fR*J`P^Uy&^} zeLq|3NG{jk9LO zP*7e-<7It6p4%fy9us(#BqMs=dab5cyeMkATP>@mhrBFn+J7Hw`lc4eKJx zY`_qqZiQay_gQ<`qyHawPXZTJ)&00&SSDyzlv-G1R9fT~u7RQs3PqZVX2AdhGYQNz zGbq|3ik1^;^l4_hZKjo_Ev9MiiI%1<7VT!+Gc#>7Ta^E~+k5xj``*k0MnAuQzh55a z-t+Fe_uRAHbI-lc!lnNBqnbPL6sN&tx5L@2X2)}QE%t5bY2sDaDm+a|TXoYEvjFA) zMQPKt_92@8_a*U9Yp{P>qxq*byg&d~Cg5x$l3f9Rw4?V}9H!r^OhGpeO3EHU3$ByPuPWDN4MxmxP3m5a9DGVg#USvkg)R? zn1nT3@OhOjDhXF?;YrwPt5%aebAQ0}KsG3AzSMwEMgv&uP-Y(9A+FOd)1@7|=N zPGBxj=U1CXo$qN}>fKK;bspb@sPpJ1l{#}aHOy99{X(<6liyw6GE~>g~7j^mGV1Dopxkc>AqaXK|$4-9>S^l3wJbyO&#ck<hjTHr)RA(fr2`@o>Fs(Qr%Gs>97$dz^2^>Vy#@@xDg zGhYYkvaAez&J8F8g3%6~9ZNJyBNA5sZ+@q0vG=`ebc1Yy208UFZIE}|O9pwv`*@Hq ztU-f(ZjBmye|U`?drvcoy%Sll(7*BCDtP1R+#B1|H|~26zi}9Tqg{RDnZ4J9+6C;U zZ~aQt`b$T&)<2p$vkW9+(9ce`-MLPj`i-_YOZ5`@=Z#J#?ZzP<^0{gZE?*DriSor(M2FpY4|);FDj zntuDR*7W7H=|wwn(_?VcqqL@nm^VF8-}EoDplJ`+^bhxtriYWJTj8cp)tdfi)$!l$ zn_Aib`Bl1Z4ny7C`iIud@ieOS^t-s5Wvfs({i{^fT(F8)&8u{9oJD|L)fM_=dt+cQzLH=2F6&(gxq)}V!*1&a~d zlh|#+lTe41M3jaSw;dh-*B){|(oL~BiN~bZeH2A8odLG40vmD6!Z_{+#%X(cg18Dl^0ga|nGYL&U z*h2P$U&i~vFRL^?MK~gT_DK!o$p5j)pmWxjM{# z%NuepYe)TCZ$oc==qK&1?^TkwP9$&rVHtYscgs{)B2wY)jC&QJ~fPx|Ib-eb8P%*JW|B29WdXm!V@0|kS zTJEt*4E8zBSXk$@!m+OH=p|WNdO$~AdH`>IX4vRCd^C&>Vn#r%Mw^?`(T6a>pZOb_ zZiT|_v3P$o72by*mFRR>v@QpD>++*uUDm1ArN+tC7H4^u~^aDpZQI+z zC5CRqW-2Ttj@hxY17l8ZET9Ma!J=iv4BiWbphx@3?WM}?XamCFB>wJCkye9pG{V;E z%1n6^Z8t+FT^)XeW=IY{+}AEH9yFBsLft#>`?3J2Mv5r zvK;Fc3ta!$r?SF%Zi9gI*9$_yEWu7GLq%V8(dR-z)N)R|B9Lg`6{!K+HhK+`ZjI1X zUL%A|ji)_qCU$wFIPCkxVS87}_WJY15!EA&;ke~BCz#{*2p*D>BP^(tcR!YRvi+M< z(c&+pqSxM*iblLF6OomDn0C_$v2r5()yM)w%FVT~tH4aCL~^F)RL7n(i`nGm|lA0_AtCX7=Sw zdSTfX$ ziFy5R;LI16iSfLhW~iy6QmtMn6dZh9>PnZT@(d<_6kd@E_l%umV14jxbR}1WDeYcI zOQp|{mODVT9Gwe#Y5#OjYg`q9&Fww_h8Q=k=$u8O~bazn4_Q3D~}aV7(Kc0 zbzwd|yiHV6g-EaBeMaWSeg-CCCGRIHr#@lOzfC8NOyBzC36|-*7s^y@@l?3=dOLBh zUJ(Q6Z6B5CyjEPOeDOZ_a?9ai^bvsQv|w67^TENLI6{mDn9cG54)bB+!+btK%#5H- z4HF;Wr{rgGO7hc#`!a)j(t>|gU7ye#Cd6-h2sBU5!1^xkDk>CG+#%c zI7&p^*_WPGN#w8Wf2cx7qRJsaqBpl{B>KclNc7+nm_%<>;QPxewC{CR5E8{^1iz;D zB9Pm;jp+3Iv&MA#{8>V$aEHOE{PY*)H$#;0)5Orjry|BD59ocs`We0{6LnO}91K*u zt45>RtvQ5h`3o`C(k3FR4VKvs zh05k5r)O2rw$jj$Bhrrc$?usjEl)IAmx->ti z_=Y*c$bOX9!zg5!FB^8$iQ*-uQ3tx2EDI^jg3|8ZV}@JF#ns0`ZY6hEYn7i|>;N&k zbpkC8V`=(Oc}CTmgbLIhcAQwgzh{qnoS5D5!=0kbpcjtda`XFpSR0A>p*HF{W29lZ z>nyZ{B$m4LleQ)`xcEZz7Gh>>#6<0qkO}Nxh28`L5l=7GX@^e^E5kP&tDBXUa239) zLUtvN>DH%GDlNZ0LjpFJ0_y2PW<3t4fs?U^yVim@elh}VBy|XhAq=RzVoq7yE6voFsUDQ zK8&p#f8)pVHk6(|;TD~{a9)3K7qZuB?n2=h;x6=9fZc_jx8VEeZ&9;{6K)~d!vnPJ z;aDwuIGSY-H|M|oNU{yp)BWULpTh>mnRT07*@K<71IRi1kyvfz>MBBX3!jY)LGo!o zEyi<jwUb#O<+GdfeUHN%j5~n zLKE0>sWyS%*{n|BYCM4%rMe0Hsy9sFIcu~D%($LR;2-nw1fErjCa_(pI)PE8WCE|% zW+Gmj2{b?JwfR{o%+F$ee)`#Q<{Qk9y`l4S=q@rpE+kG~Bu?cS>P)e3%dR0ecum{9 z;P}ss=lO=u%*JBf%nVLO=ilGZW@gg}dj5Sbo|$`!@%My^)tR}q_#~Q{(RW6EW@c$K zlZ<923C&CwUKG*vz)T4kl*PiNEFhBtGt$M5KTZWRlHAZ4**t^H zNVc6AoC0KU@Z9hY_O#8jEX+myZT{%Rjj{PPZv7oHJ<i)uUp_qp+X zes1kPKlgvp<`1uojLnzWU;Bu5H8n{KZPG`yQZgh@v9omk7dyNk7r|= zZ!SbM-&m;9{FOqgZ8SQ@h2#5lv>au8|8<%gdrY)S;l?s|qpa>GdcvhZn zW*+VeGqd|8ZD!`$$;>RDg=c0&9-5h~JauML^ZtwGc+IVmF~^OPi=mGKy(4-=%2( z?*(mU9ve*e|7PNu8JB}*COb!+ne?3hrQnFa{G<$y{Pb|cMeC~@^>?Ty!UX>NoHl_6 z2ayS^t;7?!BgNjHGPnL)s+$WqFcz z;z=4dUa$WyM*44wHc8n7ss5XeC#l_dG)Zm8tCQ4hJej2Y`iK@v@Cz+hXlFArf^}}$ z@ewS1$`$2>q~fc_Da}#V1A6rqsj~mopb`cQ!$7eNS|C2(9y`=&Z zd(2Jve!H90&G{iW@t@~zjjz`rxgUmK96zXZ3>>RV3+`#;-C!i$;3Iu@7D*=C{(vyb zKxO|iMf&drB)}+}bEE7^N4aMz9%ZK@G|Kaf)KSJ32_GTj_29I~NF2RS(*-_;5N6B`(__g;Ze8x!<#y1`SF<}(Dl>Gt2 zZR&tW-qEg@E^n@HuaGvp&Z&^mqNYh`Pu?!0-Bc}?4f~JuXw4&1(eG2`Cu8q1+F8#C zZbN&wvC48`mtU!vCgEceZJlRaZsRxmlwW5F&0SX}6s(^t@1EXfP-g#+T-HvYg3|1M zqrstUgfz6P8>bhdFUe@x(65Pnj+ib+7(#p4mf){4#|o~y<5E87C4VbE*F#k z($dK`ejM7yk45S~n%;G#?0yUpkG8?z|7|Dtf0rBX(#HwA^l@4r+3h9xTOqN+w?bmY z6hn*^Enr29+3jbb9D~;Qel28@l!c|%a>p!Y29Zx%u*@L&jtnj{sC!y)pSJ6+%!+J2 z5Yjy}X4K=Lf;ND`I7N_KLdzeGVyR7OpdZmHRhM!8#Ul|PN*a|?q=U#y${#rvB0~UW z-K7OLL629lVc?TQBans;Zm^Sk0>KMnxdEin3m*xYAV4|-x{&L~AvwR8k{hHz~U-%EF{#$*DIlpUL95Xn4nc}lyk*UGFIUXdDI(AglFHA zZQOtb@^gK}{7biNrj$`LWD_vWAeXW06uB(@Hf}nMa^D+r*TCjSjqVx{=D_Mf;z>M} zl?QJT2y#R)@5)soM(6uvlC?!vdVw$jNAES7Pc-$F_Zj*<{T??ge(iBXzIgXxJ+@LZ zQ|{QdxkB$<2EJ80m(hFCJKmljdCj*cyyp90UO3HXH8mXkD=U0c*A@yn;l7d__?7 zO1GI7wR{89rhOHWO`Ar8nB$fuZJ0BRdThYapw9O<)J+f*y??sz_)>X%VdSY??&X<( zif9*C-72Xz^wj&aWXUS6l*=yV&7;yvZ2g8Jqcr%a+vSO0zCaS&IaTue-u78tH)Ydp zn>d(=Ut`*Uj|Bf_Aaa584u2w-9i3Ypti>y~p(TktPg~LwF2>U|5?sUG=q2!cEOz;E zQ?VKN-!!sPzuyb(IB1m zJBNw4YW+s#9pu7F3nx&=S~?om9orYYwn8hyrwUKt*;6ZFKzfIC_~DL$9t$_i(S=%! z;cOgK#^jQ*c0@w~b*A#wuk^#kk^4%XdI)8(#X~6F$zAC5=*iNe$ga<@{uLeS+B>ak z1Ur9$DhjSgn;eio$K-%~AkWg__+Y7@Uj5qj*CU@|_+Qdd`X%V~~ z9q70InWE$Q0dw4;1yvF1Pp*gWyhO#7z#PN^KET zr4jP*J}hjUhZp&nTMuv*c4$QXpurt%_(u#7gP?@nMwwi_ZvTt!upyJ1bn^-1lj9l@ zEp(p*lVkqr=L(y;hZ}l=#^DrbZH6u%hgK7zuQ_IuWO~i9O?MYdM)1gg<5rSeEscMa zHl_K#@HafybLduYc&2X2FT!@5Qa!a^@rC-@p07(HVtXPV_;5`)K^?0v#U8gP_TiV= z%7^13AGNR*A7`6(yd8;ko|DEv9Pgn9Mx3aj>i4*}Pn82H));s@n2uM~+8Q#-U+(SRFd>bx3l#7JGDk8?Td|{}21SI8Zfti#$ZkHJ6`RP(o`5nSz7YBrb!y^m>?vL|$ z$`jX$`5u|a>VoyW`z~5dlF+{1)!9JGYqVSlz1d8Rfj&Eoup~Gz9)*kcZ{(n)A7D7E_Ishpv}drL%5g7U%N6?sgf?CEKZ1 z9v&^s;?455>kpY?5#olg$H>BZ`b43%dqxQbRYF_JrPo-E1|9b+tJY0F!FN@jCyLO4 zDe~l|%M#p6B;p@pZdAK5azhp41f;LBgo3q}Jl=-ymRwU1+1(Q5O>qSM3WS2yUZLP^ zar65UL74JoxaFZSREARghu zbNsyE?SfvzBRns=uMz1Xa(Z`h8U|h$d5`>>F_G9uMEf)2txSN0g-xyD!sbq|wT<@h zl!v~yERp!5zCvYs1s<<)!`G?LE=$|WvqCV{@TE%u*5z&mbW}312jTR&@ zt{xINQdER{?LxtOd3w+JIOzKU`4PXm4W;nZWEf=+flH$-9buV+@wO^x%bl6QEt%?0 z+50H2B&bZpoi)u9*x;_pz?Ze*yQdB+?+ii! z2i!B)^0vMm>x$j!!R^wfKI^9L+wVW?gQ?sN*P*pXzO=bdwQFM5@rtT8cDvS?G=qaR zRtR&KRl+#Zf`1#3rHSLeNR~+LExvGF1oJ3k#u??lOcu0_5;s67I3-0guOAKM~gN85iVAXs?ZziZ=F_igxlNQLY>yAw1@kimE(P(E`!w{y}c3Kz3rE zNSEI?P-tnXSHk(*E){)zlT;K)liistuUytaCRFcysi;tl$UP$vM%m1}ugVa+G)pus z_9x+eO8a5*>S2<)ShJuo$aTG`_|lER!5#SP7OR;WxIohu-)kQ~;zB2Zf$70olJb#8 zqZ}mH^P6GFj-gv7QKAaD+LwtE`NpGmjvNIJ>UA;-g$v#vBv9f$c{xO^I5ZzmzeF8J zjuy=yE6xYMzEFw`grwQk!Xr(2iB!_W^~g8beD&Z#R%aw~XCvA;8@gDj$JvM%3b=tu ziDqVJBYGhLH$NLOra+4E+QXcUNGOn|OgbB3lW~+sGPwR9hqDp=Zi)EW2*mtvT_>xwo1>dydYZS)8sCU? z0mMyHud zLV}{S8zpyn;V@ce`YD%dkki3^%H>_cP}-+l@b_MN3Idp{^JJzBtF6i=dv)pjG?AfU9;oaf5aPF}2M;tA3ShHRNL-^7Rf~kmg z9uy;?$zqJ`V_|PnIjg6ce$0@qFpa!*6HEn4zXN_oRYL{;Qn^!#F=lAN2t~QV=pD*( zDZt7_FqF!xWR-O7we^sZqw1m|+_u{~W~RF1h|>3H?eJ=U9Nf2JtSse+t16)iVwL`)>BUok&#!O}~Gf-WK(-(cX_ z-z8>@EV|Z+Kk>~-Y7FxNnIUL!$lhIOZj2Z$>cs~qnH=3`u!H|a@8jV3lt*%p<@qaw zD;`eeJsjl`ojZE@W&E#&mrFBzyKYD0TG21~Qg9`d5}B9&&X(p5xmQSC;0QDk=gq7$zuhNPT~$ROIK2VpjaEj4;@wcud+7LxQc0 zG%DSjV5l+MUnY!fN-ud7hjp793t0R36SppVaCcg8lgd_cG&V84AC66slg=>hr=pnu zLAG9W-EgC^-TSArQRn{<-NkmILAWqczjQui*Ho6Se=K&=cBmRVIW1TN;5>XVZGYK= zyVkMcw#SWcu?Ke&|1MZVtzBIpN_m;Y5aZ-NF$-CpB3MY&vF9(MhhVg>5feCKsL6VG z74PjR?d4Pw3};%1w652~GhzDlNW&f=bI=`a zm|V_nn6SCvzk7;Fw*|Ut+vla5$G1>^P)3cj%!W%nUnZ>q>&7JXSD*M#V{f6x1gu8_;fl=Q5diJbJ=oBZ z4#;H0D=!RTpT`biGUBY63_RKkO+Sw<-O-(dEX8FyOTk~y&6$Y?d>&hA%FCV7Jk7)f zBK}5t@WZs=TdSF}U|!<@^c46*Xz5qke`izOk@qh**=$M|6g8$t-tYa&hL5~QBUdLF z^XG+ZP05k>Q&l*4pbhtX>PVAk7#C|C2>(H*(?Sm-Zms2@NhM@ zL74@0u6z3-dQo28OY&dQ@~^oVSMT06B8I^9K-H%177Rh0B|{LASj8brv5G^6v5E&e zg^yJvh4W348oIlul#53Es#u|*kdNjm**eJ`ccD>0Fs+@QZ4%D0Y60x3_-H<$euYha zK+Vjd8yo3Nd_dPm$mKiKih4Zqjg4=eZso>CMnsD#M7B6x{Qoq8i1!WPNMI78vhf+UTV*o(d;<*sYK_yt1wPb*Xq1`j%P!mB2kK+aPr>?}!(k@_0OyqbXJNew zgZTq3U@skrqbU1Z(CA84)cPIdT?vR%sfbejuusqww?-G0b=5}@gI$1dNU*{P$LcQ8 zTjyG3hioEzIQT-pwow(;n)H}_$coEi({ai%MkAgQ)iR90JWSj1w{p;}}H=R$a`D zH|bkrp$E&!FO2BU9gd^7h{E`c&!mT z(_YsZT`H?z>qtkbBU}pmO{5cCZ?z8W;Wh5S)kIKgsi0`tuNfWj>T7>z@=BtaiOoKP z|Ivb4x2mWMwi4|;g}(fJqI*2lo@@NZcx~lr`S_Vq*i~{$mA0qLWdmXxWp*Ce^8eoKZ2ccIJKL`~er9L! z=KreM*}zHYYqn5k=K-yM$UnpW;7MS5qU&RNG$Gw)kR#UfLb>dhCURN-)2#})c9VLh z{wDQKhRMH5Ur{>yk#Qn%b+9l}qGA66KH6 z@{Kn*cZa4w^DqLVR*vXO}8ASrT1PDenloEjdU_e z8Yzn=gxKGUt}uTClk{s$8x-;F5{5a5$?KBvYfP*$hvu)2y8XX5oIK?`WW( zW`?emGBeC^?@6$ZYZE=3{9lxe_7Tm%;kMhxS-Oj)hh2Z@{ii1NzUQf_O&8aS;AcSo zWjBLb`3%T#8f`uhI15LcG27f8uLhhiQ@;yBM%uKA@n(FVe~;pCOt4TB6$vszok&FP z;~=^#k3xuTat3G0$T0fa8Y;o{x#y^UcUSjR@2)0&e4y^Ar9OrU`ZiMMbt5{Dbg8+; zE`>WWMK4kypQPPsdE1Qb607cvPphNecT|nv^U!8Hq`FcxA65}L!NsHH#d#Ry6V%#n!w3fR}pmEcDFM+!f8mb z@NQe}KSu={hMcXoYH6e`Q;sFe)SKHiRkhutZBC$h3cU*^9R@NQFaG9HU-Z#4Dw5x; zO7ZtJ^|hf{MmBWYpGTPuP12GbwK-`SYivl{{4N<%TZ0X0__@fmG#7nn zAAaPgVrWc6rXo`OLi(7d@8eUgBhp6`1w?G-T3x_&(^tg2`8AG_%aBT5=oyWP|pxDE;=4Npf2T z{%#aYZp)0)9KVpsZPvZl@S5(AuSF|%=FE^FrKrcq~83~7?MU_I2Qs4E5FngoV zspmJe@wGyhZrZH@70>8FZ`w^GH|;J5A2bNH;ho_sgw?3e*^z6I!h7gl)9|*Ga%SV> zT6S#(p%m5~mBMk|PLV@&H=QXNfvPyE=*U@ud3*M#FrQn*T$EA5%ap)xa*vjsCikeH z_^!_st8RTZ``9P9sYY@FwH+o===Ix6U)n5hFCl57ilmOmJSJuJ-)l45TZ%n&lORH6pkE#sQKle-_q-kdq9&O3aDCoB% zNDS~PI&Qb zEw{VpavRR=z8hPWsP=$f6Lk*!OLMzVg+CGQ8R_)4*6rFKMY70&x3kdPW}~^y zMRV)Qz!~|Ko69$)j!7MzIx6*geCD6&&UiIXD}x;fV}~D*^2{_;p6Jj+SvQ23#I#E? z`y++@VP}72p+EMuX-0k}F)c9ESPK9KYW)&Rjjn0RzrAt|&Rw?!2Jz?ra5_7GWFr~1 zA0Mh&XH)*Q(Ch--Y;V5V1!A))azof0#5LvLp&qi+ol#sUHb%}Ue%OR-3~4wnx#qAA zKMUh8Wd{|}YnI{HT*SX-nf^7zT$Q7j0AJ(didp!$B0acBwh~j2BqyWOib?n&2u)Or zSAL_()9SlA+382twX72Eo$N#DooKF@E^j1D_qBADNajZu7xK+&Q-mK#5gtg<%@=#1 zT6-W_cpzEFH*qak1|py)2^~)oEzwo<^vo5y(=$4A)J~mKVwd)PcdV`sEjPjL-f&~; z_|zLyZ%WOHki%9R!eP6`io;gFgI;tIP;**+mWK_x>I!|WdpEu7D!XN5@45^{RH#8SCo60RE&iD$-6mGyRmsv`IGm zBNy|_c};mG11+wX_0xJ$rDVwsVflZr3HOeWN+z3R@HOFDpkEv?ij4sV#=nJEO0}|5 zs%0JJedvDZCHX*3Gz*DHALU}l8U2+_{CB3UV(~{`>{Tz8ESkwNLT9?B@!ypR14MQPq|$U}cM{ZM~*=vINDGrJFhS0O6Ob z(4wK6a4LHx-%+Ai@Vt=CyM3v3b#*k2%4H{~u?x^aHT_XZbUIr+^hM?PTJfCE<(~?! z?Ex{8kRgVOs(v?e(om42NfY5Fuw8r-l9Klm_n`80OTMnxXHuicUv6u!YK!2hcz3|w z`_w>`G8~*lA?&plNsf|tkPA$xw+(^+`$2Gr^2}dH@pTiyuat4ej@AW#{PX=zS#g^3 zP&Zh$OJ zkt3&Ccx03Akw2`^uUhxW`-d%z_vz z;gS1vkNAIAC1NXxG-awAdF`Ua?B{>oazIc;-o3P-y1E)KgQAfYuj(4Npp&igc37NE z2BTb8eFSS&c++5z^=9_jr;mCWt=P`YdYNk5L&yLJ2gjn?sOE;xf}I%_SHMmYGiJ0D@5|V8m+} z9SBCLfRU(S{QVNfXf0qQX&AKxqnm({tYNGm7+nO66g_LHYyW7Tna;eQ&DVt=s`F*L z-;kMNO_(Xpgqh-Ld7@CBNXwIi@+4ZGER-kH@)V&w1u-ig99lGq1EdVuV;&pezz@g( zDPwg!cODz1%y@BT92qZjB&kRw1C=Y1$VlaiWHMB_B87~VtI!6?^ZAu?8J`!K^7(l$ zV%|9CMN>X6Uy3oBzG%wlqY1{3OHKK_2f_GgsVSegB^c|Ln)3Of7cj;POHKKF1Zm@b z0V74jNFo^11Pr@|aW{F(1OX#U!*CIdF#<-m6={0>KE`ajHEFv0KD{)>!HgEBHb6I< zh*O-2IK`WYQ=%wNtV2xx>|#YXT9IWUnAr^xOn)__)e=(3R_g^bw}GZY z6Jg=;(LAr@TIH2%?`FI**wk~# zTZCt^Xpt$ej3O9Wi%fZ?!!sCT=^RsD`TJ>%ahA{q=Q-38jN!9QJ%^R#Ekgth&U2Vg zFxEU{>Nx}m#?t}@=Q)fZ7(oFe%Zgv{)hxyj{cBZzczc#1AIFJ&Y=d%G218Yc6zq zZjNZ__zr`rX6ZOTXZGo9p1?S-KOx}6rPef0Af&C&0UK5H`>7=GeR{MmgXXRrRNC!V z6~Q?(4VT-|q9TY!Q!%UYF`6YaSeL%~1a*nr0(ekqvR_*uL*pH%%`jddr^c)$-is=S zS)mLM7JkAMRb5BTV3ubd5LI~$oAqYu1qdR$fA$!j75^+#Bh;MCZQbLhM(DxEF~&y% z24{qJ5R5+r49*BWPcS|eFgPQ0CF$u50fRF_ZAcqG1Wb+4LDI%xp$*Omz4Iu>xKzO4 zmak37TVe!^Yz^Zp@|NEp(_`dP6@rwLiOOCuoiWbnojl{%6_uCErt5iz@G_P}#>_u+ z%#3A_*|eWI8*wG`+^)Br`LXR~cEVDavfeI}Xf5 zA^Jl1Vh5{FRA@CxxF%k#fj%Vi&Vyz-Gn07dK{JDyJ$UCqTO4I3^Ui};dvi8^bBaNq za&e7aSHlsNS2ZIln5tQUR898-p;e9dAuJZrGfY*@gAZejc@LYanl7XbUxlfvX+_#d z7uw)dO$xzq2^gHJIgemu3K*QK`Iq$LQ~`riHJ=iUgAbXinmmGGpJ=LTQV7P&LK~c_ z=}s`#lnJVa>*<3S<9&}F!xcgqQBm>V!9*pvjTe>G->Awc>yG*vRulI+PSZp>H#c5W zL*MI!U3I*ZrJ5v74GA^LnjR8r%-n3d7=EMW8JZFjR}fjW$|XEgP9TaXQI=Po<81qUc*Q^^}&NR&iP)nN%eiRHcxrWP>U@sY(&6P$%q? zA5SYXctfCDsbukXz|K8#J(&Q*HGxip&6>hA*yTur-J*onU@zW}WogJ%Qw{d)EN{%$%;5%TF%Ij z{5mhre}AbL=ceWL6=xR2G>mS>8HqEh=)ik)Er3fOXz4fOYu`5qt7R`ytwrJL@zE+`Tn=mH``~Gc2jYOyYYTlRtVo|yiXTs>iJb@ zx%J&cR!qWbhm+ffXJd@{!Y%_Rw@Cz}M8M$Wwt!&VBVcfHJCI=9V!qq4wF+ajH{b18 zOvZ9Yo~hhM6O4gE8~o~zyk&zBXXaLa3rHJ_1PpHVH<4ge2^bavO^^kO9w|}X z$PbVkx$@I6ZshNG5xsCr2sg5Q5aXN`4BYk;5q@IQ?!C zaFQD7j(%0ln69VJrl0b@D%Wd@Pi)MFmM1jWd!<8bt`2QnEkCpu-qXp`7Zd6!Pw-qE zKR!+d;`#A$(bfchd?E8kbQZJE1ZFXP$YM6D4Q(+~W?~+xEj6{6?mIC?!JVcS^B8HP zuYkc>%xA zn1ux6Rskd1f<1Jr`2$;cd${vm%^tc>GJD9HGqs0ZM0?1(oFtkT#0Y#mMTV6KiNUFm74+4ao-YHZptGj9cGA)wsQcjN4}) zgfVVsPs42g62H(CjoX0f7^nC25XS92($3Q&4sYC+5}es0PGTbs*AOS;o7)!id~?k^ zssLW-Jid$3#)ela3)m1=Jz9Sgf`mKza6uQQWt`>S3p{5%r`hLKhXNGo+nZxW7_YE>j$b9#gg5MY(z{!P3M;Z$n+TlVqPxLdhgPDqp*|8TdK+p@ zThrxtJJH`@j!2y7Z6wn7GiN7P>=+xrOKggHL0&Uwr`%-iKNla>7G6Ua|8r*<)TPn7 zYJ)nuV?1|(K^@&Sp1aVXj&2*zU2ITyJ*``2P?t^XRvOfer*-QM>T+q_R)e~NkbWNJ z**kA!JlpRPm1l3c@x&xx6T{caFdm8?iRC$0*H~M-+4+)oMiEiAZ7!WJVPV&7qlyHc zam9G7OMHSQ%6g3|l6l7E-;p9Pu8uC?=Cmdlg9HrD&H1SeV_YU+aBfaKX``Kh!MQod zNE=5dnYuZ55{&N!j9e?Z{11<1s^#k%RkhqX*4mjAS}q+)q2cVg6vSvzqj zidi%ROJJ=0U|Jm9bTm^$fqimDRC6>C7Ye99~Hkzr9=S@|| z<31wx<4sk^t+x=zca*8>xPi1$BV;Xes-ri-c*|!qQypg$j28tAPIXKo{ix11RUM-U z#xV2j>Fxxhi-5tYjy43NwSd8?jtO3j@sHnBb)*xFF9Zy3g>*5&*eqbUtoYiCu4i%) zSft9ueb*cL+M0W+$^>&A<2ChS9E(d7E4pY3#(2|8_OyFwI!37JW$I}sYkEeg>0{(- zrw0x8xmX3uew1A8EGrIT|B;N)r#+)TeRSbS^Rt}sc9XN5iDnw8i(@(+zwQ>PK(Pog$2#X>UOeD`2)3C1WA zFgVq;o?v7M7@TT)j9~N>FdA?YHS;>gFMXd@1vB}&<7@RB8}eI_#X5cGQ);Ixvrd4O zwGiUFEQ@i;K~n?vY0(I>iVkK*Fy71vCYTw)M9~O} zX0s88idcoTv&AaBE($@CzTVJk8FX zt>DV-WICks3^E&Bxv@!|KPmSWmAqw@fWaxt z8%P^Z2^gHR>`gFc2^gHRJey$n1q@DEwj%w=5imk4ODdJO*_l*s)NFi_-P&z%f&(Ht zCapA0X&818!x8N?J!u$r5W^82G%cy?XS}8)4HId_15ZhsiqzpJYZ_8lo}wv8T{)*8 zrGrx;qZ)KB4h>Q7hj~(+i_eCz%kSucciqwu_hX_or#8wGtrP8j5K6A z(acgR0Zqh{^5>|gl{6&Hx63tXCxxF6u3V=fwLRw*nOXdV*v(bsZ-beN{K|CI@vQ?3 z+7p9JRpds3kuG3xDsmyg*g4!(MczR$o)oq>ITbmcVB9NUa4ND7!T5Bbpd#zBa-rVT zXK9Qno4l$r<(4$PHznus#K~2Onwk)+k~B?Wma=1zGnAs~3&R}N#^mFDJBSVY)K}DD z?;J#iZJc<6MvZF{4Qk?XO_D)PqG^&%l4)*fvO#XCl||F&LE)FyOK!rfb8(I-lix)! zTIHBBxszc0a+4{O4Q5$(mE?O zJeO3C$Me@d*OW^f1ml=Fmt0LS_U4#!Nk@XQIj7NFa$5=`_}g5-! zrP(%m7NAvRlelrI-YwOx?3~AP=Vp_4c0ADEICB&*NTQI|qmcI*8{G|cW&hB3L%lc# z6Z-+dXK2{nP@DU)K@MBM?uJ^{Pk%QQi@19mbU9R>`Ud5JqjedGXEI??6g7;#g=C(m zGC~mIHzi>I9vfW!hPuh0TobRC|Ml+mUFI+RRV z!e~^%^7^k6)9&lHvY-91i>nhW7&X#LuJ50(W&Vr9`R z=5vhiVRRoQ{_c~R&k>FoZMw6U);-BxZwTSl=_^-;zf1Y7Of1%yUT3OOf6u@eFLgFmsqYhv z16ihv?H7k*jNJkT7k9jeU~CgGxVYoZ1Y?bW!EMdVCH+_|V5Ddmok<(_3mDwiOml)U zQ^2rvm-3(_#s$|+ROR9Fq~j9^pej+?@DNTJi=S9vK~y#LM-P3m2&%Z@AzcK*HZZK@ zQg*w75hSHdB}m6BA{k_0A6PKFMmf%I@*RbEyWw{f)(SU&@Rz{pF8&ZR&Gl*S#=8LD zbqOE(y3EKJ83gECE}F^yofPON!A^A|5Q-^olDb}}umMsB+#{6~y_8jOj2+c480fWnIc!OYkBw&R8h!K_CA1`KepXF2Me)q+CmkRBSSMF(&8ndEZLgn6LEjOml zY9}53qexBGQ?#Qxw0Hq+!=9!T?;jmrqJ-Cs;{B<^OOo)KQ@n#ZykrS4isF5x!%LCy zT2Q=oIy^g%w_(pI6ltmsDN82Y^$ATV<=`(Zvq2^uMm$Optt2-mc4jj;$ID#f6|E8~ zzitw%qI?<%MIjK%Esg5Gm$tKk; zNOg)y^(lCG+Jtb`89+lid~GiD^=#nTx@u!LavB0ODpO3Y=2Ujhl?Bi@v--e>unH)58R=ru4_Jev{945d&A*q@(p1t9YQa&)V@Gkw;A}R4E zSBq2$H;eEdzTv&#Yah#5AYYw}StddM%%l6S!SkmsJue8?T9E8kyx;fc72-`LXokgIxP zj8g>+{`*IyjbHofF*qM`62bUNz~ENCqX@>qt4vqE-3i8L0tUD8jUhd~xP$4+w>fF! z8KDhs<-3Elks@GlE8ph{#s$JyxSakVrI>(`%Vj_IAQbDP$KZ1M$B{NZ7BIY40@xSC zsrew&RJK1oPS5sQ=DBH%$3&aPnrPE@CfZbrVGGIm;%(L}wr!WY%7iq_MDd5s+ZPnS zn77YTiM{fxQ>;YF7oWu_Kcs?N7X$g{oMpTsj@SoWwMzsHuN~-MH@PD_-fnnD_6ib1 zmsh$}8)X^2B*|{{sgf#k;@I#;m}I-bjWGD_DR%CBnXpi9;ADf3}U$g zSOI6J2S2a}cV+~)WCY(qYBCq8Nf%O+-V887<@VXvC&XvY+=i}SvDstNcG+hm8qGWa ze{ap04TdOl=C>I!Y3nnfyj|wZ1Mv4&JCw&}&ODs@26`$zCT*h~y#Ca>59~8P3$(FU zA=fI{3_&megQB6ez$x&f1Be*37KpAw_6?qZ=J1bpskr7a#)?f1wm@1v33WFabvFfd z*N(cI1!K?Nv=RyaCfwsqi1F&qT!PNU1*4ur0I0JGIPfv}8_d#~b5Pmrs1NJv>d?=7 z$I7jiq1S7GvfvF$K=y1uC~RT zZ!1gL_Yjo8kHVPq(?G0dpv;Gu6*183=ysiK8CB6SD>e*^S=$`+U%ON6!Cm%Cj--+a z>b>Rm>-OgW;#X%NF-o!r{~|RpE1JyCimmv&b$aD4B&c?HJjvc<8vQW)B|v4R<#SwKp8K zH-xtLYFpMG?W(@ffiWwfQPfXp^=zQqdUykB{50J7F15>y7F1oaR&Cj&$?B`vrQ`)!9a zf^~QXQ|sPCGT#x2H8yXnNMH8agI^N6(ZbkAL(-g>QFR-LdFAHh@D#8E1?)(dfiV~y ztjP?%OK4!9`F`L+Mgw@MN`urlNi|U7SxgCZe2=3<4!U{y!Agx1GrJK=l#R!f7-C11 zNV79aoY@opB#mamJ9{AQ=CUJaAO6TiK@W7vRazCk72}t*;NNx_Ay~N|D4pI<555(i zRMf|gvlAK>AQ}}XBq3@gBWk4pwd|Wl)}2{16luIoYss){lzUP5DOh_0n1oUG;CCbK zvpc4pj+7JF<*KOO_|GGCfwmyO_R9Uyb-R#<1tn#_U~{q!oPIeup# z$59sWMlPDjAW znu5XPOmdeNK;x)K9u;9ng;pjY;YwDm3UH`d=P@k@0L4g*1H$ zI8@&o|BS(8oscM75h`M|Si(#t`C6i=P6z*$r2&k46+MFc4jd4 z>;|*{@9+PA{?FsN?&Y3)-t&Im&wJkUah=K2Erc9b4EQ`$WvO;u`mW8t)7z$Z`z$5j zgn!r-L^)T4zru8$eKTo}-^!*ed)(2GMHHm_y3Rhd*wt+m#w(DwoZp``q#-PPeRB=0 zDIoeM09TbTIZRd%*(86ZpqA3Z$$8wRiLHW z=r=Pa%X;&*rpeF6;mKF6EEhUD8}f_;v$iHTp?@4c4zH^H>QWKlUP>)+Q#8lx;)j|y zTwuqMtTTUlHdKQa=JxWGg)bv5` z#FfMzB+%{i-H_Y2k#h=d7d5M06iA>Hx6vmlMve7P|Umxd@Jvl@y6s|bpdgO+u-Au4fTpZtsj*|9| z3JKlqoR2Qw3Nya9_~l=_sH~HUcIv!x;k8=5sWklf9owayG|G`;(MX-kWWNmU(3z3X z%-r*ih|oW)YW*rIX`e^6558SHe6#fMs#@czS^i+plQynN`NU6lPaz?PE96n9SZ88i zn=ZI#=DxFU=xz?m6%($>AGP{e_A5A452O6X{_maT$FIzu{M?%yfE_4b$yRYg_gPDx zlz4Mem!JRj4i`4#aUeRW2kkw_{|D#!&Wdvx^J(BCsj9uU<)543Nylj34*f1lHeA!) z*Cmm1pPqlkvGw$qqeqNC6@W<($K(`Cj?- ze&bez#8=+?8fTThO8!0k?NE|IcZ4tw@|=iCVA%3-Opcglb*s?FI&H86g^nW ze7qg^PWZgSf8uFJo!kN}Gt%{2lik{BmD9clN}Z2}sxMOuYN5ctW9R~} z<2Z+#=o$7|78RMPc&NAB<8$^U4Ani$?qPy|M*X}UcqCIN;cPc?Zs*wDkP309BS+81 z>8DbPa4!U=4BZg)>lnnSK1IfgW1D{cj#cm23FZXvlV)j~{Vjn!zu?IGf{!PbRbNPi zE2V`dlk67;a>34TC{8}J_&?T1YFX1R7iDvTy!p>FVnt5ODaV)2TKyw9TP zMZ)3DxboKbcmMb*uzySZaf|9waDasrzHFeoWDeR>_+J+Ay810;pQ!qvG-0Z1b@a5^ zjSNxsMB>={^EU8Ek>d}J{A78T=VB^ud}r~9EuA2zx247{IXx1weMx~5rOpPWXg=!j z>o$o)C+gWJ-@_F>Lw(z`5(}~9Wg8}vA_GJ|rG9IMne)D7E-%U-Zmw*VBYF467kNjL z{>0xC&q!Z@O4gU3hrNCq_QkHazBY>^q|^(kHYC|aLZprDjWHUkwmKPoEt7sqiWaFy&^`(dDecVXIip5It zH&Vc_P7Vbl@ZzFi9?n8YX2vJNP7fLb6E($GzE;1oaLg1^Bq#J)l27CI9nW|FVA`g; z^PN_U#zpsVBePw~$nURmjMaj1uj8YaZ&f~=BF3iP>Dd3TWX>V-%N5nrTC4nb&yZWg zYESgbT#+e^M0_|qV0o-fZA$i>w=MULk;R}i7pO{+U~Q>2>^=Nebep4fJFq2Wi)aT$ zPvrNfW-bfc+##1~3hoYfoM};hnmSKSkG&n}ajHr{)MJ%iKC`{;e%G?AXdPigQ0`>J zJ%uC3W{syhWTjRl{G9?Wc^%Py{O=jt#Co4uDaqrdfXB^r+ zyF8_w&BHASmlJL)kCJNAbeiW4P4C@nxs0>EL6?{IFLVn@ zkBg1Gc(vH%wq0>)i68%VH}m3+L|ack&nCP3hmLI3fAot09~89l_RqQG^KNf_j!H`l zuZ> z)Z;@*Ei=>*|HSnzU%yt^M=>1nvQ|%}?dZaD$(U+$Y$70QvdUEFbnBPFZ zne^k=HJmh!oSs!Hf{}iDp<4O#JohK*Ic>rvYxa5XKw8-$mvB#dWFtA!%YqMo>_D6* z%l36lSf@X9d@eQU)f``Lb%r;2l^>VRFEF@rX7JvS{6J}b#hmtYZoVJe$xg1$U6y%e z$9}f_`;>pW^J)g@WNjZ7o=*T>Yj&Jo==o@G$9?u)H3%6#s{IrzOl^pP!n!XqbH0AuIu!gi{6gMo-+w;c#nfnY0BHu+DPO+1|L@`&&U$9_$nU#Vy9j+ma|`S>PeXLs zQ$_InEmb+`7E8^`C^*;8Rvtc)nq1szH^ql{&1UKCRBbiGSy24@J9_6tZ$83dt_ELi zVAZibLuloasc^UAc3+pHjz48Z9RB&wX?E1S7{4l?%TE$^V9)5B{Qc?~*SEZD7q3LW znKjl`F6ZW<&Xa#;%_eVxogYl8mC)VAVWXq=eOz%No^~mVs(BS^%C&RH5fL(e%#Sq! z^(QZ$PS<+loss43vB{8c*A@6&^;pEmph5nah2Wn`ko&RmH>yG3e2qgwIy5@0?(2=! z$`(#6I&JADAVeJ^l{+BY@UBIr`^5tZlaO==QG}{Ogt_Lk!)xaou32b&$X||rA=_W8 zk3mLugEe9=&f1ezjn12m&(VlN!E1`$#*20HXf)yMRhtOQ(H z(-Z?asdDcZ_d^ua~6B)gK~Sl4bP?n~&h}l{NSEso$9EqQM$(?dlAR zzPJ7cnVH2x(m)AsO{rCS9w{AKR|6XpRw`;NEnV(8Qg%;h1$?t@G`MMfeqA}H$k2f5 z>wi2TCLJ2P^>}S*wv1Qmm?hDYsxM9J4U(D_CwBMek>Z1g+;oMI=k4WZeZ@@P{BAeW zI~MilyRay<+V(^1T*$b{;Sm^DZtKQArC#Py{KvEnq};6`9wD83qVh;0OYsr)4MEe>9cJ!~fzWaqmUrJ34mGHA9sALT^3ylDAhf&~lErXA<@FtZc94nZGTM zq%r@zXHLHu&pmwZ_Jvj(FVmK}!H&bCLXV26dIj2HuY+pgd{eK74NWNI&J#9&6Ujjz zT9!h+#G=RUd`mv@VXyM=ze^@%bFa!Hc&|mCQ|&QoNlLHopL-$Bb?7Vy;(J!|jGzqo z#L=(5ttLvO7nU*nUIn?hdepb4AI}jaYvTeA&1Qg`)m_|2v?0R56++9MIeuJ9Vpn?A*r}zIO|A~j zW}RbU-u^mhp47afYa{dd&y}=(?f7%+DPBRzQ6*2miS<@cJ5r&hedgZ8U4yUz+g=G! z_$D%L^K7h2r$I`q)agl(HSRbWlfqleHHoXU|L@PYE34_1=d=&P&oiW;Pr3tv`b{3+ zx_T{{|LK0xsc>ihS)}o}JL1(%GQpeI@ITfNKmS>s^XK)2WxGxIxdf1O}Ew`$xwek5>QiJU2VMo~rPR6a$0)=c4^B349A5$noJ zJ#zFzL+YPzq94DZF;ZE7XS*;TkC#IhRnJ`;zkkj!hU??C9I>MJAl56YMcVswcfLPY zy?(o@%V#G&(t53ZBCaj9Q4J{?&^s8KcU&pYvC}25m(X=zWTkj7?%%n(yD`_kZ+#NB zd95T;;dn*5gqHJY6$)6@$(leE()jjt| zy^!|Us!}d?DWn$U^ygE4U=PThYm$G{9TyJD0O@fJK;hNdzwQacMaVKBD^MROOK4LT z=YZP*^>H1XAie-eaM|%~+T!X#rM#a&liUNtQ|@2DlF&^VoH2+5c?3PsAll*Z5LM{G zCE{Zo5@aX12_jNKScr|lraCSLGz^Ykcdoo76;Y)o>tu@S0=a_R!FpnO0#4yLagZ+9 z4z%fl>j4Gv)QUR=14ixfhH#Vrf?UJLy`sQMT++gD9&#Y45}XKL;t5e8+Jbh#YTQe_ zwW4`YvKr0}B#x`$s-k`yD+I?wLL`W%aFyV3a34>Wz~)ulJV*{|BeZ!87Y9bbQ2NAq z&=YWz@aAEn7WjrE&#vJdv{ooVj%L;Lf zurwIXN4CKQ;dDU$U~`a-FiI2f(ie~#@4%If0f18id{hUL1usD)Cq~@sg`DJxZ~g}y@5^;o7HmLZ(-&6> z3OXjh;;xlEQCIYFQCm`rm;lOWjCuJ$;-T6LypN#d8o-+A^RjfFO-TUaqRB&Z+}c7; z=YYY$z-A46^g)vQY8E5_qAl)plvoB10}FTagn`k#fDuL7I5ubsxDO3=`UfKK>xIaY z8$p#I1Z;q(oY#qm=<`2wwdEE>@*n^}HIO?X3=f?CID1VFw*~T11rBylxP+6kauuW; zN#p?_sugugy(S4GqH#u`CLXvN52gIjf`HRuCru&?)B|>}BZuPD!9Wz zfvO6|y&eD#_6oWXFF-yVAOR)*&&9HUf?fWX`w@&R2?P~_52Zp(2kqxA#=Un0^@AmK z2i)tq0FN~T9=iwWbgG~z1)?8L zMj3(!=Lj60+K|-eIS?m51!C7BTxAzVR>D;ncvVv-^7L`0;6hLXIS3do8sMzQB$x%! z79d~4b%08_CA0WhkkM+c0|@}N3WKNB!-GJJHelpULU1Xv;oy6HTsp`D%oGJ0G^zks zJD_#0ki&UgaDU$3IGF=ECut(F1Hdp7Y{nIG zoPLUrcn#=T3#1AS`3r#)4#CJSxJ*?XjVENqfQSX6v&wDMOHN;K8@8Ejl;;R4Lh`SH z?3j|kqjNfd=RgEzLUC+mAh2BV&lfK=Hz9cXAq`8OA+jgvr_gFd8ITI-hQ2^QBu5aX zNW_By$NX}Ulpx*&Jsr7%%_P=>FkJ9sPQU!&SP(-WwgUySi!Er-0k;QuzEPtyW`dN6!N7sP1d0|w0tnAZ2i84Z#Hha7U62plLA$CC{m8|3&hlE@p8i4je@v@$k3DbgW|6}}l;^H({7&v~K z3y3r!vMO$5#lTnQ{!g z2HFML!8WCE-$Bqu0T$Q6kt@4DvVDSD^thxe1={&G1A){khlT-X>bSokQ&P+xh%E@v z_yY{cI}_MLtm8u45Wqd3rr-#i52Tl?c3WXdf{2ha&;cm`wuj{D&xuz+LGl0eKr_O4(-eUMtHQ%@NX%;F%|%BR(Z;gRMb zWk?l-33XB2g(90%MEDbWVaK}Od-mSDtsa0Vn^=MW354I`W2 zpg@pGYICoFCi&oW-hprefFPDGJ`N7q*Z{a82iz>pgzgGBts<}baM=l>B#5LW;GQy; z!1ys3nh6__B9C&OY_R9*-*OH(_{ZnpW;#!Q=yLh{C+v-2wDdTGi{LGq+f1c3AL_ zrO%AHoW;;w64JpVJa9mG*hzFD;Eyo-rDE;WN11q3MZ;F89h zZK))hI~C=pKA0ZmIH$xskR1FDQAoUCA70+SRk->(ycSXT#fP8OA#(5^Xl_m}>%|Y{ ztWr@XUyVy$Yrxviqmxml79VApvJek{?9oSc@|z@Wks|!VI?JuD)SK3bTz`I6^SN3G zgXc}@RES*lOxY6Ngur&LYuOZuZ?0$j5q1cG@WL^pxYo<)@&XD6mI%Y2(51<0GgXy_hWxsKTZ~&da`A^hm?3C`JXh zvfx7+nShcom$@E(pe@_79Ob!jpDl9(yP0*ePz7Ap9*`4Sx_tI$(B!LFgJlVJe0asV z987_)tXw<2z1TcMAjsuxlb`I)3yB-$cIDhP&Ko<+u)gNTgK^aOn;xe96>Pc1DONMb zMRfhgYg&tdnoHBB$m9Zs9ig#CHk*u_*gAYKG4I`~cA^t^A5*!9ug3K(0?o&2VJp~9 zhrrf}jsvV0GpYGzBVaUlLa3KVeikxE^dN^<|hZk8IWdh8~-HkJ1up=*%f*JhWY?q+T}bG_W{ z9aC9RNp{VC?L}n|G4K<~7&3k|n)B`kvUHKYb(S8v@5;rFUDj5J#gMgZBA>Vqz$W9u zQ53WB=K9|p-WBUgBzZ(KDzoncavkf6a4#4TX2$;Jk%}f?*m|4K*5M!*GFJvej~)eq^TxP}CU&Sc@C#_jsGyFm z6DNGNremPxya{G~7e*!U#vYAxt68|4S&7hvL0NXJ_)=}|-sY&3jl(ExVjz|+@I$u! za89BE3ZX>iH|wBgC8gK$)y!$4Pe!q4;(4lK1O||CtK5Rk^(b!P{)ZS}X?JJ&qq6|U zKC|Y`Bl~2qS1ai^=SH88fGy?#b96$vDMW$Q609Nj042tLK2bg*KMduaagcr)vnelV z_j3!{3g>g(d|=jwghLss?u2Hi$yco?<-}%v$=sXa+WZg+f0;OEZ7Eljh|Lw05W74qQQ1jK{2Sz^u1)929l@ zxYn^7X2NSTceS|&HNct9m!}hHA?n^O9llac&+Ac?^W&{uk5p&ExEQlk$;eP`k&T~s z(31Y;Av$X_2DL)?Hv0pm^)`#G!|a!lray%1-ps#jmW%>S&({!8@3@u|Cw>4~|LO2U z)ksfh0(F|cSkBZC)N(aTWHhDkZ3gzXSX3O|cjXBJEGlu*ae>Pc4_(ZtU{|P{w;pOZ zsyu6H*7juECb5=VYvyaMkk*VET5x|hF60oYjW{2@r5HWaFBx@}Ru$9n^`rj93NE~D z8*_H^Z&j54@7g!Z&W@@rhf0XCHc`d30{)w?xacoNW;E@h>o+wg&VvxPey?0pL+sYQ3f6Q_ zP^1N^gGuJwue6`ZpI4QL|B}Z!=#El_N%aJ!y4 za?4-uIl;*ZJh3LWE7t#0Qco@wezX$KC$Pm^LVSRNdK0G(4#^TF25eecl6!4Yl9BJU zMJ&F$M9jf^o0Dyx`PPbC`6|25$*TU;oQ!8b(dlzO)D)+%>G*e7Y(&p6+$LHvt2}|l znHe$jirfqjwHDxkrIZ0BZz2Y-h|&nFbkC`rGXfb$2>+?k9UnLXLtPQ2D? z6XSdUYA07_J*Q^97|boehRArgc}>PXLBXaIo*ta0lNKqMo#3pp|MWe$*%$CQ+buxWfksedz!W}(xy+M4?H}o8%RH98L`#ndSHiOODQl|^#v=5tZ%1^$^oH745 zLS&LdLk@K~lf(M@C8PZ{crC({_TYm4aDG!1zjxb_$sf}!W(=+M?r5~sZ;o0bMDkhx z+##=@++OVgOz_UupZzh}O_RyU9hBDeb13T4aj9D2=B5MH8{}+lk!%g|2{<(TUm-r^ zymy;FO8GLvscB19@41MiO}DR5>*m?`<^Z!6x2zYzPA4;5c@QsjQ7bXMT7@oB(=XXl zw!)Inq)^=VT)4bD0?;x$TUL`m7gFrR`pxG_OUlAT1IgIPVE?H&=UO@C*^_2%b8oYJ z^dP3F)81`swF1F24mmp-JzKJgKpyvLJJcYd>~g7acEMsv4)H2`-52SYk3G;_Z=vD8 zIiPfoXKVL4JEB1fs9o6wi6Ym_?#_r@#<3n4srlW3&U&*W!ULg^_a;cwUnWXn)6+Rc)Z)FwPGs1FhtVRS^-v%wz~77^$wBI zInxC>i?#si=d{s8@lg+T)=#I&1hGw!(tBUzqWHhK9u2OMnd_7B#Ru98WMS3jcxQt869P?9#s})XE%@@o)>tC~N`L)l3<~Dmswo}*dTrxm zDH1MN)+TB9%6N&RzY|;J$|v0ZRhu{1<@|c{i_z#Wzd6o{u&-4yCnckjv!07-2o$-V zbVw2pxv)-@pG+{t=IN_m=a^Ju1)Gad+)asm2geHMumBj3^7Vt+3M~J{TIfx$);#

gnJCu%w5!ou8CEuUQ5(*xf17Wc`xcOi#lkd@MMGxVbhn+7>A9i7jz zqji=@eSSX&eN}Fd&EwnjL&U3RgqAp`_Zua*KQVr2vVQWL5sNfr99z*$VEVyKMkhJ~ z(fG45L`1fQPqH?h8tt6*LSuB!njTx^BAgqg7=rL_`&}zmM3yyc)9k&@>8NElA69J^ zIe;sXC$bXpt_Vy_b~&VGhL7;u;$KX7#s0~?%nnCY9Fhaxl}74G3U@A4x70lSn|O> zo`^@u(>Yg>TLHbz&Nk0ECHl`yIlcYU$H$6yV9p2m)BdGYb88BY#muW z#+W@DPs~Kgv!5+5EzS7NyPo!LnW+`(m{Xg42HoVjSnb=;(j-Y<^lKwuF>ASy==;MD z#hpR60Q9u!swm|d2Q+U&k&dvFqgTrz=5giOvaDAfa;D*uFBJdBG*^}<%%ZhXTM{-? zKIge{Y<&lCQETjhLgCyVLe2VQ6F`yYx|_&aDQpWQ#16y?&!7PbNi>nTXyJs|5&|lV z^8Zy9>}R^=nNfS20eU!tfltna=EHh3*T-8iS-6?3#3+a-%*ctS$@f*ycQFZ06z%7O zt;5qXkrQJWxS(sF6GB78N<+pUE@29k;jP7f>*E zVhoCGyXylT;gO71f%A^Iik1q#$A+wdsvY%cUc(UYR()-{^my|TL3AYRY}y)xK;&jZ z27R}#(4#O>Sx7zc;MrS~Q75B3p(J9Hx*t-!J?!W6le>@!4X9SGDH2!`MEB3BNnc1? z1AEBpzOMi=W-rZDMOh2w%P@uf2S{Dp?Lh2fp4Fq4L#v|N9u4`~$46`##}l=DGJi1Pm}hEO8M)uq~x|BDND`^Mw}9PGm(nI`OezKB!nkWk@VI z68By#8vSvQnOJ_|wSRD6SfYjIODzXiZVIUW3@RbIsuf~udG^T;UIt<;M`u<-p1w3t zQ|l>_fw$h>vXZ1wgDUI&e{Ep3GQl(5*z<%C_UZAlE_RZ}fv(bw*zaCB1R|R4B(qN3 z_tjc&gB&OvFFQ|iwRH$NdV>tld8wcwuHGu$08DB^-faw&vgVBJzYB@9pZ>o&mrD0F zxYavEw57yQ;KI3ISxGfBN+m#s5MFOFpNxxe8S~aS4sl#Wh_;rCx^7yTwE$ZSMUYRc z{!}d?#`r=3M{U(^^&)S)zwIMw?c`|IcBuDfY*t(`Kr-z|T&$lXzRv4gA64kqjx}?Cy|*bL-8oxZ7JjGcT~TbO^xmsHH}O&)T6Yz!xh& z*oG{sXdne(6aV7?$8(oh*f<(cW1NVckGz@nYRA1Cy#*^Fu6#Kr2l3EA^xbq0U3bwh zI!`*FNEgOMmAUCPoJrV%2dk|@eCiXaan`-A4K-uA8eI7QJQ`gHtF~2(H6hN{T>C(&8q#~e#jiy<^E`f|4oPrE_zs_6 z5bJm7L%YoL)8mY3UYQ}2kB2WH9$dk04IIz#fsFj9IWNmQ-5yf=FQ{6K=_fPT%6KIS z6I*xE(<{uPp_Aqw@v+)IQ-QxWH$^Vbj3%hcc{OCuM-6If|#^nUB)gzKd_*MC<3U?EgZdQy@8FQzL%!}NYQC#1I$7_lxL`;%%b z#L&s`_CV*b_2%ZD=#DnQH+c4x8_0&$VtTk3)E`q-f>NK@cU9!%bG{I2PkfSYIk*36 z7Gipff3RcsohmjyG<^%{&iHw#7n876u@4k@Yt;CpH$LYhyu#yXK`=rbjs2V@&Y0bo zjBK1P>Zij0b|vT^pzz;<+pw)`?!UzTa=7 zejw=G@|Kq#!wF5=Lr~e1F0Yx=50mDtCH4Kd!gmFXEq(pB2%xbD~%EsMo!R`I7 zDD*bhc5r{Sc}+Z1(GQ7lh-Tha^f^I7RR?1BlgsXG3va*hc(ADQlKQ5EGP7P5cxOCT zkzmu6+66rus!2Oz;&6T1pi89-E9vx(bblKwR4z=q&aO_8JB4-VVvT}oPKUy2yoyPu z9E&Nw6oGWT1awBqVv5DRZOd)!*-&wslVX`N@@`jlSNNGwU0Tw~VLcoVvmhlVg-}Gf zO;JA+Do=AY$-Rn6N|AV7c6UaH+&wiN5nxP6(j6~ya zZ)-o>Nz+Rpn<@4yF6C1S8mIQVyZ0>09lKWf5cjs-pN*t9yd!%m!pJz*73?bQbQ*Jy z-MxiJpP?Zqo1GWGU*FD7o4L?gw(O&yDE_)*-#U>)`3)GlerMeC1TvpMZKzBlYrA1| zJF&WzgW}{=s7CWfXaC-tGSs$XXKTc^4c3PtL8o+4qSEGH-^9H7!;$xsScB3j>Fj9? zec?L=ndAAC5Zho2SWk#346MU+_^N-ck~3qU3YEj_r8GQahT!!QnQDp_3hKAE?Y0$7 z987XcDR(Is(i?JtUufeS-Z%}t8D1-Tht4{W1_|x&cTJ7p-cyuQNHfAI(jvjTsw>}z zeW)7tRGXC_LsKLE?dgfHKjc0M|GLKrXI@+huRBJ-(F<7AIuDi=w%r4XO0qU(zy1}n z9#&aT@)#S@on);S%J#nERA+}Bp)Q}Kqr6tz}DD-y5yvR!g;Rao*A|O|wrOl*U92 zLGdhW{l7J`#7HMLiF$|>rcG6@EAF_>VQ+-R`{+l}Ll;La2ruagi=#D;hO9CFe-l1= z?6{j#l^iRAcGcj#?B3>F8i_Aj!?G)?%ZNc)=OmqyllZ(p=+7)_y5XrF#ndcl8L_|g2k5Wj z(F2DWEXAx#xrm>2GDh3jW0cgXs|^Z)Py7Ei>MbI-q3k||y6ubGuo#=NR6iPFs(Z75 z(%AQ1*s+a6y|o?2?k}h!^>PB)Q; z@9Cj!{!HC5xMi!Zy=lYRi-7MEW)4QVEA{NIaM)_V6y*Y1n)aPyUBAHA<#%E1ZNh(VuBQJ3&w5-x3=-=Gx?qB$BVrUkA@A5H7BCPWrQz9gSq?*p^S` zFW1J>#qxLm_fUe=*Itfi^o-=!US#Np6r*I4?s!wbcnEr(*^s5HV8Q-r zX)jVsLLs4vDH{q{<1XFKn~Jc@QCRb8*^!nmxf6~HsU5j94m6{M_4!OQL=2NOP0d)T z*QZz_b(;pM3GX*L+=gHQR%Hba`riksg~7n5{WUsz7v^R;Au&+u1+|R;p>NBMC}iIk z>(L}RLsqu;O4rbr(tiOz1S7DW1x;!d*|Z`It;%}=Pxl;LTm3aeSSp;p%9{Cd*}iP- zDTf(Y%m}s|lQzo#)c@95zw}fJ0d6qH?&4@z0Y@wM`!VVYSkFQ#{e3jSyuNOb`nrac zvGYg_`?P7CtvzTS;qWam4D8{jYMe3GJhO=1erY?b_o~4w<2yE%65~E5XZ@{MxhSA+ zJrFuhg;6amOL~f5HE_J<>6v=L)qw}@`^?^IS&50R@WP-v@5*JzTiA7MF|v5K-=Aa3 zy7ig0ee)xGL3%adLMN;AcM=pJ2UV-|d!Laajn8vl)ot zllsdS3@Pa*4#v~|&B<%wiC9Xd#1e}t&H04S@kAnJhDe=N=IW1d&N-F0esE3_%)4B> zY9oQ8gj|WL*ZWM{>k9M5bi<1u< z36D>3o-_HfF|r>K9>zth;Ou!(l<>a4Y2qYaZ^uHm`VOI#3S{ja|3tm%P#{e6b%u9b zHABnXhFR51()%0I<>rKK>5Yw&%6fb;vV&F6TQcaWgxqp}623;Up7J6fo?R1~+qHYb zY3W*|+^?yz*}lAue#6m7Y^q3n`L}U6NT2h58v7!!zFU#-qM^MW;esBqw*9mp{BP`O z$M+#*uN6>R6ivIVEneL=tQnQ_f$$!`Vb-LlzBQI;FtuaBI=Lb>2{T=|Y!W8jBp*+x z*}opG>l|3L%Ad{1^IFLlxNQFeGmqhF4#^Z>K-`hnETz z+-0v{A+0GrdhKjx^Q8p4tvhnRNX(QiLu=J!7#*kfUVB3D@A|_3buqzmd(L|`;2iah zxp*l3DV@GG$+?B?@X+gL!Dz21fX(J~JGCagBM?V{O}j4pjNp?(OR=AnzgCthTVjDa zK{L=OIifL*&h%cnG_+b@taTf?nrLqQ@)!M4@JQU(5h&PM(7}s^>=m~%!IjOKF zD;-X3uEn;hnqMw0P)RM_m3i zjhxJgS)|A4QQw=7eAQfS#L*wXZ2Je@D9VV*02eZ36 zQhh3HEw0W)Z>=9?P$i97e~d2MKhSoitg{bc>Rz`et=j5mz#Uo*>8X2pOD|27I_9!}KIhTB1UpO#|aQwbagVIKeNpe*Kc=Yy`YZ)2Ee z@9eDgo~#+2qOEwWWm?Ho%hI>bcXs?8-3RKk)=S-N3$_sLVm;6=E4MOn#4L9^hZQt_ zb?Qee8*}17FELoRWbH;uO)}D@lBHsv5YWsG?%UjlGMxmX_JV;em~>50M7b}C%L9gOAt4PGKn3@&KU;kX!V0z zW1KFM<#q(61N-ouIh2-`^e@wO^Tjl&;OTzu^&?cpo5TIBqFusQw0tKf1 zm3zRjzTv>GU{U?fQG*e;bFr`So5kDAOud;w?_8|o&xjdW#{R9AV$u!$frSP!qlht& zy$O3>stA5!B<>Su_-5lPxGVqDeo);Wb)dtdkljbnksXa-8|zb47^{r)D~)@Bb>`oHkUke+9^soJLK3k<280Sy zzwRf_Zh{|2_u1M*Y|*M%Hpa+ugY~rNOVY2PH~+CZe9{??+ntPgL>{75=McpSNL`9Id5jjxOX5>m)@YJYhD#Ir8sfx|TC-d@b~$wAv2ZhNbU8J_zRDK88qRo>NScClv#kO#O5yFYSVf!xc6+Z5a`I zEHu5J8CHtzlvCvN4UB|~Q4O&+MgFgu(a4gG4tj!%Ofqy#Pg$TKceg0;hf%U4t>}M+ z&K89*;RPETf(F}UVnt{=GQ-PvQueK*%M_6YUD=(S_f^0nPPv5;9_=Y8!jzEap2{hc zWvzA6bDg@JDiZ#}Cr5yn_jQb65i4N47(41Y zh2Xm3KUGpCVd7wc=#V+R;nr@6Y_F=$H1j??*rmj|3Frh9Y0K6r*ok7FjrA~n>hZy* zrX>0~H3q9qy&bM}tgrytOquIEw~E($I~g4DGuiP&Rq72)Bp_zQsDz%OT~REP-zcL{ zHKsT8w(PxsQ6ed;Q;~a2Jl&+AF;649!mg{eOF|`7H8dJ1h5D(s=DCYViht+Yu+bUn zrYtb&!0v2^ZL>|L%`o?2tMn(zNZT$*yPx!}?OT3W!0j9JssAgLly>$Vv*&YfMo7%j za$#|ut7)`P|I=EYbk&>*6$7*q%xmUJ#eJ!ch^MgCJux!}lWF%0a<_*D!|qR*p28PU zj!_1~@9$?n^PS#U+Okh{qgaKF`zU`OV(gu_FQWA!X`YQ~G-cQwKY~r-4j_?Cr|JzT zC!ONt7hOtH;1m}H>J%npjdC}H)9@G0zNBK5lYPS7D069;zpss`-@K7Rl-6+D%ngKJ z(H|f=kR66u7nUshS{wgmhM%I|bGz(I;oL1uy}`b#zRFHDo&F%`Xu>{2JB9$XAdl`% zDn;&Wt5&Mknbw+AKmh{J3C-Osz`v)Io$WdEis|Op&&mkm7(|rg19jX|@%a=(iU-ZM z$jW#+30)Y>F#gVD3Q#>w!hcaRRRB5Zh#v|6g+GfJ^hr?81kv1I7jYvw>E5{UDY^Ng ze;kSg&9-KOYa8WO=SFch0hZd3v>B<+{yXC&Bh0IBcDkJwyOpHADCOhri`@b8UC7&X zaU*t9N>TELC7`far#GZA4FTA(_m(a6X;$6LOZ5bWR5WgirB40qYBqr{5^joLjhf!0 zo?aPDy7ZLfXi~@~x!$DisPzN47_$pkxr!MGN7Z~pkE1PTeVP8C23U(-$Ic%Z<>paj zD87KkV$mWE zN)-U!?`qcUUw&JR4{liIx5~8$8zwMxtr(|A2)@xG?+88nV!B#|m zO==>A0NGeMZSQlZ3d3dua6@ZENl)1rOQsAEdai^6iC3Fg$9`6bvKM!nBD8dIM4TGU z;}nr+t}GboBo^K#iRR^Sx>UH*=HzJZZU<+#Eei-#ilWK1UK>kSyrk#LJG4wF60dh+ z8vZJsCY-QeJ*SI4LVIVzi2al3gFWBrW4%y=Lx2BcevEb&Ke@w+q`(LB0M25U0c}s=OW3 z0hTY0_xk8lui^J5@jF_nWv3h`e=RcCXPBbe!Q5|4t*2Ul1$tFVspZTp_LyI?H=Lds+V`Aly?cH#m7`5ZUxZJ-A2b{3&aWEjLuKN<>r#^b zIBd>GS1iQm_Zz9UvWEKup6Ya?j%5N{CoeAT-0nt|?IP&`PmZlKU+0(ALie|p;L=j$sl;fsy6y2$9|RK@tW|l%IXR` zCnYl(`L)W*04R5D5x`RX*NIhMH@qNeM0umUD@wHS$410;ZCa}dqk`gQf=unwIujc7 zfg$5F&rWK%?ny{tYATi~IvOI#ThDb;luKjj!j8X zcAR8o6;`n_;l5|l8|GO>A7#R}-D;>V`Ru-K?_PggYcZi@S0#h_!~ZM8^n7Ugd9)_& zsG_>Ox;3C7hxdJV$o%Ut&z@oq&yQbsxVFw<1i`vDn|k`yW8F$si^!*Zben)IOSA80 zwkJ!RUHh0Uae5~`n=&OE!LTyPUAHo z=`>}6<1s`EdhnJTyDKi{ev*_YCHQstbp8;Aoe;Kxny0}+`%$Yw_YK&KK;wp3`Y)4k zXjf+RgCr@;g)yJjwT*p)V(j~P2}8DRZ3HrkRn$uMz1guO_E$&5^ox;cQp8xeh@@FA{cgl*s~9OiI}}Fv)E~t%e$g;4ChRdh}_q+Rdy@x6A<7A>_ZY*n4{5 z-=~b=8Ac%Udr_pT3yOzDno6tNT^}Fqqctr^Rt39TxIfxn8381< zf(g&S*`!4S%}Q!AgDjcUf&?aBOa5?^pvC4Tgnq7tE#52`4%18{ei;qq#84Za&8siY zGVsrcaFTcyErhI=jkc08DMTYNM3I}`T1NxPd!;4S%eJDJr;fpD>9*C4w3=+in=Yg^ zBf#e$SHy?;&oSIIjV$%sQ8{X}Jo%gL8nyyfa#|JHsWue3{hqrKsroe zm~bJRd@-*@LK)C0-+&_7hD9{5K~z$k@mgz7l%5Zv)ASQeN$CXuusXH_ooDKQR#LO9 z&W))rA>{=gNzKAVk_cGI;k^D8l%p0YJW0$X5nC}+!$@jGa7jlnK{=fGYKst9Xcs22 z6ar<^0l&eiJ7R*4AQg29d)tKAkO?tO@X=lyfr}V!QaDr&j%hKoUv)KeY^uu2Un-_rlUruur5_8IaT@Of*NJGZk`iuS*Nu5g{ap1ySFF z@L;G&9vBi*?H?|c29%q-SlMSM1&BbrhiH<}%bJ0j7YHAOM&L&f(J+;{JdE&6$zpL$ zV({fq>_sgEQI)wqGDw?wSigM}PS}wLP;S1=3OGwkHCftY2a=rBqh5SLvL+Qgm{733 zTIAX}(tCl+4;g#Bg^|HjZL|jybn)-a|6M#I@nB%5Y9AagP_a3mghP(i51&A;po>VS z)UYn`f~N?1`;InZIU%3K)ZmSad@KJ!4}v@-U*%ot52@tQ@0?wNK14)m{~I~{^L!Ek z<@})k_~f5!pCldi%do7d7!t1YtW6=!;hkU%$SE;SIisB4wCHKv5F> z2q0~G{!xKw{fe=OMDMW`&+_UwHZ!OzIelvCQbQQh9>NZW%L+veEr=AW@JYS_AVQnh z5*rU(f^vO`s^G44G6$yqrW}hR+t_ql-&{N*zLYTKIJ^Vc^b{Z^6p%yl z1Kj;II6HL2N$&aVM8RhTB0F{NXHu+#)%{jm6o8m{=-+^cy<&{cAd!_}rQ&#VL}^C# zPQC-eF{8TZr88_7jMkMUK9ldp*Kpe^3=x8i^zjt02}5X|ZywHvo4Q(TUk6141dt3` zgfekbNaQx`qh-BG2=AT4OGkD(rQ`7$G$^Zw0?WYQafdAoF!;=2i=>$7;bXEP&-d=( zRDW6)M7Q-#$BCQsI?WJ0W_5heW9&}#(5!ymClco{pl^5vL97{9wQAIdTn-sA3-0@F4b_<9G@NX~L05TBtb zFv6P?mN~6gsf&F+O{i1~#@|DC7G zIorXC*Q@VCwuY4l;CLa*@`Mb}dD2hiO?XHyw&2AUPLoj4Oc(-8pAolWyoe$W^Z{>Ntq8_J#u z?vpo-IFldir|S&BP;TZd z=nRNVh|U?H@py_Kn{FazB;{Qt&5Rsd4@K(oG?BIyaH!zNZcuKKIHFvnrF-5+mGBs| z-~py?aK=km=+aC`mEznpYyWz;Fu#z-aKm*+mM=tcjb<{fC>q;msvpr1df9 zki;9k-y>!9whAB&Mg)N@vqDc8A%1LjWuXHVNhp03_ZFTPVe@Pb?+(Dg&2npNj$X(u zCr+T%FKRX~l1~(%^C(wM_0(P}?teJ#TZv2(T!43aMA8qtE&>XqOLrr`DjJIyNXX@d&H>`o>JXe`FkZfd1n#4) zU$crNbVlK*;P5t5t_>1Ljo+VnzG@7I2&4RcGYg1zw#hI~3Z2u7fbcrBhtT|la*NN3u1puSMW;?69ute384`aXwcVK^gI7t7 z4`pqcsfJUa@X`n6@gW*ze?^AR4`-K2wCd_MAxUGrZfOE)Omr+p;37Nyi%~%t8r_g$ zJlN<79wB&u3S`32S3r~$W$m)y<^Jwlc37#17UOkt&O>^HB5u z0YBe!evB#AV$)W7xZDon&ahP}bVg(KD?QtvK?>n*gc2o(c>H-2={ddt$xc{^m9`lr zq+kWyluowtqurP^qI|M*hZ6f#XbJr-ygowPmt_4J7tFJoAp;b9u61#^Cn+e*bDm(g z6cjSo5+O~h0wVHhVsP3^^_@fmcScwS8TQ{o#($`pPOKIze~gG6Mm*&r%4veZ4%uAL z0Xqur^olnZ7mj6a`Pd8pYA&zp61chMptfjIM{1yevrbKsO%;5i+-eFnSP z(?DQ?x6+*zkx3at1EW#MzX6Cb0YZofA?{>lf+6Cu81uM3dE$S*JT+jvP);`FEiWOq44qKYd(gF#UkcPJy zxnUpv7p?D-%h2eczy{EBv6ajYT{yhdtZe>C*Zjkorrk#~Zh0bNUqnRp) z)`OP8(3Dl3y0`1niajTtvE4W-Bj}-F!$D|PH4M_3JM-Oi29>L;_#uQB)j?`H<)UXm zcivGmx5zz4Ah^dbS#B7YDYlZ$KZEL8CFqAwWt1KG=M$ujflZc$WG*m>DqelRDr9e< z?RO{!&NOE`akRsmz1}?M#kw};RX7150c1P6^Q7I5esS)L9EyT+Nbu(`(Un35($L!Y zNpv~I90a9dsq**yTd^Rw_HBS;NHN;G3;pc_b$hN2OD`q3kIr+8E+jUX=#Z!Ber20l z(%n>_kJ48>!+VgB)QR>=GsB6M*AURslW+jaKLDv8zE5UN#_vCYg0$Hw5Z|KYxHCHk zV$%f%ip}>olyC>g@Dc1imsvAIkz*H`oX)JUWL?Neg)Js+4_~N!xI?V;Bo2auC5-_~ z4v>O1<#4wQyno2?wNdQA*}T5tY)hE?5*TPBjv^Gjuho@CS?>oz>|sRl{VPRzwRF-m zb)`x~W9&h+(>^bJ&xue8d!{(zg!_HFmRPYI4zawCx>2+!0(IDj8BH|00CO(-ik4e! zHSoz3hjqPfm1vi2WVp;gQTUztH9?^M;@AoA8#??ihBK_^W0aL;`y#QDEWC1F5e=OM zV5i#w_umz@&x3&#pS#2A@L&V|uHZU6epP9|o+#D@qg@&Exg9hpYS}FgV$!tGOS=*>_h&Tw?E-yNg=1N)`FPvvX znIXgz1tJGTn-s>j5FNs)+EQ^AK9Hhy`9Z)#;1n^r$`5! z_z!oy_ph|!#=rc>W%!s>EtdgDkN_7fP7o?W{EB18+()=5wZXVzyyb%v^x1T}m&;iv z2>M{3j=g;sBsTV5Ws7m(63*IE8dV4#-l0TT!yym?dWUp?!f#KYx2JLsYxa1)O%a7U8$KcVw9m^hm?NMM0L`sbGe7@Y~f zU@Tmuw__6q8Y$K#tX@R6@ItY(#D7mkMWp4x7Qg}Je}Pl>o$ooF5`yjP&4o=9{q0K@ zIGYp7cAuh_Z2c1c$IbU#GYkdxjf^5?ak=(|=7QOYzw8SZ9yQ0+eBzURj`{zN|eviH37iCgwf=DgAgLiSa!5>g~{ zyLa!G^aY8jkKRZAw6C|Wra$06zmp?7wg7AY8noFZ&$q1m58 zuLtHQ9qbK#xPh#5+^1+zgaq^YjoHU^2dqcj@RevtCPE0UFbT}BEz1Rz(zAIiKZw2n z-c$uXmybCDmTNJCP_<}_c9xT-c^lHcn&%*R?uTIWF?{&}{=IO=TI}A%1{NHT?O?Qy zML`e3GleG}Zw(T#`$uKL3#31H_%YexR*7se{)B7_S zp}O^QKyY;H!2sWeeg+tis;%hUnRJ}@0zq(Lgo57^U|8+R&)3A+0qXm8XH1Y57P5}N z9k{_9z_ny9;^+wz^Z;Ke%&NIZRA91|mf3ihG{2RCOl9eI!}!0}q}U>MgtQ4YqiM=8 zNpN{QozeyxG>s)sWSc67p>|A}pk`*1s6w4Mbg~d^YfBznKXLw69Q>QOe$E`;Pn~w6 z>JSE74=0A#O^|vsAB_j35G&R}XwXJr7w>(J4bKd^U(ewWpl;pG7##@w&5uukxB?}z zI}kw2zrKM%eX?sO)Yk)n-~t@cc4HuDKqToTrDzkmWIUKgl(dmK4cu<;7eVt~P=)?% z_FX7ae%ZAl^Qad<*dbms(7ty4_J9A8lmDku+^g|svqxxd{>pz|hGhTI|M%a2?k@Je zfqp_>E)qh{er`gNQsOegzMf&>w-ho#r$mXDqTH0Xgv6ubKHqR*{iODchlj4RI?Gg> z|D{0MH}ym{m*N}Gw9-uPf6{enX%w++LPOU=x6udV@HV0n9Ni@Wll3jcu57LNPAE>0 zx8zF}j;xEOL&QfJ%a8>-9=XJ|#5bOS?}z{<8i@~Sszi9-DNf%syO?O@{zyOb4i^qi zEP!RGn%~5U_Y&0hsv=8mlbqw=-s_?9$6adgax$+9t7e9sOH;oiZ{Y{+i#ZHMPPe`O z)kEx-F{K*O&T5Wi`ji>w$$wF}LUf_LfIiY8-y>S4#=$@1`ToYe zspSgZA+4RpdTh7a{caau2xxb|8c_SxzkZzj?Qs&ntxzJ(KKTdE+X{#2j*f-LAGqFw zpSBr1a&3;^7NZI)eDx)6k5_s}ETPa^>hrS?A@LJ&vK48V{HPDN-6qC0HY@$l&J+DK zCV%Br{wDv{%Bc}Q;P{hnm)0$%dP&Xc)3oqIA+gvIhUNV#uMan9F=0ycYJ=pAIV!SD zWK~K@-_0~*TCoAUKVqXIkTvK#lLj(b(GT=6;MaNxW{YuP2zgli1G%GkEk{jE-hS zt%;3AsW-RBYl<-eU#I=p-uvo|KKrcl;ZHlY2({UfFe$2r+|(mwN#W+g`UX>@52k5! zieLa2_!HxzvEYEjk-J~_)4r6tXjit8jG6UEV&Wf=PD#SOCZe3RA~4IJ;1 zx9O71N6kkDc@lzDto&&D--+Ev=3*F_ihWU~_8_OXxm}-xFH`_&f-=vpNWE`$ODtzZ zeTw5UzgQ<&VvI%6Sc{xFj7NfT>w&dpQYxZGb<6?YxKVE^z z>>1MOZC$@8NHdW|gABEl5Vn>zqsfT!n6`P5A>a`6b7@eVg-J3mYV^_R4W(XM1g81V zI6oqKCE;mkzs)H5Ig9RRJ^u!#685|C>CqZt1%uC`J2`}7lSU`cw=2GQ=L*(p+|&PQ zDEWZrb|wvQafCKhS2sy?*L7F$cZK)kv*xRk1{b$l#lgxa z=^#et`loiT*_2#P4}A*MRh+zbzBX@3$gvcMS0{W8RiqtTN8J9YP!<@S_vEL<{adH* zk9Becm11d3#ICYNKU1*ix6yre_eYB|ovLcfS`v*KzvB=&lUTdz+rsx;U+vn-rzYX&DdB4G)+5my0%`>}wMUQXWbeeH zBRqSatHBiuqHljvS~is7iX5!Bqkou^rhd)nyZid`K*G;FJ{IRwDY2rF=Z;C&FBF7r zjqm<2l%NsapSwwulpHsal|}2@^sbh14Kb$D_teR{F)mF#J3ehIvF&V1gGE*1*WKAs z(}=^X8sm2;{pt(jW@0_>0xTwIIFYl9XA#60URQrQ$?KD$l5>3(c`EuEe5t(l35qiB z5z@ZT`#P?>UozYis^ zGp0Nlq)TY!EM(gcWp!Q`cW7Zsdq2e_?2s*P&z$)4B}01VIUNhLhhT#{qxe(WslU!Y zT}N+l)tj&MP3HCvK14h&7`gBIjCGchlYteK*6^mN&ye1$hN&!J-TURCp@18w)r(Y1 z=db4RRo{2`kd%~ZHA}r4>daIc4=E|lg~{;x@A zM5j0;WKR42V8L*zlSyz`|Dfr!Qsf`4p4VdW*2Qmnn>IpBgMPXBME6h>ge#~CN6hpZ z+R(RM9iJlBz(2U1gL<^xz;&H)=yr~&dNL})F$XpK z!P#FDKKcrahP+B5I2-*GpJI{KXVP;9IsBYq{Na>8rr^zB@z(ss*un{yL)N=dPO+D6 zk=?HfwtL76M(;arGFs8Z);rznxJ_^qcIf-bsjiVCBHnP+s99KHk?Z|X+E6t*DOsv0 zjiypoJ~22x>3e_M48!}kq7-)05%N9OQNatzFBJ@B0d^b_G7*5|D+a>RKC+nthHVdQ z$cn`oZDK!)-?wA!3CSIocYIGC^(rcvyq?^f{1*8$a+l*SKa-;;|FxtqCJwViMUuCX z2Y&~|AjtVsIayR6Q(24g>-QYoGbxOicQy#7FTT2d)yvGlBHrKz+|3}EwU}-FrdNwW zFxk4W3T;pLZ+W7kS{;BxK{+U4GUAwJ{YH;=lo>nrK^FWsip&J8c77WUEkQ8!0IoOs zh_C3CM6pxoePW@;dWS^`lY?k7*Bd-4Oq31Ocd4wYO}=owVn0>ejrH$4}vRB)y&^a?iqw~o%c9{iL*ZdT~umMVqz@be!Xoc6~-1d{36OA z2ueTfUtvvM$7R@dg@c}h%1%P%Y7c!Bca%fa{ixUEd*t$W12`PMqtPG5DL9zz0A8^M z!HnzVJz~Fa8wRr!ldp5D-0E?Ok|1}c;lDf1Wv$YB6;`R@D5XNF!m6UEa;--<%8ERh zJdoyyOW{dS)G_1wEfoqC0Tt#RiKra%I+``kPSsbM=hx>kNy8$?7XtiHjxf#hx5pIg zGGYDd%rZ>ZH@QW>eU0Bxt^BWa8kpbVacsE-(4*`+diycuWBepVu}lQLitTld!Tpcl z01<4R^Rd&%0FcIR)2*H-*NuOZmH045$VTvZ^%}ZFi{4vHRWBv~@n9`gwe%1eayWT# zi~Q`n>?a=tut>gm-uP}AM=Sei)WnLxUWzD#UH9z6K#mZ2^6$vssdxV?5>a+!@9~ME zBd>Z(t>Q?o!q_8sT}VzCNd8L3 zLDC%u**d$5unJp`T@)8NhCG^FoxG2nmqzQZUa!>nr*AAIgH!NgDDF%e79jscE=BH7 zKEK`&VzQ?8DheHFi1}}wqlY%iDa!P`nlfwRH;TffTP-O0n7%dOpRq+9)2(-y3|IKUJU9xZATMS23$P zsVS<$_~u1~DEW^&4mVY0?`vs4sxgqgXLrBnRg@Tc0moIE2&taOQ77#3BwGD(&%uy^ zT=6uMjlOy*MT9_)&TprZX;zz1mA&ZU8^qnecNBuS&N&4&%oFa$ePyw`(Ze4_vb1wO zM*jI;H!*&$yPWIclKe04N^|_Y)^Z~&o}{ZvlD7WoYVf?Atz+syCkRTGwMZUH<8$}y z8su(P>~DYJ%-{-zJ9Wr^M#oruy>+HSQ2lYDL5#s$uPf`fRit{PqC(!ieuH?(4`-L; zTu}{*J0@2OO&E427rDE}8TRBn?wDy^peHb@h#V@v#gWvf%)?+aqQV!erKDnZo$mVc zC^qta@;vgcyCGbs$|Tir?tTySH>^ZMIi&ml>pHsQ&BXPd{f-(VVq!;i%FXxs2&8pt z7sVJ=PtN(f#!&VMz$AYfq7b3zMO&O1g=Qh96-3$SYj~-=zlrlR8GrIVT9mm}mKau! zgjt`0*&=v*!mf*)hjH+C(S*_9?q0o1&n2jAp78Z52RX;}O;zGl2Yr`KD!WG9b*0-` zQ^z4({4Z(#-2Fq-%d= zz3M2yzkIilt5Yo~`uM>*Wlv}nH93Gr2|uG}J9R)61t*8xB?N%`i6YrQ2H6L>9sBYya|ZXp(cOwrNro3v5X4I-(9o!qd8qi5-KFsNM@DvBH=~Kiz`Lcnx%9^$brP| zF1AQf`YW$3-k*@Ckn6Ea&}GfgII*{0l{qMacl0bxR4FG^`Z_iXL{2awAQ{rth|zj@No9qvXG@Mt-}MMNPxTp>Xr)7Jm_EBfElX zP|ES`b)g>lr~s0(ifH8Sc5~sLod3dv-;mtCPiR@QKDE60HkLh+J@(_9Njit?|IgOk||>WtSYCLWW_Mx2ey!!)E93i0e1gI-0FD|#X+vBJjbG?Z9=8V_*V3uo#gRL zLq3(S8edfzQ(vTt()_scgT_ugLSj%~vy`H<@a=Fexd-{t_c7%yg(wU;1x?u9uyqNb z$xU{_x9AarwySm|y{QZW(eoIQ`(4zk=80dal#}khy?eul*?@_xZo{zch7Ip0@ydpy zYs5#-e>Ur$xe}xor`SI)K%uSQtufUv-UhCXt-G#{YODzk!R!5^^N4Qw*On;ghwfNU zBXL9U%7M(3L|g9bQGO7*)lMzP?`yIyYkkrX(}6aO&Ibuwq|SCtzoxG=Tae=K$#%KdITA>J5GU`4fmz9ntPtM56A^|gsIue&p#@XlCF!7@~6hYokn3|Q+5n5^3w z0Rz)1f*%XOONNWgvZt*2G*zR0Q)^Dyw#q``ao``xvz=&L9mw8t3Wn9+`Xn{H?1KUL z%g}EBNGJni)uWw?dHwV{n?g}f|BBT&2AEsP8Ft*$e^OHkc<#gIu`j|@~_4z<|`;t;+_SJn+0Z3#c|SZms{ z$Pk~`W$J`_jn^8(Gdm9R{4qhr=URhHXGaZrtJjvTVerS>J%TG$z}*QXYv33Cy6VMy z9j+&48!>i)mNKEjUp9I2j#Yeon`4&iME!K;D686P_b}~tTlm66=+InE7w&to{o-IX zn@CbNBP)%=A@`=RJx9}J&+GIW}S%!f&#^G6%yOFgUf*KqRVV+*{%7CWP( z;bkodqWX@I!0?1W(+9@lKFaFw-vI&px9t$~5v&jD{Zp%}JfzlS=Vnf-ZrOR$B{`Cb(D@J_Q!g~^J1-n)+<+ShQK{v$KY{Dn4Rj}*7fzwZ4kGQ16% z=ugoFb#uI)Ls4@H6TrVRgDSRnI}cu7^L4wh%=u-Gy|cWf_(Ijde)>gkQqudz{;|ak ze$A{gM2c?}O6d6sSQf?`PUwvpc?tIf`wsnruIFf;u%6tODVNEjM7!1{PtX9~LFB+a zM-MZC!FBp9=d~O_SI36Vbhkc*CXt6-t+dI1!mEdAHK;t>B%Z$ZZk>xehz+QdBpxk< z%_(b^uAfiOCf90Gtm*8^{VrHV@OXxtKgeWD!^+~PWUQ{$y~1}0gCd(Dw&gShrU>@) z#&reOww1cFKZ7XzHpRx5>$h*-1}Xq`laz}78kIx!39K7I;J{4bjtgF^3sx4ndc@err8Vr) z9bH(1b@oM`Le26~0d}b3f&#o$^~U0qA~y+Z)KXeTP{UwkcFhi2 z(G;EXob45&!wsSVK~2XNZK}VW>!8O;wjYa%$p+*0d3qGrOgHG;qeAhRgR^7j+dwm} z#=%$3YlLIB&_uQ7zp;1{_GePMBuRg4!u3A+=e`%#Yq)4gVudF)& z&e+jRcB`zVZbc7kKk+D=cru#HDcg0tJ`6q!- z$5j*g%_75V17zhI4sjk(StY+!#80LSFzaDcchHb%%~)-Am61*RtHVZlHwjLH z*tRx8VW87y%ey9jufFjoleo{)FHn(_xdl#WL9Qb(BXWDaDBfNI8E|s7K!qVW3#PBL zd%y#bZy3aBr~fJs?+!#cE5fSIUTU_?8i`OeMNo{yKu&@^EEdg%7S9JMFnHne@yr<3 zw0JsQX=x&Y@Eu-S&b9B!cjmL^@cO-y0%@a3y-v(3Oe_T=6e{UTpt;z0H&AeL{kosD z(?o$$+}7EklY?1zKhjuyX*w&Syamf$_wAOZbEho0V1JCC<7Q4|2OlR#{CvyR5`Vy$ zb0`K7H?#GH2jiyyQez=(gu;7jB;!lp_BX>Tk)R&L$PpsD1M)z!zT(GO!$9MGp(0TfZOvuf(u; z)?SE{CEAgJ+M)kYXF}*W2xxQo?Z>m%i54HGS3!g*=eQAmOQ1|@Ff&m0@PZ|AgnC?W zbrliV930ZAi@PDDN~EAFfO8dLa*i3~F3#vR7%kQKDHQ;9=SRl_>Mg#7qx9;5%kSGZ z>Q&yU5E0L*R4**fx&1*sN$*96PSiYh`8T+z*Q9}=O+&@GISpdvKd_&BYgXyda}0f5 zg!8ezU_+C%=z%X_)CjAvz$Q;pk;R9dnFF}&+n2+|Ro9@;3p_)Mru&?>!|`-KQqe(o z9=)y^fye=E%i)PIyfcM|hed5MTbja_jzeTRXAbks@+Wm-Qr_|6Cz_5(w^0g8tkNsX zZ=X9>XVP3wL;}xFBv0UFu%mVfu|vJosI;>V8&f}_1XIX}%b}F!=h9H$a}7r*>`?z` zAH@3Q+YgZ4>8A70&DDoWLqe{%;)hIJjN*dX0Wudsb7S|=(FY-0bS8IRWhGvmgm=lP`G$`Vo7+d_u`bdTty6@26Ow6r7PO~&QiHQq-0 zEavxsUAUyIUjnP)Rf+OeaO@?SeypTG04_!G@2b+)5;Z$7R_d^0$(9cwAwUjBOx^@a9K zA(bg|ypg+Z6=jSIUU?f)IfmzK3A2&^dv;XA^9(keL3I-h8Q zw$ifGabsjm)tx++c6QOD7lX;?eSxO?2L!#LZs$W}#rMWWbLp4{u;=W@(f1|7tQ|8Z z6gIH1y0CzaLHOzy(`}t3)Y{izm+$#(Z{?{E!@rszh4r8ubB1g6WY4x;+`nUYU`&+x zt4Fnc&5j(si;|W{yFWwoAI(T(BlIU= zLw@oSYwcC#&#M-XYb5~C@Oyq$vDjndXT@Ux0bR$Ozl1W+^P{P>+_qsSs>AU$3O(yi zHl+i~RNp51$tj+J`{{FG7jz2nZMSHbr6rWFNH`Hw1^qJ+h;E7gkXgSB+HLpuJan;u zOw`TIToZtf4h1)D)EMgOx$Ncjz5S3lwq8sfc;}g-B9E&{Wm0y?Y&C-^9>7m`!+O`= zpdQ$qy72?tD1nS-zVbxkdss!=tnk%R^>lmX?2Iz#VmE@S)uD@Aa1u2BVG zuEI9sUR%NUMr`kMm;26jnR7^-`uO|tY#7PgAGvNRsHliZHTc#r>cj)8N zu@+y-0Rr197KXKVUQC?5{uJ|8UH&LA|M5QlYWP-FOyDS2gajdE$bR*C?wzyJ%wb;lCWeuI)JBZnnJ4Kro z{6@F@+FtBLthG5yFXE+tjno8nzA{S^Kd1jYMxqO^Fv2ZwV0I2gzK|66A*21~r-dHzXs(?kx8m6}7_@7d~3bMcJhhK~eNL7d41~ire}=2!=+;B#__~n8_-D+p1%Cb`$c*mcKP4E6n6slNj_xi`~oM@oV$fUc2mqmt|5Izo9^(F+aNY9OW8j zPAyS?^`t2QXJ4P&*2szwmg3lE9;g(Vtm)0Z7FXbOJ!FIctN$DsCfiZB9&V!p(6)$; zu`ruHJOWJ-EaJYe!$nMT<)%~~Tch`@4yFDI!GH@rLBX8+p zKu0KkhJ5VWtv;7v@q?NUugEe~$74NTbqkPXvuJ2(xn@(Db!N)Je`ZH!HijKdoyJ=U z{5$#A3Ot46AT7A^-l_9Z-md;Brk7^?=#2aTwf z!v}^%wwR!7%&-S-&R(@gC|45v{l`6}uBAsKN>A>eeU)oV?ev6I2F`c=i3!SL9c2l& zcb;jix4|NvKd&Gpp#=4bh2DzgOjRq~O-Ba>{UFHi!JI$bV+BlN3Bum8l^*mrM)axP z&0K`M=yy)cs{n5;UePY!%>29pxiB6I|Nh(OhG$*lk2WibzpS=Oaqj+*m6NcoJ-Url z4TYIJQ7`j}uS?9BtQBd@h=f)xt6O?O`r~y_!|v{r5*k&iFH}Kc63=l;r_sL?=S)vF zzJ1Bq6Th-kRt{NOu&)bAZW}H*;~M^!vzRN!l{BG4kirKzZV!-A&^M89i99|Ol#mU+ z$e12m2hznpk_s>S*5uGyW{*s%NCw3?X`Zo}t-QlyR<{;~jI)3uYrd_nu-4{#V~df8 z8Y3pI&+FQPmd8ZOlFgh0f#w|5o@Mo_^>2F-^V-xqk8LslK=qT;!Xz%a(yij0B>yG= z%e-qq*T*_LT!o<{vgRn)XH8w5zO=avJO5s{&EILNvbXX>7a~^E4_jRP&9@jZv&Q2K zNjBPlKe(K4Duu4}Ljm%FzHsR}2DmT82g|bB(o5wro7E-T1W)GI8G_e8+5Tt{I>7qA3Vbw|FLjf{ z6S!P7VYv!_`FgVlJrj!0E%Zg6^!t_G@VAq2Rb<#4JTI6#eco=*v^+P5-=PQgg0zw! z)-zZhTwQg~JL?Jn=lop}IQ|?VG2<<0H|o@9#0sUvP*Hg%`^|LIk1Yf)9O=)T)po3$ zm)W!rB+J_)erZ)-DIlL({wO5ifG ztLGpDA=R-jfb+bjb+k|M{1UopQTxiF2q$ho<%vLD!*TZIL05X&jo->2qpU?jEr3OwE4qSL~CWBvG ze3#SIaX~nD`y|})=WA`rQT&fe+_W+o)kq%9rG{2R_ZIW-^K9Ft`_7N2T63q)N7V2j$Y&!D$d(8xtVov;g1=+N=v7x66nei@Q|Skd z8(&ifi!^k|tnHtdc~FeS>9Lq&+a{Q4W7{w-DY5QyK(V)mruum@VKF$7owugbil>Yq!9jW?DfT@WtY!v!5$x2;IGjg&rKV!d{79R@d;AxxaLSaqQo{- zRz{a%`fd|%tV} zUut!+Xqhk)3Z#0qOO6U9y-Mzmm_J|`WuR}3sn#8_WzYK%h+5(&+LgALY}sb>9(gp> zkFWqwUQB-dURdmd+0kNK9VS=bXxuwI)zsFmJIgJeH=Okhw(#G@t}eyYd-95=tu{WE zk|^9Sv}mn34%=Vfh@eo+4zMT-3;Z)AfE>7~kODF}?Fb-nIlUe^Dshznuo#9)PP@2+ z;_!`gKhNL9GW6ny?~Gy>DxrrKO1sl7_<@@?R<_JoyZ7_4JH1D(xO!B~%cBLhB~9#H0u^IFCHyepRg?)CEI0HzAv^NrzGdICtED zQ{|$R_T!EgJR+h|pc}kqsll5H?$4mVa#jQ7L9>EWlJ2*`^?wUB@r!TS68l|YQi{11 zcuwxGrrWCH_N(8~x;)Pt9JA>q3GqdUpoR7HXl9L9)_H{D5%{gpdyU~2wfm2QJh$L( zsCw62fpCCn%35pn-swV`9>yup7;`A^qbUdkRFqf-xLnW%YR{B6FUC$2|N5A0n|yAI z7ADGuXwJMOrX77boP%4zgl(74YXbbH=~hFw3Znx;+1%fcvF4didtx#Ks`eA_bqZ=W z`sg~OKy&>jKjV7J?jMeP!J{vN>0H1|Xk_pAW{T}=cv#T~pUsU3P+$O+{1g=SyHTio zty<8!Wz?}9v&yk90+6s&?B%sV`SXEL;0o3Gk4u&HF{?lx*AIDaU5{`-+S+7N1R+!4 z(epa(#r|-L@V?N7jj|xO(Jn$y@8(9)y{XR*2cPOY1l+DWnN=xudD%LI*m@jwjbxzA zbO|3mz)o;^8F|_LD=+FmS#18vR*r%Cw$`4EXb0a{E{4xS-WOr}ChmZJQ{*=aw;nAl?`8mtgnYpB{8ygZY(bb6)wJ}!$kLm2xDkdS|9D~QKV@40pODuM z{!^|eJF#7DkO^wxCXe9|pAW{}4my9zWL3$8XDr%$L-Wc~w?-$|a;7_cUXC@Ms?IZn zj#STjcdh( z?TFbr96g)5xr6HH*|$}!!aY|yjXa^YN~D+NVkR8+b9Z?aWGy=>h>yKrsjJD9_lZcp>aBzbX^7!+2j|(cS zDLRy%)&XCF0ujCi;=n#Hjhm|r25=CtA}J ze2+Gd673<5)S-$c;=Ds0g+Pxu?>y?;7Qk7{*wDg{L}`9ldOP>mQXNhrZj29J4Y59M9zf{)a{J8J+fXL;?WH#GhS z6#tNmv{2eTJcs{U$;$2<;gMzNg3r%6OG#yU3AGoX77NnDbH4v7SS+2{)rBii-o|@6 z6M(N^J=PHp2WRJkVDZBGq82(cf;9pgDx)`8DFOp+-a~xChG9V#1-Y5!tZs2RYZ=X1 zm-VH|t@za1@8Qrl!EWVC`_(>|I!g}EjeQ-S^Hi^3SsZt!>I(6v5`^_qXGeH_isDbt z%v%FmDr(21Y&e_4W%q~j9F%F@!NWWzj=m|rvB|ZU$HSY>LPZ9xNl%d^eerm&l|!qR6#HTgGbjN?lS#N8jF7=5_kj;QKhGO1!oJ5;cA z6+x_p2T-0PTKH>3iq4cHRIngPRZEZlb?SqdyYPbED^O9&)}zHcTD=?vIjUVEhv*rk z*W|@JHl-tTx#~`BhONj$RuzGK=Mi?;jWYbKbSrV-A+0K=g4K1K&adGYqL)PXhx)Fr zmDIuL%>9#ebM5$El%kkQ+s#>mekzrYbECui_CPHYKHKD00#u9J^g%KADgP;z^9?6V z=@DLzgL9K-(I&B`Ti-b!ipr%kPoRHXe7ZeSSmQoCX8?LY+UkQLVu0b6;LC<~M#%^}!S+EWF0W-bzO{ly zWR37nf_2uwQJhUU|5EJ9&GK%(^P-{nh?b>cy1Y2QDU!v@v4`Bj=B+-H52Ger*=zj z&EEKfOyf`0tYbhs)hv1K((&tckhf7-U?bWS6t)YmP79*Lby|^rTd48W5s_sKyAV&c ze70)&WxlTr@dAY|k#XEvb#t9rQ9OpuDmDbU~4$A`rY5!r5C$9|{OLbl@PO$qH)m^g3PexJ&f% zQ-au9L}x0aVTbQ-nT$?+u3XXZ@+owX52lF(crd(V_s$Nnh*`iHfY@eHpv%F#|wf?!3p-8-+d_t?NCSD$?(m5GWgmfC*-^F7)+BG=T z7~@4HnZxX*#JjYjH&rY@_YofOwU|cP-38+w@a1e$ok@Mf8+3tmm*Bvys&3-Q)}ZrB zAFrs>1-MrF(DH*0eXRX^B3Pgkw5`k3<;2|hvuFA~4MycGB-cscW#<(q`-W)rvXSU^ zQ-`l-haYhy5p&drdF!%W5tljve);s-<9_9=JFRs10t8AhnDbaL_Yvch<=frCkH~+~ zp#oJc^0clNRwMeX#nETex=BH}>A zbWbGMj_?y~XW4vD$}T5FmLD3TL)&E)hcqcP#={TKjoakPBb{@b^cf?WQ4P9VbNe05 zig#0QwM2RkJ-8IRv{F^jG61ez`uj15GT_KzsOX5=9PEIrION zwg?6q*XY(%MpM(0g(s(ypS_qF+UYnWucSj80_vr~$fswjv^~8F5gb7-h8KOVu5xf^D&I+D9mk{g)*L^P!$ZM(LI5OR;xaBNt z;ElnHcJREcRMetfC;6}8Cptuje^L3d9UPZFgX=11Nl8^gY9}&giX1uL9L9!u%ZlO* z=c}>pPBU#pouGtgU9f*Lc8Hr&tp=$@^suj$I<$)^boQD2jam*m1betE;#=k~eNaMh zKc zA&>b8w){n8W-oI1ft(VFVo>w5>JpQ#E<(PxzhFA|8E_8hAQep6vLUYBO=j_D-12T2n5XJ%b2@5-?1` z8@U1iSf;S$eN5F@+F?6U$nm*`4S6jR<>H-o?HedOPDQ`)R}T_gDI+HmPv9(Fo1Etc+FCrOmtO2EA=ZoA*wle^fJ$ zb%`#g$+qm?#sHLIq%4<&UBhFoY3WIZ#7S@>u@Bg(RFw^ zchDhaM(#Qyl{xtBWA|y9Hw4%`bi>OzG<-G;%^LSa!!aQ-xd zXIN1>wC!P)y0dA$7$I|ncfn4T@oN{9CxbT0cQ~yT0FLcmaAc})?avu4L~ndeW{&@^ z)Pq{=Mw^$o+Ic*d(*m=Z_v5e^)+Ar)BG~G71fhOh?iudqYRNg?#hqPsTd9N)06E5q z6Ndh=`B|3^?MbT)d|F7)Q_)$PU0kCx({yOxe!36C9&6w+r@I;sIlPBZbZI+<#y25lp%=_Sk@ughrJjp4_QMg`2egC{@ z1KWmfI1I2;zq+p=%E4*oZmkY)_ffmaez=`S(0@J?cdm@YsOROdl8SPco_-oe%5+6M zPh;ZEM$oL{Dm16TYGlaQ858)l@#hNJpr}RWtrN33@&-q3Cpt*Q9){=|cp zpt*(`pa1X}Xct^;8|!}9SwMB_3i*K5p_%Z|Ca5!W=8fpem1?KK-zDfDzad?wR_C=S z2B=^M;d^BMrLX;LlY2q7$P1$u=z_x2c%41ihE^@Y&V1uUWO}%?-fS zzhVp2_?zjtRe*JF(@z0IAq$!H7N);yK)WycAdC8w79_Kzq}skIx1Ff9s~hnA7t_CK zh~3WgtS%SmPx1Tj^G2OrEq84Um$+K`8DwyGAYgj)*;8^T&UQkm$-LArz>Rcs>;3}J zAOv~)?H44CVzoqNto&@Cl8yE0F>IYj_bt?$R9x;6&M33Wn#jZ?9t3p;c{P~G&Ly9D zbQ&z!6muu{Zge0v>J{u(LycZ!K5Z6YOT6&~n6}OK_D*>wruH;(xBsL}IMMSD@t!OB z_>?i^)u!Gr_L&`%4YEtcuRz}Q${P?=x%zv%()L3eAmj3eC5Rq5I;huEf|WNdw`wOs z4;|F(G58lhH+KQ?2VN>R6I48uo0>q~6CvbHC-)>huzaF2uO)Q9AI&R$sQh{2L&QDA zI-;jU642VrHswe6&qk@T_Na8O_31Q4?5QkQ@5K4MRc#W8Oe!%Nqc^Y~da%74^?j#h z!|wC^rpU{!L!q*oxNN1ju+}$OnK7Io3lwk2y(4JD_N**6P3Wf=gUi-1BDwsyYk4uY zRJ{71xx;IT@SMF;lcIftNd25r+SdtJG990V+Ga`HmSNzA;uSYV=CleH!z)D+KCWci zH4p4$lbm6DgXubsT3;cPg@qsNJ}HUYElhuZdD0+o8=(w)o)8e|M>bc_fXFOs^7`<_ z?&X5Mf~;6cgo9c}*a@w6FnymMARu8*Yxaiq?ryo1uc*TbwcE9N&=SOQxeUWt<7S{HVmo%%e>00*J4#5 zglij!p>5(O^=@~hCjaD+a(8>;FXm_y@vBs%NP;ydFCjm3boh}`s2pLZMpB*gX$EddUd&1d+X|r(41gzgi^CYjyKyAB_zl#!h^xs0{ zKGumfYH0f8151wOeDej-`JVX4POPIP@`JxKvrsz-2G#C#=^h&ioPLww)SXI^IiN?y zBvo|b_&?>oBp7nav*(!w|NIQ!UlPG~=^;j4o*a7gCuG<9c=ErP(2%=(U=W*_dlnM~ z^EF3@8+E;0MLSkc%H1c#poQ+CvFw~lJ z!8J3_Oj1J)aP7=9Rh?@vsl`Xz*y9p{3+3WBKwIJAvsnNwA1tcyfyLR2)X;$gKlP(p zBM94ZJA}*&Um+QbcVS|6S5ynJlF`JR@Or?@ZBki(!Nbc9wQt|}em|>7#nxi_r|V?U zo`t~IHuqp5AU%USNPf%%FLF$SLDAkG+Kx0qZ>_5s9Z^b@nD4%=KQCp4w-qEdS-O&C zwMu=Dug2V}5U`%~0-i}kp4~x@%?CXAsP?j8RD-KNZ)A|N-0)dpIK|Doj ziP)pH$*zF^e&hjAm^u)0Pu1>s`-`$Ha+(HhXs=HXVAhsL^Q=Hf6+?F!v@q@K?>{`f z%5Q?y5KwII?MMrm#i|eeffn*h;y+iu7fC3s)aH5&^)8>@A{;tSr95fOZopZI-h4ur zf1~gv?2~G75ko#}NIzfg;019#{pb)5k8#BqV){b@_GI8@r$6Xzn}jH{>%mX<-A<(7*GjbZ>Yt7%AS-)cbg96j{=J)i! zmjj}=g1?@1DOk@C z;yBm9P9h6nd0>fmte9-+0T_{S70Iv0r#FElz)ttE*N=X(XbgXic`_XMUlYQasr?&2 zdY5MkSQUD0n>60Wn$s-DY#r4yxo;7mS)r~r1&^nr>%- z^XKD@>EK%hf(k}oxk$E{z3%hCw}IPC!7K0$#Ugw4_&G_bG(I<`CJPztoZ&|nXl9{M z$JmB3&!WtY)1V3XZQYQZ7jp*NAv-y=9JEmP!&`Qb%MtFy%5nxK12spK1nn1`t5eHD z?tnj$9&~=5UP_KN$f|{1E~^rXx;3}X93$^7YU_bS%5?laL~IjaL&pf?{L_*a9hD)t z@^f}63kkpCPu$B_k}DS4U)!;PCFF}y{5hJEvF0Jr|0DEIiF zrX)KyMrc*tZ&?z2G@+J}FK&|HC3L%b0XAgsxKch@-nCSw#s5&Ve4;h>`>`n`Fd(`OasHK==C=iGK_#QX@w1!*V}S&3t5`%~7ZC zh;e-tm^l3(mQ~Rxqy&HFaB$9!SMW@IDx6Ug%Z4}|Yv+yp%))Bb;2=N=!TWppYW$Sf zFw}v3toeCG2>0JPX1(_3yAzOiFzqQE`ITruYpy%)3ooBgQDRxhB|{^nTS5by zE#Jw_w%f?f?{CrIzl4uI>;bbh$Ci&K(7cOzp%>LC46|Zr_nXbV$zRErRrIiDU!#@U z@A3@e!!JcuzdYh_R;nAf9dTedt?h8nv%oQGh3YCFew?i@%_+@N-C@#uo$$RbN1vQEugm)*0liH1}h;uz$W3)H8i??&Absj}v8kk4z(Lk>BfHG&E&a zBj**Y1{#dG^WhaX&?@Pqf-GUB`Px~_M{=}XZKsj^Z4{6+oqE^_9Ia6*9c3#Lxb;Lv zq;bAA?_g!K&kzrUH;-)8>o#_99yKP+C8lwTZ1vHE5Nu z5iq-F)+Ttu9e9MKa4kv9{g9P80`AI=ru=Z}3e5Q-Phm8VcoR+HD&QR`D(HsE;sSaf&~W4)F6s0p_{lB8mll=k?rtH@0j zMil;(-LRcturIojpK9KjAq)nz!F%O{1(!&z@OP(1ECKhtN?*V=e00o)|VZtrThAov6-mGv<5!hmXoy&pa&q3Xu8^)>(%k z?w?O5&u-e&iFWey26|fUypH$#P|FS&tl0(gA|JUr>}a|S;6wFfO&RA zamc}U&2aIhE~%au!n?-74WUgD)m6sxTAVs=aL>}nU4#T3RoE>0+c}h87 znDBDrO8iZ@HD4{G7N0m5KV9FbdIl@@Jb^f?EEof1;kF?<%zD6CZ`fBih1+s$(6+pi z3|^La2NbBXVZBc-_I3EX7VvHQcA6TfBP!?b?jBsSn{W4M1*X%9zqYWCJ8zqdv-t3u zR}G*F%iQDG?mb&fK-7>G1G81JfnoO%{|;9oAO8#p*FWBl)L7>OR;|yzFb@4Sv}_vJ zfcw#$6_ydFsoZ2ruaKdJTUH!qDTiF;4c_hHvX1Zs`#i9xoIAs($}hb`?Mz)>m7Y0I zITG+dR^VXLPV9d<2Rp64{UhXxE!+M;OQ&JM47KtjlCK-A_V;y&vKpv(Q?7{#(oYs{ zRm4ZO^G?}E18NSj@(Ro(r|P0^v#dc zW_PWsRMnY7}m}L;Qe;9lyCe`GY$fO6NY&Du#%gX}DTVtx|Z#KxJI;wfSEbIkVvSSGo9SZ7} zttAKk!yWGcyGp{2V~$X^^XITm{JEVs$oGp)1|s&iv`?l9XQVl-0<`d2=S!idqAA8E z;YIj<>Pb6+c=^A5sKqVLpFr${-B!qFJ`=qpTIecu8#fETAHL7KbASqae_*2oUi@Rc zS^KnXiLKCGgYuE1vH-gD(&Dd#7@lwm)KcPGsO-^vU3SeD7Hm%bA+c6ap=CD7^=T{hasVwQO$?{Hs+ zmP>cy-fpgsTq&!n)zIhKZ>mZSwm&WDh#GM&Zzg=pZw!uRg!Ul-LQH90HA zAD^beRq^{+GOtd96x!;JsLnHoLl6;W)5m{Cm+!kS!Lz#HX0$!i`R)3Fxu_?=TTPa5!vKd5XYt2jkmnyuaExA;B z)2sT>`nbAH+uZr)@L{AZOY&1LO9NG6l2wW_YDsHc_l|`wC1{Fjy?j#X5L|Z19llx z)v|G4RI<|)Cv#-d_1K*D>JfD#?w)z9HBiU&uH`@ABb(0{?G2Dk1Y;+khc2o{*K}%SA;BoxEG84|dxSTiIwT@b|yOxsrQP=v66~ zd!kz4ycVBgR1W!v#8hiTnncDu8tt~<)aD-$QhAX%{ykC$Q!g_L$u<+m({0p#Y+tgiZI76l9_K|U^<-cD z=KOOXV;~^iEHmcE^?(1J_1csltO?oaExbN0;J!d3F)6V;yc7RrqE%1HPuR-SsZ8F? z(n|KF$)S+)NH>QphBV8*aof_`RV}h^{}FW^!n^-xY?@&2-A)k#bpCa|F}as;GpGFM zff<*Qj!o}h5o_+K^>)EsV%`oKI~C3zTJ->b>)%WtQGtX_B6>mNJ9(!Tk|{IW8#wCk z#IIAKv#P`7)(ls#R8;luwrR#z_`a{Hj>V#C_QAY zcHK(5M+>vC&ef}u5*TgX{Ca0TCS}#bE3Q8TmUfr2fBNcoq#E4-t)42XQ_m?P8*49* z!R1Pk!Jty&j&RKmvUfh_RUh!>T&P09%=@Zx#P$7k1cR6Czkf2(f#$E~Se6fX|4kX* z6?z_LkAIeO=iIICD zsl?o5Xy2}97Fy)?Tlo(V(0!;c2nWg`o4oZWx{ zmJ)sQ2O$uspIaXVOkH!Tw*Eok1CVkp+R|ao@y_Uu#~`PAJ9Ka`z!65d6_`1 zFO`B_!Zm@DTTC);ZFxUX0o(KQW*U72ub!?68!oJ%VbHIEF_;K zteI{Y-Yd|Fg)Hx)DVh~^hQx&>XNo?a%m|c39H-}zueM@O#k{nDTVb%Hzj#9!thc~_ zdnr-fX^A1xfrV(*B+2!^2`@Cu5Mybj)8n5&Il!x1S!!C$iM*EnuynsBaoiE)U9`C} z;Lu*4O5fv-P`GRl#YTfFJP!HA%$7?-5E$2c7zUbr9|@{lzk$|}L12gb$!OXS)(gh9 zSss{(jtYquW??U8>(x++)2tD02LgjZ0gx9mtgsV{xiJS9Y9{^X0m7jU{#92G9`km3K? z#%ve>FeN70N0+S+Y#gX9V9KjKnI`yO6%YCx$zVL)^6l%4gR2VkuV)~8nQxxS@*VvN z$6g?TH@k?p-U!YeC>ESkdN+26c$9m`;LS6KqAP<$Wm4-7RtL<}KY?xH*|v$lD3p1A zn)q=aD>eLOH}u_u9VwiVpo6Oxr;+j-OT}4auTITTh$Km&&!h)fVW_i}8Q|Kr|&;9|+rwsV)Eh{SPud9<1 z1awwfIt6v}r;M!{I;1vOt2rAt)btEGeaoU7q-}J?AxAdEv~*j(DPRVqGGPlM2AR$C z?dH0bt$`l=*s*D8DsX2LYv=62)YR$OBr_BYl(J)Bi_TfIr8x# zBW}9uaYK+SZkX|R~+-r*PkfP1x5M-EvT01^wD#LVeu_BUp zB9@Q%VclJc-tfcLs`HgM;m3yX9ozV(<;TmKh;!?s8&>XhRMjV6U*S7V z2_nk-d7(;KC~LJ&Ua*?ri(v$n=XS_k3?xb{-PyJF`ds)*ctU^usv1IS!9XCJ>g`2s z<~{ixREebgGMiHek0$D$cBqa?!RsIDEMiSL#`8_r{D(=zqJ_PQZfV@N{?u{Vtd+7fk8<`xJ)i>;th#qCobE) z4qQRTnt5gZYhe9!z8Q7MVH5t(!XJ;PX+}dfGB$w+zj#szZZ(nP)-(m8IC%>B;+b!0 zlf~~XX-Weg`>o8g+X6)+jQ&1;T+0k5#TXx0rTmH7wGRgErjaJALb4q7VJKd#okKM z-7Fvv?T+yje3MrO}Nc63v* zv*Tr(7U~xxfIsrvY?nT>h&0k|Vpym7PeZ}p(D4!WqiFH$&|+9(k+9|G=nv?A9+H53 zV}|sy;xAZlEeh0+;<;uYkX^7Tel>C}t`I~{g|x@Z#R<6b1YO?1_$5(Aa4xfnbX1%x z+0S_TIjX*-OkpFs-|Nbsy3uTs3sMP7|7;NOX%Kuew|i_o&>NRbyYC0jX~=9U0Q4)I zOY#VQJu0U9iGw&X{$jWZFOA}UV`(`VwG+X1_ea(k{*DhQfTdKst6OoZ&n?JZ2Yy#7d{tghkFb$a1 zQ5*}vN8Z?}#rmoHhlc_N+iDEbJm<`Rmobljlm1?pO*icge7#xmxCTM5Jr<_&8#Uu; zMkM0Tt9#n;_3SyfmmQIlsLg2nKvZJ-xfS!_(m`Lj8hof59{5Udl(6Z1AW#Fyf;RVy zc!&=%xQQ#v;QtKd&4In5cjH9S*>8A2FUeRKKksSa&nnyM{EtOMn~BZ06ze0C6n-QC z{v}gJZ9h;(?^T0Dnop5%s@hB6!iPovTEwI=+1oJ7!Vs=IC~x#PX-oHfr$I4GfdALv zY6~=g{rIS$`5+F$o@Jhdc&zfUgnSXVncl6fMHw}tALF4Rpb7o zx0BkdzPjSV=juH3-v;{jG#sSfON}A2hV~nGCe;I`mHN!ZzM~C(fxR^DIUzA&KDxUa zu~Bjsp|ot%Et*zkhUI?ZrH$Lt<}K}6GR0WsMc>hC#6u}&k?`7OH43gkm;@j>ae*97 z;1#``E6EdrdxngM9xO0bB_W&5hjtt%$&kt1km(a+GmA zoS7K@e*Us?Z^KoRWXuggGUM@*Fy=pL(J4#C@GFbUSEGIX#FshRN=Wdk$H&&+k*Kyc zLqH+a&K#w;6-iB$a#%`#1spKZiTx>t6hp{sHH4!Fr8R3rThh@iqSca-a=WI@Y{#AF z`p50VrM^Kv1pAL?#VnZ5M-CM;Ok)H`MI#Mh>#&7(;W`p0Fi zFm@*L2{=NZU-9W&nT#-{39OBo?Vv1xP=7bBh8wod9*#)(8i@1lu)M&(-%FW2&YX?9 z$}+|$aHP5)_j3r^xk%VeR&CB@1kl&KghQ_FYgiu{Su}qp!6QapWFh%4=0mYtr9rW- zH+NEG>`&xMbT(oIZ}N#Fu^GBEv3t_Vg#?l@b=kPSrkZ(0>lxDUa z`R&dG*EE~%R_l-O$W9g0d;&|@AqvY?&mV6AUl%i6GkePdmQMODOxr#?acQIPIfnb! zc>p#$`mq4Ge^ zX^D63Fy!GQ(J>|5=QY2&DTr-&`uzK`zc7Ub7sEGg@kO1MdSeSQ=K!L`?KPHidEso` zQ(i*%>LgK@%RuP5*mOD-7AxeE5Y`=CUw8_i3~)JF>IPi0`sJ z6A@v`?3=vK>YL0N{A)umT^U0p5yJFZ9W&QT17Imu(okDsqEthHuL+}2*kP8)<(9R)yncsG6Wb;b-2y;mtB@7 z^2_Uy6)#Cu4OeJ)sDds~HvIrqyl*qo<67{7Ic{7oB9n7HsnI4E3)TSj)?x(C}D{52cP5t?~U{|= zGHY}Pf#SHGf?e(I^jg2XY(eWY`&eyjiz2Y^W zpJ_GL*^+Wq*r0G7`OA<1{Z&F(KPT91WEup&GC+GwDiXmnK8W4wDbpsKQ3Z`YyLmEY zh2h^TvC7wJBQchrnK@RRW@H0y)HuLHWA*pSt)X{><+Nr$LsGx|WldU!#ujskK;VHt!TOa2!&VJ#meey>h24$>wA+GBRVc_U~)5 zrZ~;HnsZAl>R+RqCxd=r&D`E_K(vfHw;~prJjH;Cvygx4G7C2K6vht10C|AdQ%tp4 zGrErZepC$A#*y*}7e&Ei{vw(jsVK<2sI0F<(d9%{+9=WD;?m%p&6lO0k$$Rye;21` zjz{yoK`Go0czPSxXLevMNR3zbRG7Sp9|ze_`N}$ScfL&}3M)nl)7VajAC$YH#0T4_ z`?$A>OYH6iVW%uyb{cZjLb=LA4KkU>h)5#Dk3aoD(u`z>k{{19u{$SuFGlqVb;;{h zE}p(mt!YO@p{Ztpun#20cmXfQ!wfwbwe@~cq=8dI%Sz>DBl&MV6Yl0>hy1SHIcap& z-G*|N-z_%3K0uCM2a3WQ+{E}k6O%U_p^tbq%B{#?a3icRTK^ zb)%We&3bfespvgPTb=M`vk{d7`BPog%5SjSV1jyu*YAWja`uk^2s|k>--{Gr&dg4W z)w2tR(^6@W6@>Mzs2{HvX(4XHYa_M|w2P8Ah4g~0F89bpGdc525;qr@%NR={y+XdB z_XOt}B@NoRG=Akgz$S}_$81VwU8HzpjPo9?NU)IV4svN1Hg*6W#=lu?n4@_I5-(NN z0<4ywr$Px#=!ZZ#gw@j_u7jevWOcZ+1+}QQ28wH81NW%AjDaR-hf~DJ>X2u7^&Ls; z`DUc=H*!9|s^NF27x0FIpIV+@dmatI{f;bh1WqK(m!#63eGr!l)7|8-MIwLgWv|pm zUD>vnQP|k0>Hy)(?*T$-o^x&<&)M_pnk$ovI@XR4`8Uo@sfOsyEts=+2vVqZ-}RYFYJ;VNwA+euEYGKu z#5cI<%|h(ZaWOW8?8J|$>U2QVJ=8|V&A(?%9%Xh5YaHT~*7nyZ!Mr}A2KfVFZ^j|= zgg8=N&bJ!w10C6>)EfUNjNO{|2jnScclcvKPa)!NP6twD+Cow3{_5Yw@O6tg#v10f z9QO|_Ch8orT&@$0h&yP^&pby;@+|n?u3m4Gm%Z{g8SzQ10gACU0>zl~7!xJAI9SHa zwD?whpn3L+u-Gr?JXAy6({5yeT`%t-u0scsEOKWaGHf}w7QjqtGnd|iRzqTpKnvP0 z(DEt>|LOuNOZZ@mK+w+~A%YNVyVZfUoZt!QGkNQ4F@t?^l#9z0=MTCMx zXKo5$G^%8PGkHZw{>e=to|{H~4qX8TKITj3(;#_2oKk7QmA;c(DJ0-qM0;k3`Y_)m zx|>Ind@gp#o^P+*!mVGpZ%Gq)fW)9&S&H`rJ1_5s5Ux@IpIhBk9B5a>d9AF5g!!0T zX4s=c(zrA%RU&MUN1JnfA}4-g=VEx&?pF6n-@>mkVJljzw?-qNdLt4Tago0^=*1u^ z6Z?&nhO}Qw;fFc#3*!H&r&08r*0OcWo1HwFRG19@&9Q)9%2=ysk8kjgR->3)?ehvur*bv=%&0pMH zu~9@WG&%PRX8(zjo>s%yw-u%M5SJ46_awnH#6_CIoC*H(wWT%XuY{<#f@|u%ZFrQ9 z!;saYvA0Lmch$S$w@YZ*y0V>NIx7|Hfeok`zo^m))6?Hj%ii-O$KjE)VwK&{bX{Avgk-$lKe^r zRhO_DxeM^0W6uc}Mn^cu`D4?gh_0aiK>BB%3-f<-9QlUQ@FPFi%-u4qa

EkZ{q1z2)1NR z@Ia!K!gFa-SWCh(d}gH>e0A9 zAZB&>3q;s3NH}{AH@0BED4EZs3@Y$O#S!Apyl09JtA}3@@4~W@&~H?qRt2({WKu= z!Kxu}cJbF-*zV;pxC5RsA9xNY()?XAXBGj8GZ0GtCa%ej5`Q?UFanDBU?QSC`6NS% zr_!3@TH4BH7kiQiM?!O?=;yYl=p%=KpyQTfqgssT0!ZzF&gu}<7AbOj=`vkC(8~q3Fkz2fAh5nY)nDoIJ><$R(;pt8p)3*cSC=Hq)L%J7x2g zQKiD$zg)trHP8grSI^rRCl3;R{b)~XwIk;{8Wt(8`K*4N-l-b?wH zM9VloL@f7)M@?0&BLykv7KmAY*JRVlK0NXGJ6oPDbm`;$W7R~eCVdQ%;e%axF$M}e zZv7qMq=l}&2RPddDcZ@K2H_G>I}e9J84Bji@jvY?RjoUIYLdU1TO%&Awn7J!X@!MI zM#MEwh!`!%F&LU5TG;X5f<8uB*FhRyliYtVIwEnvM*!Y?rGX$CtlqcE1_(Aw5GW)q z?PF-pHs#pRgxEn6A7sZ97xE0@*@(=~P%t~jv^Uwq ze&ZYEz{bKTp}A8Lvce7g!m3dChbS21$n)mvFgyFfUT#io>fuV8<>HaNhz`henO2bP zev!#HsldL$S2lg3)^>xh2-)^DP&1g~EWFvC+PE3@4a?ISi785QT$9rw_}q?1j$P(@ z==HU|!KMnQ57~U9{iGTgN4Z3cQ&vO#kkqHCC;6Seon-%>GJkwz(#!~g!T9PKbT)CQ zqg59-Z$#5K3H3TjH~9qqRE=JLz$Ec;?U3iE(9KMIjrz$QsW z+NC5L08wML$d3qYkjg=DrH$|{$EhGe`%*u2b62a)=FV1|R925HQ*PWVn=H1 zJcY?K)`Z%+QFS#l$TL}<%2gs>f*Nz$1xOUTRA$R$7bg)lzlb08EwaL@8K)iGmFU+H zVeRI5D=L%VrI#V+h$&S%qG7p5LcYm>Z_4Jf;g`!D&@=A@ogqZ|;5qYUrG7cVg5-cf zBZ}myevzi{BWB5uXc+vNw;$`>sJx2D2`rf<#6`_eHM7jkXlE~Z^UD$9-QMjh|D2XW z6gDSn>%h5zMM?Y3wPkNDvD6L^dG)o`SU;lVyDu5be&j8BxGAX~ncd|WIewA#An1|M zX#H*t*8KfufkZ@(LZz{O?}}qRYUZrxC9Yq`+U65x3{RWa-0@?NRElu1()cE@&FCB$ zk;4h23YD7mi$X6s3L9lVb2b!yi>V5gU{djy2)=BtJK)X@F3u(;Y618mYC+SCyB>C` zY$0zw^1AZZq$=Y-mJ1{|)$W1XT^NQ_!^IK(-sd78;3%9=|5%y(E!kO-}zM99vOvujso%q5;`|w!79Ato~=+`%Gfh z5H_C^BbSDJpu#im3b}?9qNZT41+;>3)m+JlyWi`<)$ay@T0LM~UH3fRBh9?yt$8q7nlIuFIf6O)*P*JyIyT zG#d}awlWqF$uXAuHYjs^=Y~H@R|5+mTwQN5AoSHJt}G8BS^`L8DDsFyBfjT5iWTD4T(Pjnotnx22rXOP6=yu$Q0G@CCqH>5=qvI93*3p+Tj(@DRrX6+G3+sD+46&A zhrwewzTV*u`|OBHjfvRiuZU?eYVm7mEA?Yg4eLOhBR5>m_QhNhKK<#!-JgWHHk&e^ zSt&Ilq2FLP6-?p47HN^yQLh1i8mob-pGTV`7r35RXkp%B(-=gibj^l}a|gox6AK9X zl!Q3+4NQiVaRU2naUuc1Yz2FX|I5IiCh{r_nIl?2o)$$qd6W)hzoYyQlR#|0UFubi z#_L79sroaL?6)1-e&P}2hd)R2+Kcw@F8N$bqf9zy!+&rB8+4g@*wZC0UaUJhCQ*B3sXnr!|MuAJe#3$(f zi#O^1i$#QANB8|5r0MxVQ|dpJ%4`|>B)g2zw}R-Lif1cqWzAy(ENWpiVN`s;oUSIP1o)}dX@Y|&woPg z|Gz_a%fD;;IOo#6QA@s}&c{xrdEGq|RQ|y<|38Z2JTD{sD4H*QbB$`}e2o0tN4n~` z-%jyAH_ebeU(VYpPRN^DoRH6xJ>hK4p70FCkr+b#vy88}O{>5U)%-X9u(QDL(C#-m zk>Vpgu}sK4LB2ov|NmrDA$Jz#Kc3>8JngCNs411o6-vdFUrBqFRH`Y}l<~B`8_oh=NFPCyMdAIC4DSu8S6l?q zE~t~{na$0q<}qzkC%3np+LX)or1}CN>-yzWv3~}|4L=LolH-_75sd~ zjFWQxRJxzy+8EH4&Nywwu2QPm4}P&yS3k>6F6HxM#~2ox7JOgJ=+Uk1spj^UQS7gA zta;0slg3$7O-({IrDf8%)}UN_+nClV!H+j-jD6v$(^{rXGblcF^puYF_SP{{ zStK&5g^^8e8Ey6{Oix8cOr6>`s=c*|C8jeO$M@Z!iAMU>a7sX<#9-7zWnHK&iB)-lDFf@_8ikEqH~9UbkhEt3pOjp>-qD&MTo z*!GT={;G~F(>BSX#8F4|yUZyq?bBP0y1#7C=u*Dq8UFIwo-tlJH@XzyeHG)ft-VgM z(o!^4H!3))TB!shpjFw?UMXl<#hhK~*1mEn$YND?Y$;Rq?TVi4Sk~0UQpL5csmWfc z?06MR$x}04Fy`2mo#7U564l8j|(~>=_>Q@TpwG$N0=S!Kig(g*cMwPr^R%%>qw3E+Iaf)n0>rHn1I;&lL zKH31DL=@A4zApKTOf&B>HG)zuP@V?72e<~fde!O1}rEwQF%F+7H4+f18VSkmpRns$G@Z1AB4i(>8Rxut5Q-7VV46osc$ z4fR2kE#=JnGhWiN8=W$(J(#?YYwsA<(r!|sz2&r1P4Z9b??Q5BFcGpCwq{zD;S(5} zl6?9yx;v1ne1jNRnH0a+pzzdX|cSYX|=cyRv_tozQ1|LW`c=umz!%Tm&XP( z;+AZ7tefxWk}XihG2uFJbVqxz;9)j|4DWQzR(}fBV~0G&C_{ z=3y_WW74#iwn?p11b5Kp(!t1O(Ox8B+r$=T6q?vNX?pN5t#u+Nb_?m8lM3d{)s)MI zRcrc40TZg8_JR#~waM+%$8oN5zM8YA(amC<(&vjnC!3nX*DVRyJ4T<}D)iMaWqONM6R8NZHTG1-X;dY_g_<2raJ)Y{(f8lytg>^s=R zd`xRFdbCZR))q{v%4}tq>I#}^D(eK}hg;@$2Gf=iBV}yM=vJg>TWO8qo!HiXs>)n& z^K+ANx3`?yF?|}b=K1>Cm~iH3)+E}x_7c8eQYw)l*0ki*jOX-!#V~Vh zzUp`PKX<_0@66+z%2X%sWS9zd%n2rlCMCtkODlN4OY&{nVoRk|=y#Emow@$p!n9KG zcczr>|L3*4zO>T2Y^%PX#rQ_wj>54)8Xw zx=udZ|4$JtV}oC6YOqSoroHUgl2=IgU)dvqQ(V8A&f66abW(nsSxNp=wDwh;q93TJ zP2p1<&!Xs5r$R40I>;Rw(o8EpqM7EUi+)#7dHrSx%C>r5$r~U1Zg~Sm49c~2SeGo8 zt70S0q?BbJ5|rfC;_}TuZ@g>J2~V9{%yfIDB6G{>Z<%S1Um2V3am4nhS*I_{23?R{ zL9x?lR(3|(D;xGh!ZV`Zxr2W+mpac1e!bu!n@u%)PM&XM`Q2{Wip6sZrC!I5C`0~C z!G9J-EDQC!R5qWviV?y>u=^j9nm87g*omJ1Zqw~8<)qIx!-R~)@#!xMnOJ2q2FVXK#CgA8~jvc%v{h;4AEm)dU zH2w)PtgQy)Zcxyf6D%CVXZC17BUwDBi?Prnd$J($-C_^0@J9=pYEgX~@oYfEY&>j( zim#@do!P;>&g9qB%Dcf&)K4ExUObzwq?twqGY!5C;-m}WS4v0`{BNM$h8HBjE!`FA%1fTlU zLNMb?=S&o#%Vkahys+7DA_~IDsPqL%hzg~w+vUpkQgMxXd%+edGfaYlzRAlapem;; zCO@oR+@^YG3rAyCImTsKCum%#;`HAdOoiBR9H=ol@tcm$5rdetvRcZeLX?B%HK#CE zx<=GeGSG&zQmT2!0J42U&ZfN|qceOvX6OqT4D5YDsqVBN%qaPEni?J-l1zqwHkU>f z_qj)dF#)Ru^P*Yy-oZ*W_;UVl_Rszo&j%wrpV2x+#=sxx$&`dmL5vI0Z^j=Cf4GPs z)!d96sb*uAJlp*tioC&2P(=cQbH{``Ddzx3^Hdcq}?J-oS2_;Iy1ZKGNXTrCrDhv*1UYXS7#NVH7ETnI;(`O zdHFz8=I0^0s=+*1<7de{l_y$Daf2*;eyQ_E3D(@K{ikbxMG0H;^0}bSDnM&a&&fM! zzvf*fY{JV9K5!oXkN%gG)?(p}z}8P$z8n%;iMbS|i)5Z!Hr!_fAxwQz6HG= zr6i(uRT|ou*JHnjITI~5xZ$lv@ns^UC6K@-UA5dCwPp=wdY!>Mv4_nZE5TBvR)C)p z8Dx4AQPtuGg~=@T;>31LL`~1;L~_Ymudk%^q}`=oDV0%()XGQ@&k<&@=p{5Gn{Q`j z^R3KmzG7$dZ47O`X5#YgY+SyTk;^Yty^JHZfk_ttJsS{JJoeqaj6(j?sC1>26=m5! z`d_lrEw*9ZqHFTqvKV<77&S$*rPEwh|OF~liJE|2hSbdxNDVFapjZpl_)c7PQBCiUyO@+8|bE;Ol znvV*WQC(ep!NRta%H%7VONm5E3zEuaUX=0lY=cfBF*0~Er5ekj7Zidt5i%`EvZW9D zadt>qGSZ_l4eVSB#C+QlD={RDYFaRlP^&epW@9;Zt93-uS{Ikbk9f38Fnh-1u z=>H_C{Qe}gY*iHz_n(Mw42NA}*t7&Jxa!lQyX{wS1=AWe$y| zPE{&Nwp2CqEmeyfn^)B0hHf*pg%iI)HOSN7_-Sf!Hl&rh?7yK%zo2K-Y@*`K10jJ5 ziSl#%e^#fau)XT=;?@<9X#$Z?wu~G?}#Pg|7cLB}M-k7S zHZR?2Rj`mQrgM%Lb+Fty+H|6fjMbv`%gjl8WzFA1TrKv#G~Nt@4PCYh?wyDJXRvmp^{ zj$Z!_J5ebfT*{)H2nJzYko}a*OVnbRAR3bsNNO<~aTfhQ6=a=~Rk{4!oVjMs0iW}% zQ#RO4i6OAl4IuCh4vxo_?{!7^d7B9^o!zN;Y(h~kiIs!^ImRG+J{BxQCo~8kA{a#g z%S@>}S8(xX{%AoH&O*hr@hp^Rn7>}cV&c80lCbY8d0Ag2OA%D)? z%FD*rWfHT8m(68p)z_KM^yIu!)nK<=DK8fFg|u&00b2Qz=jL3qZY^ZUru}Zip)4%x zu=u-G&$f7%Yj6}*DM@Dq!$3QBEPll;n^?w_!WDy>KJELo=V zM2k6Yk2ANkl=iY#>zJT$wCE1c9t0_r05538TVc^>^4$6qy=T+|Q?@8qn900H4vO{$ zAm5kCm*%jY6d{430VptA#%j^!fw5FhiJGvAcxuX`M1)A@I2_2ZVU-hX#rVjlPmu@+ z<;HVZ%&#aEFrU7##fOu|D9;?_pbaN~bu!x0j4&yWMXOR+y3@?HAYblI>%pv9ib$|0 zcidcerG{9b)~vyUllOI2LakZ7(j2y}LG1vDqXcX2Ic~Pnt@8@dhBIGE^J_muL?NK1 z1fxhpvCOib+1jv{pQ_t*;iwCdQ#X~|bPA`l&hgwzT%`!yhFN=0+ZP0TDLc5WQ3?;F zF4I9=l8e~i(h?iAXm%vZcp9g(vC$z>N9Ct`>BxHU*yVWrcOz6-GHG!QMWL+Ln@Aws zkQof}rmOkTFT{9G5|*@>&y>R(qrzcTBBmp%+&yE)N3S%uN$eXDDnT1Ur% zNvj9}S(8bdwpXL+BH6AAd>+R*WxrXZ5EM#9x5Dh0X}Mw6ATEpfbv#*)F2QR66`wf< z&t{v*b+}4ikMVj&p(=)KX1%CFQuf?Jn#C<5Dk~;fZV+U#)R1J+oShK!`MN^Ie=nUb zW%?V6x+(>ehXT393r>E_QosT=VUDwh0p*w8%i_FD$&9bev4-xf85Uc+%4WqClS4HsiBH!riCt0ge&lFNNt9wj-^)W| zhaI8m-j-6Fd`nmY;d~3g+~XwlglX{Y)*tTm{2QLORol5zB3qp*pwm7CnJ1Z zf|nDiBGEIQhDj09u`F0zxcRha=UXPptroiy3adQ7q_P^GW2$9M&~p(1X|Dcai8Z?F^Yh(2 zyoAclQ;a=@74jrr3ks3>OiC8-miAc!x=qcplE^IvQwcZAyLf&4fHdpjL*=)wE_O(Q zs$F?QKP6EcR>NcFYSB6BOu&5WNm7Y3$`O;%8D*V_J8&~Qo<^7=*oK$eeR&jz=wfgR z;{Z}j&B2mAm_D=17PxD2&~VF4R}^BIfu+mjM1s~MBMHBkc73JZ1z<%*VGL>~oRpGa z&RMG;2~;B}T1gwk!o#w{*9TvO5Gyk2^kLIWfGDpiGY4F5~3t){@O{SpKQ`7d{?T)Ozb;H3D(>}%k$Uck)vpxjL_o32&6z|$)EW3&m@PSD zDa!2T`CySA^k@YWThMyQjMfsdJbuL~H|(XV<#N(WGK-9%Q-sYw!i-x#(D{x0+5ARU zw;$D&_Ps16StU{wl}vZKm}3(}m0zUFL?aoVCoLQn#Y~QBuwtEV=*q*a|NJu;7VZa_ zv6VdToF)ziF)9HVoeUC0edblb3;E%-JNoWYp_Frqj>)wM zDuW-qfc|C@Gh*a7o^n5|Ob=-xVG^>TBM+>+%VN4kl-%W3pf;?s+o5OUpOf zqH|PWky$F68Amvmlg~%z5%^D^FvQA}OrDuvWyXuPlFNmNVhbMjkWUDl#zNSIGqruYfGvA?;tVEGReq^UAr%F5=yiaf z=inJ~d_BmM6H4M|0E9OsUuwpRW0`m!ktLx>e*+)qC;GV1au71x7!FttIzT6SQ%)Cx z7Cmf4C>Qt7kMab&(Vna)GtUBX-Px~6)DXrI(l9B{NzxODiUpl#Jzoz zx1N)B>+JuYLL7_VIF_pK z$n$s>Tw;DC&UR1@QOp80xk22PwGT`=+AQ?QKx7h{C!Vn!O!O)h$Lfm6ct*eB@92jo zFX?R!`3jvSTIdC@2EIEwqx7&y^szNlity!ZClfk3cRmdUKxuV|5hJol8$>d3 zb9nF@^7yZj#8WNSJ|Kmz5aha=ki@*tjbV^84BmYtq%*u-Vtpjs8Oo4}pYdYDC%?df z|9BEIxx_&u3nnV;Ra8C#Eh%{?D2YcdDRK1R)lMjh&&UnocTM3sXQ@Cvy>q0*6{+WmBjqPgx=wY zr^5B7B7&0bR-?62_*^zlfbk?Z;ugny=usu-M|@;T0`}+>h|X?&4kU&hR-2v7bUD*5$Q$+uP| zVN}RP9h%Vz@fwOEpM8mmRRWgq{86TxOvECcB}b_|(N+jW|5dpSLjuQh%3vIREO|;M z+6pP8`=VP16#*>8@cu!aJZjk?t9hFSKK@g=d6!+*uCPLa1-IND5|5^HW+D-`jl7#H z8ao9c12^vRqnhwaEQKWq+?HCjNU^XG|2A^xji>XSBI_s&S8xhX-S+7>q<#AJ)!a|4 z=AswP2Xi(y->^2|SkOzOSJvsH(3R8^ za>$UeT3~Lh4v!SV^2m3UcnBNO_LbAcDBvAE8crla4&xptEa^8ux`;z7e4H)(bXnv% z*o(rr4+&_*dG-J^?xNO5WcDN1VQR#eRXw*69ErY1-JR;<-B+Xe5`U>mnp7Et2UkfP zOyhAK$+z4qq>U%Laoj=2<)s_ZjQmFFzuy?G6`e#W6Xa_y74v4JMjTh@kXOxDT$3n_ z^$6`0P1Fo^Tc(FsYiUGWE4!aea+r<=YH8o{4Y;>hXr33(#)f&Kk@IQsX#71TzFVHu zVMC)4y?4oQX0DO=2}tUBScp{fU#uRfT|LDR`AObRM54*nPA#}z@Kmvg?X){AMFK)i zbfGDuSAAgkL2%B@_v#21XhPO7JbfIw zVOaW5M0mO_pDyOA>|z~BFH^z}@AjQ?nobCsm+cixI4{@#pv9~p87tD6KIWUBE@xfLhjwEN-eWYH49!vHgTfw-b&+_KHC) zQUg1gG1C0^DWsy?E#zFS$C+F99A8)@f*djfrH9G(f`m7eTda}56Abs!qZ z6;dk&oHsNHHiL|y7waTWZ!<3F9I+L_{Z?v#JeoF&h(vZ2@|9Fl(L;o379zx6WpwFW zM9=-O&2OXAxmlzEw`;COEKyrlaakVos@8~k0>_h6i}ME7M0~hNO_-p)q46$W3}@=_ zQOPLkYn6P(2B}sb-v_rYKG&i+k2lXxqRFX)_&kOSm{4oh=zt-Rnx8OKILWbJBeih% zS6G8SqF3kSm^CAKZUd)<>$bwme(a#Bw6(SA%%;=hSlS6~$vN8BTXWh6zjwg71@p3!?uRNVt%SQnY6sqRau`bp572iSVJ*_8dre=k$1Y)O5%@( z*VwF2Qgk3RPLTO|&^S3|wqLZqPhuGw^i5or2(VR8l5tq6x^mW2d1b59w6&5v5s9q6 zj*AQpQSwTuygo@RIjI~uoC3A!fU5BM8}4-c#&tUWhB_U;QJqdrw;Us=A)SuDfo9`3 zsM+`%RA#?n%It4Inf)Zn?6L_94+(2iklpBPS1bSwO~h2zd@q9vN-ul6vK4l=##l3X z;jVnzzN*X@q*Smw_*lbfpe)G@@HPc4_JpXU2;gZ`QuyoHm$cTyUW-}^yCn#G9Q8)# z!H*S2;aVd@qK2E6z|Z~pD1-67Wvwh_3Zw|Jn*8YW^}e71Z(c5E>+`e32FSq4l)`T_ zjRJCZn_SQ(@C#v7@*ss0tjVMKOvq|JpHcbhy)8>>!0U#a=;dsj1hI`?Hrr|{=c{0Y z<4?x5;PFo4tdjIqmC+moYebgPc|f_X{BE#`b4 zh{;K3x`8)LlSzsArDL(iZR%Pa$IB!C<)t~hRzON7Cl{2hmh(Q(n|i}A{DNv5=jXJNJUVX zO%;JSYdqWZ7gxR8tlwQb2oo!y0iO_i#wJFN?HLpFAFB}4D>T1K&9s?AfvY?O;{omx&=>WK{NrM|={O9KR!OL;dlx2`Zs zZh)Yg7(Q|0Y@_+qk=fgiAF#KvKVa{Mf56^G{eW^cpbJUfp!^M}hTkaF@cX42eiGGy z=v?Va8az3o6rN!0%~8WFb_GpuNvKa_VfctA>ngDzjSV2O2=*IKWN`^c4Uyai(|$+O zZ7_(?6t~ollI@q<34%aDQE_Ie-rMoiq;H2J!g0T@AUXUSN~=&bOjFHPH{ZebsP4O4 zSRb_=C$UQ9macNJB%iAa;hmAl5oRzFR?75*mQ7b!0SIG|YqoTeY@tGkXJZ{GvlFS| zrSmMVYpzPSmGU%s_k5UQ&mLz2LKz7RK{0}GHmnJ;nG-7l!b%6?WHnzifd^*WX?(2! z&AO!vv9xi;2;z~#4TB-X&_tFk{Z-BFTQT&otkLK#)-|>CVbBeo-8kxoN?t8zrK1Ki z6Kup=NmqEVvrJ@#cS;3^-BZZvMLJLBwrrW0PzYU55GzBb*^3BUComhgPK8lk>>_6h zUDcwYZYy-O)uY7}!q*kMJ$8|WVq~$QyW$$^TrEw#pUt=a+2)EXxQZ0MEX(gCPaB5s zi=~nG`8AG-Zrl#mD30N+h&pY!<10*-HrE`-Q;JJ}f`5;z^o-&UV_heo^_%=!s6o;i z_VNv)F^)7H%Tf-0dD9IGoyaW)qljzKC#y7`V}4h>&f%vGL(bF?i0QHAw~)RY?vJaR zKlVlQX+zsSmd>U<`@)9%C`wTtLpBE{T9 z6xK0;g?&Os3X(GkLOEzUlTaC}MbGJ~;S{J5D{q#;)Sydr!L^rI@*I>Q!9A9mpxV1E z6*<<|S*l{hm6A2|1l#k{xE0>eh~y$jfbrFgF#>o9R<}YPmAVX;$`cbxfe<^o2#Yv7 zaRRtsR=PqPXt9D=mb8c^*f?G+NB(2jXd${6IMY?RAZuo#m$sV0e!$RZ&TxpEz_ zN^f-^aE$;MDKXT$Dk>Z+Z9=`*5xF68jgckBE%teHV+8TkAkoYySR*97&(sT=cZ?XB z&XW^Kg5dwE2#cWqNFFX0LQ>4`*z;n!3N#-Z10h#|0cEj>>(4!T__<#MczsXKBR zeibp7A<}U_E@OO8u`VQR7Yey?+t673^}f2_m{*sZW!IUfhwSmHlQGDm5Be4BwtYT=3>#UVnvh4HPTN-$%!Ow===Nz)GGHkMvEC? zHtTpvhkb#~xx$bLx_~ego=glIzi}8mm0pC36$dPIex7E2_CgcD4DYqn95rGM=CEQ2 zq$_Adh<>{6fTxN?Q5(C_D%7I-Gu>`J>&c~8#LrX1#DqeM8<10MoTSj^ zrHe4OdBu>klNkI><3$9?BGp_D(~{_dyI`eGA!K+ePoy-FWGf(3DwNZnqftxLmNj%e zk0_13&XJEiVawQ{c#3d@En77xpbqNL%{YDxxlFO84+qVV)p*!c*`y;CdFG}j)Y1!2 zDvKki8xUoBx0FhU&5+*IssRc<7Ia_Zqo3nB}CbHF4qr(;v|zPcRSMge4H>!W~mJkqs~@p z!q7!jiyNr4Fhk;Mi$v9{xG;xp)zw_TCIr}CzoxK8*RKJo`SoiGC$6F*Qk#VovWC~M z0jKfxYYHP=zXqHZ*RLt8zN1{f2F%8rZ*jC%tC9be>(^pM8D75@%VECz%~^z@oJS>O z`J2OqElcHzVVd#6RuvE|eaI6yyyUZ163fK$L|Kwb%NmffH#fOfB$wXrc=U2dN^C!58| zJ-#^0CUTiasIDv4bGSEmv?&)S6*DzRYM^5qk}0z^L?Rwxh?Qq^j-jr!4g89Y`Tdk7 z6^gK1S*hgZbf$-UG{%eI$#zqeI5~bLjZ=#nNg_R|EU{S>-eMOkj_(#(U36DhV$}W7 z0iwEs!~QR2e9)-A^t_wtso&JN*f2{&ByPAT^BdfgiM5AY+@MgFr-33j*m?R5?mYc| zbe?`gJ5QhH49tX5R~)4`Mpns_hA4LWd_6*0#wcF{h0|%O^$Hew8X|PPLzC?hySmbF zgEmf>&Hk(+i$}E!=0>793Wq}GS|oDWI1M7b=cFrQ*3V-V36^V(YbUW$bh5dS6 zCd(tUB<(Z9olD^WEAK*8g^BH_it&Z0HG;fq-U&Xd+f6X%R1agsyls@0NJNaot|G?X zVHYIHE0$xmml6}|t+;GBwDWXTYQ}t?L)v({?99BOi?EDW$G1f_L1C0#k!UMLUZ5OX zX_T0-()hSbJ%_$DWNBfNtpIdJW`l$#BXUOOiH(p_F1a;}!AOiqwU$%2qxX|{^fF#k zj-MB<#LZABU{fx|GbEX)_Hx!=H!JlvP6Q6<`wN}xrJ>bk|+!%2IY?mDfP zuRBDBge{ouTM1q9QF}CbA{WmT>W`7gUVeqBsU?cZF7q{5e4M~AJxPGTUdxcHQ5Z*? zvi1?- zJyd`M71^EkL)(HiNrCx4n9U*8@};cXq?}`!~{Y-trZG#FHutz zRYBb6Uvg3!ii5;bYm6xPHtp=K2YKuL8v_rRRcSw9=5K=oCstH$x{gWz15^DpSKm zD;aEvpKB3au7<#D-%>Ab9ClDAu%!bhC2WgzXpzBepg5w0+@v2L-T;rB#g1TI0 zv)UkQT3cLkpPe8zO0*V|cpVtA*muLevdqi69&G8rC(9gB276(3Wi)`hRP7X@4p>AV z+BHBCB0WsD7SzrCwvv=jwsiFqE?q?cb$M$m)R6H7aSCJog`nS%g&_GPWt?C&lW@96 zRO0R_b_|2MlbRZ#JVBN)hqb(DDW(ZYwnmeVQ}JswzY%LR%iz39@#L!_;lq@$?Il_J zG;mhVPQ)ueQ@3rS;6Hq<`m&T{TB|6P6+;wnmjV&CDQ0t#o-=dF}E1}TUqTyIWU8nEV@yS79;9KGn5)8+I4PlXC0pnrr_paDM9YvSX>({1!NuO z=N2p~kT?QuD7&ipJOm&k7*HEh{@dchHM}UzX37Md_Ejg=(>xLfi_5IP7f;~{*5uKe zSQ@8wAz_X%gT1g)Hl45G=b$xbshrNZhBqH+7Z1{5Chfhf4mpL)>?aTJgM)i(6l(LWT!mAvAnUz3r6+j#m>wZXf51#m5N%5``ZfW!B&UdvF3qP(pTLu% z6dU$Jy3Z{{i+&D={OQ|`~_aj?$C>RF=-E93Z?xX?&V|b)TNMB+)C|B3ecXDO?X|tp6@Do zHRNGxm$9`Gh0JB^DgN=g0G481E7fBBd{V*fh~v6>K>JBb+x_e;w`x>powG>fX^>>l zZ7b~2JufxvJc*~+lLu?y_*aJ{TXq)kLU^)CpC#ZA6Ak^D>PTQ~&^UI3wP}530s8d> zq(o{mF+8|WoB)%kKXtf8Ec`ffqbT_byO@n-x^-%f6Ku~b)>-tHHS~O`RWaKui4j5} zNpyCzNF_16T1zs$6exyKT>qnU8eIycuzIfV)VwDLZQqk@YkaQiR3@{eDNd#m{GE4b zVgsT?ZfjW;&+Tz4-9hh^>!wXeBX8ODv1yf}rrOE~!^t>UHp(=xjf@}AqX_t4h9yaUB!AqvHM4rZy zk_gBLU^N|%y~rOXCE;g_PB-Jn(90A{b}^!Lr3-G}b##877^YcD@YnYP_zn00{Pp|* zzWomnP4zvuEQ2D3{bo4Zmx&hLXN&|MX0(n3lMtlqlMuzktS`wp0B3!HVo+d8RJZQ< zCW6S(7;)UhwH{$6Ca?{Os(4N%)4g74l%-JundjMh#TmG!Z9rIW+H+aN`1OdBSsEiy zOmEgFZ;p`{cztovysAcw`4Aez?{8S|{RZm2zahQ%8>aVuKlI*jq~7}*)O+LL^&1ej z0loJdsrUYd_1+k-990@xAE`H~r;OkU=FrE@7*m1MQLR|&-uTtoFV$RU@ zDpr7;7G=UY5sILBkwIW>MM%tyjwjep=VpzV`sf#qWx@Fu5iPph_JL~LkT=trpiP+8KNnLAr2J@yd9ZFKaAy<)*wGd zK$Mn1f;5@b(A(Kk6mE8vL|oMno`gBb6vJETzqd~utGLy0ZY{A)NWjf+&#ECdxl^ki ztGp6BmfY+<>{t<{oVvk-5`r|F-5o_h+3jN<7cf-@2_n&AnyiS3=T6MZw&0=riijy8 z0XM(?vWD2?Qp{M^*p{BOnd2-5XlWdJaLhz&V5Crj}cpjN0u|%(0tRsh|B%v>oQB?0d z!z-7PbfG9B{u6kb{WpkTf>%-O&%x7Ka-2*`l4T!QVPPH8o|m4hOb-|=P>aa{a_Y0xh}d$XT0;)HrjlEz z8A1hOH}KN>>yzf!J_y4_{#Fw_|TD&rwI{OaWrZ6Z$Q2S}^F~x60>@A}qYFuAgUz z4TxJ=jKfj(B(&m1oLxx!f>eV@Qp9Khy57fBivAQGAad^#%d7 zalS%x2jP432r~cwpE;hB9Q?bOISO% z_qVfcPZSx!8&~j@YB5G`fNnIgr5yVKRiOgg2qLxh3KzMN7c4XR%xg4v*i+@|2`0i> z)Y|?ZhVv_4rciFmq%+-4tbIxYGraeT%P1&n7hDRIp=_FUD0%cmM@eN6>GCNw!= z22qSwOR|uB%!J8U!{K8FZ6O+;wjx4ADHdf>N-FCb4b3|#}Ym((WJHLxO1%d8M7lVlB4NR*5UMeSTSE=3`$ zEg~7Uz(9y(XsWAaktL^$8`Tn(;!jbf>7HZ?6lD|d1wE{a$v3FUCd$VIbXKuUm{H6k zqfGQ1WvDmfi%p7(O>t&dUEz2h6+ke`T5Ignzzj{)YBNfT+9{j{lC;>#hz9Ceqsfv2 z>DjPv)4+^k=!^Jrb>@K)G0bA3=ZFrHqcSzq5FL%m*JRaMqDh4Y>Ksuo2gViCsbUi`E`?dGulAmT&lz;38@TUBZU_Ql&R+qGVth9BALZ(EPFL~ zN3j2AVY=gES%=s1XeBTxy_(X?m(V&-{FO-}tSw#zr66s(Exvd;6%C(I@w>d^zeNk}!-7UScdiiXbl45MJWTLoeT`s>d zsk;<(7A#Dco92fhOhZhH8{F&prsh)BO4o=K3z^3ncQGS;Hc}2F3lKq?V)4n{j64=a zjhuz(_Jiaw)h(1MR-U+IlZEk%9G#G!+zkYeTMP(T5P`al+AZ<{)Pj5G&Q` zu$XX7dABo=edyss{nF53{3(l1Jdg@|8ZL5jmyJV*4;>cfm|5{F?BOV$UHmKMWYd*& zQ!3RN^y$G*_@PyhS$zyAmR+XVi->HlwU@&Ceq zn`}7C|Kfkl){`vIJr++^E^XZ>Y|9txA(?6g7`Sj1H zfBqN$Y})j~pFh(ykX9sDf9u;@urhA7VY~8kf@XY=z4>^GU zJ%Gv&yIxo-`ZRp)_XR#*!{zg$gT&u~@TR*rk5)*F0Urb_rzL=Yv_*t31^me^BYYX+ zz`q>u7RbK>@WWuqw-Rt4-e(oy6~I>mo&o-~fWI{$!q)-bgGQf&BmC>|y#oNBxK)G? z1pI2qKM3$W;e7@J{yq5KA%O4rnFt>S_&!jdBLE)``9}hNGT`F@&w|{^fM+28Ou)MU zp9T0+@IKvuF9!ZH;D3aA?gM-Y@XQ1JQmE(ofNu%-0>GaJ{)K=$fG-04crc4B2K--; ze+l5{qW%MZ0pwo>_(Py?%K`s9@T>s*DEO|Gfd3SDRsp^o>UlNbCCI-P@P#0E9pHO_ z+@?b!{5znx0|37ccm@J~A^eVm0DlGEb1>k)1fG$EGm&Vm8V@)&)5(CNrD!JL@H$Pi z00*&6-GFZm31z@1L4EcC4%O2%4{*rdG#_xR<^_O%7Vw4mK7cO*d>g zW8Wgl7MY@w6d~Eohb*lmMYbfBO0s1iMudt;k`O{6Yl`eSN+-uv zsWJ?u;Lurq-31Iqh_(_sD+I`qc<6>?Aq+v7>TY2AN`kf>A;-;DznK<}0puD&mAM%k z$<~;V-Bd*^j*q^FEW}Pwr#507qe*g0eW4nMm^qok`qETvC{B?6A2|*~*hsBx(2<~J zV{mNrj!+?X#vXr`aA;g8Lk^4QqRoZk_~{qPaqNU$)FyU)ewqkomWyFT{v4XY;g6%r zVKcb>`Kb$794A<3%3i;brULNQiqbxW#&HsOsUsLiKDs|S2|LI|_ae(<0KEjPBd@;> zwE&aBL%&9r$JUF{1j#*EeMwqQqbws}9LI%W?rhfINK*(EVk7LKhGR3h=o`to?0}sm z!cKq;tW$`w9KmMruE?{8>;dfX8)!D6L`m8WvN?CHfWJl=MW5Uj%2>eQMF6{8j3b7g zzTe4~hrYLvqkjmFM$fO(SSB(`);f+SUt`WZXRM&P7sk*zP(Ve9!k#A)53KxJEU z5XO<)Uxt?BBzz9zkD(fqxq)jjv*8$J17<^L5*Bc)cs8(sop5jzhpptMA0`)s#&I)D zpUJ~Xxu-AfqSles_-R_9e(ZzX{*tt+&}D3hc2}P+xh;~hfW>pufOwp=h)|*^%{VlJ zpMHVd1|dV$xh^?$4Dek^!iMOBc_a)?G!GwI!oiRwTVq31s0COO2dzAmxPj&q+Qwb` zQtKT_kcJ_Xu;AEK&jCI<=SlZ)ENQcUBql@>IKbLPmBUa(dPt#;f-6nzA)Hh&5JNvs zCUMgDl1n&hrDz2hW%dwtYH+9{J6)Gd+CV3eXF~(eVd#F_ScYVAwt60#E7nnf?uT_` zqc0=h`h+U8Gu9CA0uk@nfp-+X)u=O}%3%yJP#6rPaL~2M?p(E3{cls)z(99S!ZvCJ zR-cE4!{|!@Q-rXLD8xKpvMGis2i|2mlfgc2u#d%y0s}{O!PawtbBTNaUJC$KNuIHL5)TxT_6*A>6^&2Sb_r8IxIwqonifes6}071M;n*&|Qbn zW%PuWa1l0q!F^||7nm<$XJE;xf^jr%tPF;J2x6V#MCQgarO4duOgnNEhRK71NRZA$ z#&HsMP~))rGBgU}EXknmD+gow0a1*Kdq;RFMQhp)G{j@TL>xGeuF-x&h|bpx&ckCN zI^;2q9P|XxMuq}83Bx=9;zHyCabZc)et~O&fNgB`n`p<*6C63|-VccCR3etJk4oeu zY-P@bYHSrAYGc>M(8Mt~9tdu*P!{5Y$V=NEN)n*&Cp%&Z9Mlpv{Y^9yJ6K4>fQ8+m zZ5)IRAT2^VVpn^ic$HoL@A(P2`52?%0l5WiveW; zUvtq(=(P#>`BQHlVUBsw%|1ocnD>!OnyfQ;pF*wZ1 z7`9#%#D%q&%8#LJfe>+Lr^}KnkbM~_MEbB`AFBZt;5KCb3Uvph$$l8ZPHNnL#MgYZ zv~8?wWIryzuLc9wI$+(k`BoLl+?aYPDm#`axGsjFYy|@eLSSIM{3MmGg#FOt_#ZS5j8E?+FB?G15xrxt6{4+Mu&^vy7{L9E$*#|_h@-JX51t1a;i!Rn z4k6@D!VIB9FjWEue?`pmfSx#5mT?$04A=46@8ZyQ95A2>17YEe77&C5Mobe|=)buFFt-Rj7{fx+@PH5Xt=>qRgRTlVlRjF+7iP&r zmeuEl2SN0Z4JlWpmxy6)f*F=4VGsU~@jjvE#_KrYq0i^*wK24-u!6ZM7`quJxS+F& zz-&uo*am1QJF|r76ELQPYdx8lE(xuSyQ@O}eDKZ&9^84S#sfnAF#0&!0oWt$2a7$H z=^u@013PA5wRAwHR%#LFAr2GfPPQpx@chtf@?f(23&8~B3x1e%&S)LK>)kQbLQoxq zS2FOW4`0^*tqZW%hQX$yF!LNt7=wieqt@|MZ)7H{PV+;9(LjJE z1g#+sGeWkb2I^6PMiTTe)&H;{PX|Iku4uBbj43z}ksX#hhTI{r$oj#<+J(j<966!E zkG2tmhV=!Okf!#PSPWwiY(p_*X^>06ZpIdPBHafM`oT!YJJ2XcYof!?qMa3Ih}e*= zumA-?9tfk|5&wjtM+4PIBB+6O<6mOY>tHG+64r2PCl+%uxZzRRWF4>cj))eXhDR2n zBMyz{hKYC}FoXDt#ZBNvPY8(&v?(IKA2gWWupjXj%pHKa70+1gRCG|*&Lwav2Wica zJd0MaM%zZis9@BHgGLFAazuu(Lk}Kpeh4*Uzyl;Sqau&hWJ-tMWfrQ8j%5Mc;E>-p zv=w2PI%re0$_Tt~V-J=PqPIMA2jyo6vxr7;dW0;|5GL}%(2VzBb}pdT)>s{arJ5uD zsliN^BpfvnZHr9vMm?O+Lju&88fYHcnG+h;=%grcl{1+c8;6tu9VTK%2^#4K16a4f z?gDhAa9GI|RwBhhkNbJpDHWdLB3C}{1)sutZZNzD%`=Av1s&A^7{d~WgX5w<4voVQw#UfHVL>9B*g+xzt-X*ZK_YEJ9XUr1Yf$l#%qju;L2@TI{VmFr z-XQY?9gumbt0F@EptOj_P-NQnvDJf%bPs054y)x${+ zH6b#y0klZuLXf1%!F+eXnXnlivQf#6M&}My`H=0<(#TRx=xG8H3?c$^z=Jt>u68R} zeiybdhjZ4lul?^@$=r!%g9G(JX{K;61SwwzxQ#9VZ(OpNBeVP?i`rxmQ1`pEPNW8^ zuyZboKrU#&2=;VXk$^PfDooJRhqQAa$UP_&x6m&?2zEJ~wQq58I3( zl#q}jxnzn$4+*)U3Mm~DN1>04%G9D~LIa^~5UdOWbSa~(Wr!Nc{TEQ90m88aw${f1 zNdE$bf(D&J@6a?9F^z#!)SNprk#_laUK%N5}pzj95fVeEcw6kP_800+)+>> zD8!MC`@qIG?Mz;}9~vbCYUGH-{r`~2<#68vLNqC)s3?@~!$uU;_=+0HIVdsW$}ink zW@j9PMTv;1$oT)KPS&e)RZ5Bs`5{C^P$=VISQ3hSbF{Z4SmK8;6dWY(_+OfdMj?gr z1DoqNgYk~YW|X@~@8GmSFpq@dU=1yx4&9%@P$C9;xS@y01F+$cR%n2cEHUUdMF&BM zWkU}hv2ykm%l$rW&ngMa3<7&_NXoXsJ&B#k4Ua4&xO}K_8&EMvrtg4l%mLeoF@^p=sU7M({-HG5DU39%^(9Vp=cX*$k5C!Xc%%K z;=DV+!9;vVPOV1=Btmke>%iei(O|G+6WuzLg_1rIO+oGz!otr79HTs;9z{Xs14IA$ zEDAw{=*vQDZu&UQ!H|P<;}Lu!&`_aKw8{8zEVk)Zy#ZbgLp!mJ^&eS-6YjXFLkt}4 zE4r(RNw7div2axjUvmr<`i}(#T@zhC)IDO1h-CB)T=~;koCH^JEQM>tmkoXluz-O( z58+WX0OlU$se{EK$d@Eo8)#eGne6bR0A)^sye^BA-U=5Xf;e1+>J`xioC9ifKXd`F z8CmfoFQZg$-o=^|t{4AEV*7uS&yY%3j0wkek6tn-lhAggIffjS8&$|r;DL~BfG9VL zBvhpS>)8#BLqI9K03hT9b5Wt7FAWcksFw{INDuKS=(3@~+SH53{u5P|JeJ!7+!zE2 zQX5a0ibDyR7aAF8>m$&JgG^kDGO9(WITr!da?Vgm)S{BB6upL6C+I@28$6gJ1-J+f z>4RodT$WN?KQUHRU`%t04P(Us#yoS~wJ;X+LyiAP?d2Ur zrex6N%sV{-BW)FLT*Siszn*6jM4&6GM_{y#a;a_aC@p0dldu!IB6|wgpXd?8^@K(# zZ5hhv+xiw}$J$)pjBTYaFSx|%%{G%V@EIS{{f6PS&d-Hb#@fol)jt8^%ymWpU(jG8 z5l>7fr*qH2tn42PH_+?SKLLK|wPw&@IK0-sqc__EtIRg0FQZlbT-q|m(D(CgK?}32 zJ9@;&dP2XUL&K@fkqY1O_AU0l-)gN8jatEMU=w z&VK@USRqUX{K|<3uwi|a9_~F%XT|8@zQA`%Yr4=7EaQHmEovT?0R#@A>54xWoEU4Y z&qU&qZVZ|is;BY4PCo?8zG+GqLUy)YXfvh3w6s4fhv8T&2bKt%VK?0fJ=|AV-TZu; z9AGtO@n=N>y*|4{*o$62)x$NxYux+vIJC<03ae5_q%!YQ{d(Mvh^qgTj$Aap6M0PO zw9<~8JL;PrKTLd?_%b;;Q8*<&G3>FN?PVL2#fz7vQuq@Ulq7H5bGvD{1P?B=^fP*X>wKvqlBNci*<)XXcu7>sEhe~z&t@@6PncA*E^|SQ zUrvbEgl&Uom$?$wFEhkTSTlXKC4yQE<*y^-%&%ugkoZ-R@<^Q~nu~^-*aLw#9@u z4a=wSTKrEBua72EXW%^tWU0}Y)U}tSM-xIb2J52-Jj^HMbHftyGM);Ags!$6YN*T0 z(8VyeEhemm;<7@#Hqo6X<>f9WwYKs%K2h5RTXfI|suZB0lrWoDFU~vIwt_8k|R$HphOD!gNlSKgS4Xt2~nE$551eY**KuLrlh76|UFyDZ|n~}j3@Yf#y zJ&Oqm4YQ~40lVpXlk!3s^Xw2W$irYOe02BvCtGhwx*gBq8Y(Z89Y zvX~%8&H(!a7`RFK9xme1br&FoBq9Y79R2M~n9C`!n4yI*euFS(0`#0zWiU>Nd2h8P zfLelB!HisO(W361ly?bt0XzA1qY%avgt2bYYKvt9)XxVxak?3+Emx`V8@A0j(J)EI z*2RRVFj8*FJccHOFg^!t+Chs$7%L-;{lP+KvKCN*OBoJoNjo(uZyK6p0T$OjU2Q3E zz+12sa`66~iwW#xJb-!g1=W61J}J~1%;u>D10$&2h{h!_Ti<^l08J*rdBtmEk-@si z;2vafEda09AO&aP9W+8T{@UgE?k~4HOxr@YMl@c4cLIaJ$4shQm$?jP5WJ{Qr2x4( z27&AxG&2OTADG5v50GD>-I$cu;GBg8IOu+p@=>=ST&fQG;}$b6u7)qeo)`ke#EYsh z{+Ai{Z2E9;SsD&ZVA)o^n-+Xe!J!At5U2(55)c(aDH=j}I)oi$LxjvUo& zB6tZ)ST|^0e!$;Q$=0e8VUPJJ>K>~yO4wiQ>n$KZcFWTzH5(9*QS6h}Yt}k%-~D^s zsFiEv-a%7EFEI)0H(Kui`O1f1g$-LdM)n+xiqI8}QT!n-AFHz7upOAd?s#Z@^t6W? zI?vQeI8UV#I?oh@k`Ub%dT&jE(O7A4jV8?psDh0GCu||;EheOfNdX{i1op**MzRLN zpAJ97`1F@M>L31UiwVc9d18@@RBlHMk4N}xAp9$Vcnb{`Fk46(q{QAs1f(Q175pL9 z5Bgz4{oQ4r&RY+`o=g>kCf|)C>!uJoR>6K=C#cbdbTbun*2d3K`r7 z>tOqVf}YeRWH6G5Ed+UFunsa<8WeE9hbeLZ#d!V0aiWL(q&#=14iNmW{QsOz-}(PZ zr?-N^L}YLV9C^a4fgOZ{P39lNw;|Qb03E{m|6@216jbjvs2(*J0*qY-ym+0ahA1wS zjVS&GWfuIu%rf?p()yQM2*P0t9e4|w1%+e?ko8c50t!hP2oilNIMIxa1VPL}+l@lf z5xgOw37+bpS|iod1;+?et%37gA@`AnlvuJdKu#r)#Z5qxo5|DzNJBayFgavkYeSkp zm>o)i)9BMr01$K}&=dCe_r(JYAWbznfk3Ie}i>!vf+-(SBf9zs{Wy4Rfn+xtgAbYcr5HSph zrL|CTFpif2g@gz>8d5g*x#R1j5*~t!3E2(D5XQBS5XP4vkFYDlT5dG>snLT8p>yCM z46O~K%t2NY^1@DPBDC2`AmXogbVYTMPNmlj?vV z7Ki68wW&z=g5VTbW*XAHI#g{8!o?H9c^zd!9QgfeEY%PpEQIn3gBu|n1;AtU;ot;X z2AIa)2W;f`MF86a`L26_g1jM{Li~#m0F7iSR59s%`ey5E$xNyUk_i+Bdn$qZEmTXO zFP}b?T39M@e}-Kj5ZFP}0;5CQK&k`@ke*biDEWo604z%y1?&=bIUh3T8;qe#BCC~E6Y;G;?7)5pIwC11Z?OzOCF>YlWIVxbP8L&&j+DX%} zIr+T(t)W*Ba>G0qBSzkz%fV~HA+A!9@A!)05EnhRB8vCCfV}Pj^~f^T5uL{)7@g-3 z1Ri%iXrBsoKRQnd@WaxB9p!a!u9pNIoC~sNv`$i6Zx}-OQvl;{#$j?uMQ$J!@z{az zzmM?0@eluf2!B(cxp+TVqD9StXD$t(j#wpBE-qp25O=#Op-9LLvpdC-$-!5OE+%x6 zi@;-iu@GFU^n=J?3Pc~4ssjY(?uUZr2=y(v2~z>Rc4419PZ?$N``h zS_1tbK;;ea1DhMLt_Uhb(m*&?v#C(wC56^N9P&s*t~K<4GD19b7Pix;%ODXc0xLQF z{t+D9`;!_3=5U%l2ZDP*!Qc`$3j%nWw%+AP{>!}#&}^xM3}M-D1VtnsN{meEIiz|a z4^~@($wF{?UIu8Y2)Pa+90EY7Q1y|)%W!&24?IE`hb{ng6@)O}Y`q2;&O`{qkIP5D zMTBvKtsJ_roH5k^WkNihl#O;CYz@x<*2(xojqcrG4us^^1xo*j?_VDBdSPQ(f1Izfgig7_t} zm;{oUL+wBilm6WRT>sqw(pLT82H*^#ije_AeZz6&{!i7@6o3M9h#5Nx6&tkEU{yE; z_g`6JQ%V5y&Xvu?wjl4h&JCJ0pzoQ1VI8@yKH4qtyyv zu^O_N332*@!k-S9ZlaAL*=s|xN9RYZAORNobVbAp5)ed*4rRhvC=}dJH>5*A?WIXi z%C~V4;n2rvP~Vz!wq-NjAZLNt<=#RvCi)b`9nBA6%!Jz;RKuv=^M|q~fs9AxM(rJ> zdr&;aVnOvm_kbWL8c^wng}-NlTb37M1 zU?A3i1IjF}00=h|EQZ}77|E# z!&)B88rH?5xZ?&zNZD5j1l~yvM+n1TJV5f^jt~}tDL4iM&;l7Y8VlQs>=j4$+QTvT z(u5JfC_Hx2C6G?US+c}(@N^DjuRI)+PYK+rO5cnwED!+!7`gyTgy< z(EU-M5`v?HO8R%j(m?jYRZ^Nu2_oIiLu=9f@@lx}^Nv#B#77I0CNZxGwg%1o@qhL7 zoh)blDxR3^yRnj^hWlf8Xd|80yf-d!sFiz!C(Bt=ODIMzGL|`Nzdv@PcHjeVvZnU< zzjFG@vuybNBr$TgVm;=M?~i5EMlvnE=zX@?vO#lrOtbXvgpeQ{VGDf=DxF*b4oQ2s zr5$SUL22C{Qfx4}2c5?@!N#Oi?;ph6T$6 zW8sV5fqR0Ksy_%5u%G9?6!-pkLXP?Uk`w) zR|v&p&_8-Z4RDdhj^c3!>Mbml8|;C9v4r9gaS_`Ob%!Sv>H}%^8PJ?79**d~xD3{C z(BWPebqk01LL2EK#X^}5!GfVd)u5E7=#N1Nv;QkjVG~1I0(4`9{^b<~F!NRvfFjO- zOP@CxlI_zCA%KSs)d>Y;2tbug9fP7ol%c!Gf*Y<7oZ)}GoVJCCgz#V|JEKfk^gmWk zlOq3rDyNC+|L1aA+w^~~oG!Vr8a@X8-cSp%^OAafQaom%?0XE^MP8%qY*Wc$-rbaU z;pO4s?(c*pqCaSBy{hmhYik`eV2l~KCM;O>ZFgN=u~p{Otv;ozX%_Tqv}1l)$AicZxxtwW#GpYQOSb^L9#h%x~g!*%M{fxLV{yD~zvrT^@#XjG$E1W1@ zjwapRc0KCUwL7#^@(0U)Xv`=mR|pBK$~cHr8ZusedYKqLN8ERl*y&s}9QF87>TT}~ zvxlwS0Vj2F`|8D<65Rv3=3eoT<99G6b&VO<3hCXp^>%?4q4c&-+=XvRg_71b@{~FH z0`~E|Op(NG_3UaNuR8Ha?z2>qn`1!RP%!-CPGg48n_o?R^TTtCm!%1>ZYs68?7c=j z{+HeqgUQ4z@%(<w_0ocO8yA`GF@lF{#bq#?b9l2Cw>hc|z(yeomWMq)kKy`*CK;e!aeXELE{f z`H^aoBKM8_F7w!tn$xWMUNx`ZeQOZEWHdKnl(lc?d@ViTv_>Cg;5y01O{!?5=SBZ+ zuk(UxWp0)$k3?_LU!A)6Uf5d4Lu|v?rM(lS1tb%jtBvCO?(R+3*h{|9rX5#UCi}}a z{+5QoAzueq(!BLt+CG})+(P-SgbyUbHaXh9xzYvI6k+qz6}?NldRr6SC$8Jw|MH_* zn|bA^e??4weDn)>POH9BqrQw^mV2%575cv*s19DPxP0JF*=2!ffumm}HcLL-tsk-L z@^hm|{;w9QB_|Zy=eN9^Hj~f9Y*Q@Vc%BvU_*Wal=G>rlNYG*k&UoZ!_>6J5x!b~- zXT5+;2!JIqeMtW48S*Dj;1D|BoYBxsYv zSc1Qa@rz@BRK#zO-TQKTQ8)adM#{ue(dBBZmAZ3{rH;q0Ecmcy@imH$L;aJw&%dI(43m__lr8I@#j53| zQSNRD;J-GSmKVRAZSpqTM67%Mip9P-J=I$s%Sqke6Xa$wl9f%Zz-&rx2^bf=j>d-_L#hPBiYznB4s$N$c^rFS%90 zzbt+4{Dwrk~w>~fRX%ebP)aZc7DE(5)M%S~2!aYgZQLmtP*ezi7v zJnGn_D=6>vC5|~vIQ=O)&PYSSUsp(#T4ujI=VEz7<;%g1ZLdX-lqK$de5mBTd)z0A zlGnOhysQ~>Nob{SrI@tu^so>~blRGxDV^BSDevEZ;#Ac4EK#k5yKT3lU8FT_cXfZx zOPD)n-@{0|vbJNW|7Goy585Y3d8c~m&ATV=UtvB-HjFPae9&NIASJF9o|A9#kz4tA zOfBA%TG_0?@OgQrEGex}GFz_ir}CW}0mI~l*r%#4d6IV=gg;oYcKk?IJ?ju;qAZ-J zwf$v%!TyvhBBV+2pHe3Ri3cVQwLcd!P0-(?;(T>fLRZCC^r(rkUdnWqPN(R~HHS~@ zukDI_ua!y5JU5tfrm zc55@)-Q~t#bHkl`_7>dKwK#1(OBsgu#~%dXYU`wE+%M(A;>Q}oB! zZ3n`(DwzzZMntH5vNt%c(zCC*Zpi=DV|Kf^lVi~L^aZ9->*L4<{vDQ%`Q*e z`Qok<)1)FRZ|&LXn`*jNH`Tt7jHwp3)Xo9+xKqPjEB{q4kvA`Mze|MwOVXj?YBQq! zBE)PQ+E~%t{65CWV(MOrQPI@KEk;!)Zr93}Hx0aK&AOmw zY%5=})Z{4|CidHK)SbasRrX-J&UeSX%5fEGd8bd*QuY*t8{`Q_mNK`ZvR=oqXs)7gt$sh7n5u}4+qy!7LjLJ}EYAF1tOR~yVeZP8M; z2}gR2%YNS^IqzIa*K`OG5XI#-$88@T;aE{QOn`r>SsgzoyT@evo=CMle3$fpvi-8! zJ&XNSmsv~7V~e5}7Mt=`S>>MF!IORXukkfIw}_=*T6xYtzw+*p+0%*FTj~@_JZv+U zcju9Tpxmj0dVAz9eQ1k$l!P~WuDh5S^XPp{%AxnBOZL^#muISH-in-x{4$&NnJB7v zDVe!08}y&K`}l;A-k}E%8zb^8Hv7EG-)XyBWS`#K^hq0M|Hm#Gx>Gp;-_Hj660~*K zf~z~)TxIUR5KcXO=auy2jh}p-Hl3oKZ`OD_ms5}TtsUunW8MB&GsW!S zanhB84qt5*`nI0w+vgNCl@U4ZnKJ$Oo9}KnM=gi87uF||stzISMsuw_r*#G~&zW>@!4 z`DEjtUYW_@xO(RIl7C-_k<;$1{nAw$d{q^qUhXwUPNfNVtfp8Guhgvs@3cK8Biymd z+%9JuwH)7J`#Pp$wda6QfpdyXk#Q%p#JJ!JQQ7y6+tkNmU+cch2RhUCDhz!*d0K$& zQ|@&YbJ<}-1-DHdsweKg$_R?)37Qv@d#CignP)9PL9URhXY@mPc0%~=jGj$zv9X>~ z^;4Y@H}PR%`KOBgVOj}1UtjEZHL;G?Pi`)~JMF*n=z-#;>D|%0Wn~Xom>l8DGP?QH z$f#_=d8w<*k~CP9T6~q1Pd{_Qe|>rL5J`9MZ6Td2JhPp4e5^~n5g7#WcguAJ{1n!W zLu0j9Z~dViE7#b@e>*Tm;}SE^KlF8DNYkuRf<*ZDniKIir7D#MAG<35_%osWM7M#d zJ9Tq`^1foYZ7Iq+S%&wV|0XUOy3K}7?|2RjY-liys3+RMfur%x-h8W5o_;(y3c5qq3^snC=MNbZxMsPqidgH&0u2 z%3!GZ{d%O_R>udi(&vm!ZNJ4!37Fm}jFIyy{k&%q7ylq@@QcXKwob1nbYs~_dcZha zlw8g2dEOpMWC}65)m!JdCpFW`e>a~{M5+v5H~;6<)y>pPU-dOfW6maqZTc2#G4Zu= ze@0qVw4B2t=g#4Cj`d!%+*!dG?=0P@l_W2lb3M~B{mhR$Z*oKxz2_O16gQgO8lbH^VanMPVQNPgywMu-LL-#Mx4)P}dHy`H`>BQ3H+`BlDIeV?V~&wk2ht0cJm8F!w6xk>x-ldyAzoaeN9c5W4i8>+Q$zQrQWKWJMn|c$4Tc^ z9URo^R8n&&&n6xTxHO)0H;NqGKbp%?Ffzb6dN9SRsPx{c;A=b?A(Ts#QG$v363#E%hpsY0QJhO^fZN!BLsc z#(poe%e#aRZM8U9H+c1eT93{?6?Kh!*=B?-H_E#&;V$s3Zd@19I$hekbdRsP;l*{r zcvJ{a!HPlN?#ue~#wQ^w+x~9;4CZw$!y!-46;5=?C@=*}B(M^$Sp6 zR5%>7^Ggbzsp{ph)hQ_S=gu7d6==Y>>!)ezcm96)WZV(!3)5>g#)W#z1;VbJ>=z4v z1vj3p*|)XS*SlKARd_K>h7djE{_wZ^lfTOY2k1HnPb_Qf`|8~7 zV-qyOJbva{coW~pn95hZZ5M9W{77^DXycrCxK~_RcB1iXj_sR*nCEJaEsI5^_LMEI zh3@y&#Umz}f})MeSM$c3Zn{JymOtG;t~3@LJodsoruINU%%&63)pxEO9lU6iAa~BB zGe=qSg!z4g1A3Y!aXC>Rn`U6Ek*KOec6{v2YMZ z@%N6$-Y*sye|Fm+O{k+BOVFc>?yL{;-8|ycV|*y~`gTaY+t*SMOc^)?vdkuTDk5zDqOS6WX5z zu1;!4H5Yr!yHPCe9M5!9R#Z4p5`H){w`YsOO?T5|!vg#zzOTWO3V-K52l55azt)P# zvapQs-_0#v-Dkwz)u3Otar51W8~2Eu8sE^N-Ek`N+g(A6vMY9lx{2whdcIj&a8B&i zE8VR(U7MJmdU40+GGUven>ckIel9)JHDg}omF|5sF4*(YrlGB~n}tI{%7usIwVQcn z7jY*3%NBQNI;Iniy@J`bt(C?v4RA3Hj3k9|8{2kq{rH6_fz|iN0^8*s^WWdhA6$GF zSF&s+Wq;;r{uxeO-E&Ppji+y(JhjW;$Geo!df%V7bwgJgzx3aS`n|MV9nqOf#&uyI z)g)svwpJY=i5_Va}%4p*NpE}uNepLq^652mOk{d zdA#*RT8?9a=$I6Fg%YCrlyuBn-&Py1BiTxPQkZ-A^6PI`?^U#%X(Bq^ds{Sl_29gj zcS0^bY>~qK_wSZKTV~iG_3T6COZTkIGVch_M9w@Ity;9%XP05*#`o*W(R(TpUSAHR zIQ!)rd+xY!>D4&z@goznnTllQsGhC-c_}k5xz^U7b|H!tom=W27T=y||T z${wbw3cJoeL7MfY!ZUv|%uNZ!>e|shavPc|KQL`N@Kd#it21@Ajr1oE@9k+@rCSWx z4`U@Q-v?@L^KR4`^ew(!v-N?N)-NyV2UWM9sx`5mF67v?+MIQ24JfP>_iDW_`?tYT zDQGbH*Y?@2jMEl_0UzxKekqMw@#c7LnM)UQwrHHCq`JAgV{@>6#JyELc4OYQ@rj$WYU$rvG^3Wk=q`6wnSb4* zvGvUNdGjNYpWo5L%pK}^l7G#A=9WtMDFYtFMnTNm{;s0_{B{IPboZq zVELO8vmRG}GGpV8+4lD357(NKaY2;X`*+i7-H#u?`oirngZE8`Y$>U%(H;^|SYv#Vd{ZrK}ItsnaC1WImakE?m1b7ij@uAa!tE5S-X_DT9n z<=@k^(lc)xtV3=j>Mj1lQ}!s@rEVRpo>cteu=p|1I=Ir+df;Z)i|yA|T-qHZ{_Hg` zw)7n+uXgnP_SweiX9nZH@0$Z_oQ}TNVtgMjaE&vHHocc?A6vY7>0abIOLxmzR<-G& z%D5|6mg-3z&os6j`dzKQO|@{0GOuIqH=mwsY3mvg@WJTK7InFvE;X;;MvjXltKN^K zU#A%PA%FV1eZO^EHuStV{T|vVME9T%SvG7Rc4-Kapu=XGVfyjva6Hn#bDUhu2n$9nzP#)X&%ZMKzXjd-+(e+)0eHUscj@i=g2B zM9%nW94}}dFtYZ;Km8q56Cy24D}8(Oy-sKr%KVQn9Yl(b^A`e585+n zM`^?uD-JIeXB#On8@zd0CXCng#?PfI&zAAs>;0*$v&%DnU#n~4XvIMdHD&`LdxO@j zQ)U+8^=d|L#C^z_H@>+R+cr!Z+!&OW%exexJ3=P9iNjWYjTIUVF2qYm$6l$ELqu zmDr>nE>z+Y(01JV829R>s^44JN1sUNwk$+z4)`xv=kVmeswgVd)15SpD|2_*RpR~l zSJ*kvzP=O7l8M4ILX*o>d&V=>V0V_!RjE4<8SUL?9`$~Wd=hgp!$oN-+Ob4U-C(J{ zB@w4b>%U5%8SWcuZKKtsc6Xab_~cflYp*|CED@5HB%{5@s0VGqS{SnQHBYWJ7h>)qwmi`iHCy9?O+w5Q*t<>%D+{s}ra;(lm-#jRj@ zo#pl^sg)AwexhcDI9lnJ^;F01@x&Vio>Fa>mBG)2l}?G-7Q#+rm(P9o9B=KZO}gwX zow9BlF>*3Cy+mfjY2L4QL8&guV3wZJJK*G~{^8fHTg(kt^aL;I6*>)MU&XCk1O)JM zH5hCZ8FkyPcsedJsGIw|ejdk2|F)8B8!chSq9j#ey!_@hmWzbQ+=5~GI!oDGmPuuQ za)-5u4Zgaag{=y|<4v<$z2>*&^Y}Nj-08K$_YX`@@v=Pce#SRd5$5^?l9buh9O^aK z2QN*##S&Y`4y1qYBQUD&jnSU29PxRg<;mm`7j7tiv!hLKig|kC{U<-E@|iQ{_-bp1 z;ja!Ke$45Q4E6fI*jL`OWmeHp*S9!t!sA{|>$~kUlzkcFOoz!J=Etamgpd0Q41QeM z684$X=q^uxPgds6y0o4C{i;QUsbVvqKIJ~G{8r|%oF6cC_0DIrZyQZEgp`#0(6{?g zdqHY){=d8ltQ+?d`ZBh-=VLso-BP@@}np!I*--{ z-~;p4LjIoYEMYz%@mYkNx$>M6FlHMPYh0u3dFzY5iA(-*R;F{xZ^uIu6sp2GU4iA# zjUFW3n@YQ+3cf0(b9#Ee{C;7@hmkDW5H>cT808XGMXb2-kR%$}-(sQhP(AXXL<^67 z`{`r-8vEv#w0vBnPT6(M2iRJ^+^uxhzQUI4$8oPl5@!eWJ4m z+xwqP3`g3A1yzczXz%r2E&A~7&Q~QrpXNPFAO9iFrpN03O~s2=i+#hs zW=c1P=9V*+NJ3t(52yE@c%rM&5*RW)l$~%Aw4vm?sB6K3#mJ^1G1PIJ{ld zPN1YY>!rt&rGVtUEonVxFmRy#05UXdHCq<;|eVM*Yt}X)O&7Q8kGT1N#gvc=(;qSE=kHmP_}4&~I!Q zDED*ruwJ|ooOFgdaL%`}%0a>I$C=WyKYN@eb|$yK0mUhD=p>a_HSJx2YiUu!S8zy5u~LVG(HA%Ppob9zF_6G?R5!rla;GS zhkjB01eD+8A5r$s$Vr+S8TvKsKk)e}oydBS9@7?e#+;ox@l7EzckzJvp%=MD6B8$@ zbJZJ1>eAElGr88!Y-`1*h7C$QPb12_dnV*((B4-)Zs0yUtUjJmv)e#kc3kml_EvJ$MnwRCz0?$Q)l#``FL>C*hr z8EwsDP9%J${Z;tdsr{|f$&*$VH*@uCy=VFyljOW)E!K{&F)xzGeAE^r{SFS~TZ`BnPfZ@-4BFl*FmOnHf$vMd zlWM1#+CuDs&PSIGH>mgc)ZZ5PIx%weX_n2mBcAgWd)~DuBnZe`yc-&gH@N9#E3cV6 zc%-d;L|Z}9c52`pv(HesP2YOvZ*P4~*P8p@O4hE%vWe<^U)KGuYP!;u^+_d%sUE-e z&$&HHJDuv(t+Dw(qJNz+TFJO_N6#<9iphQIPkO9w+>Z{;xF2)hHocwy+WjE*UaksXR8(}dj}_C$SY1>w+;?qor!AV6OLJcvg7u z_1U6flbHG8|CqVnw0CM}%RWD8R_GeGoqS?@8aM59@S|*XkgTkZoUG1%S@y0wqbCo% z`$aGQ?EavNkzCGf^Iw?xt)jP{uBnptq-#c?jfb$JE$OyFn6fqTa_45ZqJY3n_`9_4 zufKmY({oixl3BCo-YH*u@6-4_UK6v!MOodf^<fIwy+5pBx#xHs-^ahIQ|9%S!n(Zbr=~SFG&r7pP92lU zrRL6zu50^E+ORTp6n`3(O+_kF(}L3jtqR$%zfSU&&QCKU8Xt?v<@<;6yYb@L(iSS!+65c$_Q++G_?QfMu5rc78)xlJ+$t@j!&~~P_rk_3 zPl+s*4`qbe5DROT#D}38Ds6HuveEsHGL`oA+mM8|^d-&y9UqsC9eM+Hbbl&Vb^7I* zka%nHQrX^sM^?-$Z3~B|@59%+&uB!xn6~ko(M|vL?i|0_$VY}`WR_8 zmr@!@*{V)*zp3=uYuN2Uy`k&9*-!pO4)uNAZ}Szu5gqN+*I6d3xsvC;f3N+sz0SV> z@p|H06XJ`azpt%ozY3R>(A{3XDj)CY>(cixI-)sNd15Nz$zYbpk4ue*702QacRbv+ z?JDl~%24{CZ^7HnYvWc{grI)?#dB5V4FO*|m^N8?d&I8s|7x0UnVL^01Ft$mvi~hk~Gv7eiu~w7zI3FSlNQjSnQ9OG!L8@?2+g5z%3zZBwjD zS+R1WEphflQTQx%{j#6La_WH7cUvJl#%RIn(iV4p!j+-eA4|9$L&Z zm{(>p=#pQydTopNK=GAAq5B!NH?EXj*`lm_{Jy1o)JK(yDRugfKB?F9#jUF4;@rwa zpCd|jnlGA`EQ*4?&z*I>`ugBnSlQ$4MFVE8!qw%08{C&~M%47z&byTGX&l?V*y46i zQR7{rO-VA>^Ats~iBH3QXEXY99DlDby8ZrK`!(^XaZ*5#qE8ml=Hsoekw$%i$2WhH zPPt;|?D%lE-(HWUb%$vEH6>N^-In45ylZ7fmTaWwJ85U~TMv%iZhcX*MLR=F*(Xxp zD2+QS)7!hO*!x)7k+FSqt9lVueZ@vaueFsnOU+g!>a~RUZ%U0b z)J)sICta=X)<2gDWyueA{JrjK-1|z+(6&V}vaCk^>hfY%S&;3a-mOK8Jg%L+YdR@= zv=tp%{0^+F*m7qKh`JaAXD0^!UN3vFZk@kWX>3!h_JudCHOxmpj zg{nubc8-g0{9Bb2^GxdLL*e;QfxyNh9_=wJz44tDfg4s8)yG|h|2E}3lfN=`Vr({P zfh4dRAU8fV7(SEBRX2K@r}~Y;ICuFoe7r%0Q+vEg`~6z(;QL!{5;=nXp9|kSeK+QT zrsPdeZ;rZxDw&%G-X-FD|J)ht$e38B&zPO<4pV(mHQfGt zuYYKM#knc-^F=(hA?F_5z%+uMCB9)!F7DWz-y0o?AFmZC=HE|?6P+y;^uJtlmHBz% z=7@()-RCLp^_r#f;oG+TMs;dycWC9=Ej^7*&r6b*zxDW~(wK|8KCVxlcwUnEF=$4~ z`dYafiE+~GcWael>KEhLn(U|z9=#W5{G;t#;!cLV?$k|Qd#;_l=KW~3_2~s+A-9M3 z{WZN#e{AgHs7jamu-Ly_PiB|N#Pm4lLbQk)TO0S2%hy>yIRf*N)|B<0iqAiouj@{^ zMA>(5_?NMvCxbt7_*%-aQY^OQhRSc(8}avW`v>nzU#b<+>i3uT%3DZle!V35 zqFcYQytgS}`&-xPk(9bc&zm2TtBdzcKRa|dD6n#S(TK2uTY2KQSoO#{)v3*A11(ZF^Wm?y_N~Jz{nlxz}}NQh+_w_J}X0DF&8AhnFvF@nLURq-$ z?su;LFhcGB9YX)_86$cMv^9&;E{vWRBM?1LMCiGGK!lfmw0+3E=2g~&p5^KxpeFab zd@;SP`S(qe{9To;;AYPg=Gpgq_2Zf^x*pd&VchI_YcUUOK8a-Tbg^eE^TnRqjf*|c z-h;V!PZxXgiP`3hJ@e#mi{FpG>=%1_o`5>x{{++tZxc|X!`1N|GRq538<}O%A(>e= zc4lUIC>)t3K3p$LUBfT^CE=|qnt*DhOhEbnl5o%aA}-~wvM(R@^RBX2ANKRu=?;5R zk8)3k3yXW3z7{`wn?iGA9oZ0+f8{&U9;8a?=ceout4O3s?IwVK;LB+>OTmxf|swL6E*gkiIIk@1tIpdT&>)rNde^leIL> z+LO{+En55g20fo$yrJTyUQcg-?uu)m*WREQzY@kL4K}UxYsT#zY5cPrVEk>v_!Q0f z)y@VO<}3dyNUclP*)%$-buQu`j80bg7@Zt!plGhWPiUV0q@g*sP-;FMOU=s~K=bSd zy5>0zl+j7N(VOdYHsZ4GC-Jkcbel}QMpf8quTZ#fkfHF|15#masj&EZ&KF&;D=fHP zDy-H??r8Fur`)X0zFx7K^SiLx$z-*LX7%F^v^t(vUo@cgtlHn6&MfN9^zK25*0+L~5xGOULCVbyD8QgoeSeDQ8!{4SI6M>OLf z#nAYw>tH~1^I&1M%>aYnDm20XTd zSAf~iJ%(P#T9=bQqYN82AQ0#ACLaOKl>s@9mSJ=Q=QXq2AN#xtG>mn{N0y-d3vJxw<;h+dTlLds?;I37yZplUTXS0L)jQh~U~G zkx-F3FG7juMJP);mHWI1#bwt1yLw9L929`LmhO1=id_!3E!KWe4kVxeV=fL+>cO~ z`w{9I`w{A1@@$9m{)vt*-QydYx_r^EmCF}*ws84k;+++2Ht{ip@NhATa|rQWS8qdz zaCHbVv8&%hh@1MDOWDnLR;HA7GncZSuBFU_kS^T@?hs{6dr`rAJ$=f#w%foh+8_Ay zCwo@%7BjBZ1ur~_To57(n0;mI%1s+=({2~?v19K(?{fyGw}G@OZtT2zGWaG-N*{0g z`8sRU2KgevxnHk|&zFJB);IQblRqr}@bbS2WP`UCp(>~CzqM5SJ_oIlP175z^w_9t zl;Zwlz9{aoj~K=MLv617!`3YB0oU;TVj+4*(#;v~!jgRy;G;Z52HU7|w8e4=wV*^9u4I%%fr4FAtWR#Xe zL`jkFL&Z7zD9^b>?~vI7{5?vpy_0Z6fbmYk(UDOu4@B_+nI|9tF4fp5Whv6J5I|9Xn=oN)KpPl0V>=f5$TghkFRgurOl4YSoEhV4K-YoJ- z>xYbdlDAdnlgTZZPp+-S^UAgKd=e-=u8EJV&&<*YZ#rz{e0OlT^E`bia=6I9?aH@I z5I;qvw$k2|C>>Ycl-Nq~o`pg@5bpieis2q!t#~%&79%MdDE&*fcbRJ)=qUFNbd+96 z>Mv^yb$?l-dIWtW%J>W3nwQ>svMC4tiZ8>a(-VS%f9k6emjagcCe3=bXOKoY>QZPE4!D^RU&7^RU&_Sp7>Y)M_3hUw1ANSQ>aSH)OnAQ;e zVKFyEU*kjYxOU2H!uR~zr7;Q~|AY9_@r6a!hqo9;8z@zx$*yx+;TA(z19PosmZncG zoU)kOtKe~eSX|~8Tq=I~a&qre3_Y|dhEOD12+#Lm|4@9!B(?{M8RH_y&>g;cyq%TD zGeoA3vX>>gPdu1nhQ>?E){#DKPv{09U-GS>(b+kU4C^F=F zSy|#F$zlA^dL80JJM&-<3o9=-UEA3Vf7H8|m0WAzpxtHcqci@6xn{o6bIr`31{J^R z?1`5JDuwyhwW2V8*WD=0pRSjMIk+(k^UgrtClII?=Cy$hou{9RoHeq~PI#!lopArJ zR6KZ_c`SPRtExorrqv>P zdnFpt+b2&(@BBzcudOQ2CssAiCss8d*R3UIocudw9$N9Kdbh>@^dlh{#F!TTuHpi~ z8ae8{M$5|k>>(}7%WW&1=Q?3Kah$Q=mmdx&QhK z?!Uf5-!*>a6`l?k`I_F}9iUh&SSc*_H(C7ScWH5H1TFp$0E?do=&y{O6<~U0Y;|J} z%i9}c-B&lBRz2tq5B7F5EtP5>#7Gai(1X^x2Z7Os2P;ylIj8tMt#SB1E4ZyQ28pY! zwu2%d({aIFRn+~Tx!C_{^6zTRzs#HI-*;8u-)B|yQZchiL+3%kf_UfVR1XIQGwaF< zw-U#s5q$}&d)L9ePpyV~8GEFAe}&V%NV-?MiXMG+s`zoQ)1}AW5{u$q>d(SGi^;tj zntT1Ed($lJe_4$Fm&L!>>sP0u*ps`3?o|CLS|R)yevjeLjMdVg4L8xBdi3WC-JhCO zEnYYDpD2$o2mLmJu4f9mvo@vuf($xO{CsWTBJr;l^kVVR^G2lO5#~0d%FSk~)6mo^ zT;lsPHeXZj^@UQ}`!nY7CzrGdK-*2;U9O*5E$Uk`u*{-#4V)Wb9_nWM+4*8ReP6dd zvK#tIt6Y|utj3V}Ci#{O_a}?2d@|mzcVKGEI}3yTZE}am$yoIIL-eVpaF<&S54+rQ zc&3Vytp{rf;k#btC8qNN68tY z-r5XNl}=$T$5&|QAH$d@wv-}IEHBk-#-B>P)r?&`d)!?1SVw=$t*;jYD>F&HUkt3A zc>U61Y&fj0N>9x}`)88=@=sd($FW%8p$m|{^@3g;wuz6I@M7RgJM4N1UGDc+I$|dwCsF@zJL6DgfUfAtCEYURa&|X zpnKe9UgT%z{?=HR-!u=e^iC4A4WZrjH?!X8;pB~$*E(16JFhiRnuPu?6?T86!VZ!Z zw!>mE?w(DLwVhMqe&>%O?pc1cxX;g*ai4Q7<9^^d#C^|mdJauGXU?HvsIh|*Z6~eH z3sf*BSL23ed}nyZy|kh)pKue{w{&OLd_+qJYwahb#CCi-;=g?4}GStUl+h*s2| z7;D7H?jgIb=Bjnm+A@`XKO7#XJ_*2n`dW62Pv~N-`01Vosqup zKBM@4HGH4a!SKC}=KGPlay)kizJGp3_kGqGPrg@jDYkosORTF=^FiI4d0 zHP(z8k;>AoHN8Wm1vBmCDWOI_t{Z92qkhdSHI{p=JkKl@JNG1ffBD`D<@K0>sRr_S z%)r#atmv3QsFz~~^KSQc%plb5m_hpOl{;pj6|*DPRclt zehzY}pM#Y8IYidae`xh{h|I)IN|a38E?Z>cG(f?-E{7+wcrets9rDuaZXK&-BXSm%eCCOXn zZm+-e%wBMFxz|^1FEqXH%)UVUuz1!#SZ4F_mM&u_gZ`GT+U-3K?pePW4a$*r<=)@Y zC7YJ{$CZpYZ=T4AceF7wV$csVBfe9M8FAuqWW-U&^`_ z>%Z6fe=_L181$`1&@DU#?W_t;26<_5u~=#D%l^{HZTh%V@WSor&&HOOd0Gu|pH{VM z^Qxa8Qxv7m#r+>9MYS|VgKARItH+?|nPYmX9d=AMJvBN9FWhGJy>S{MKMvGqpAn(A ztLCqOdE4!V`8nT7^M`8Cd@Y*)`=}nF7mxag(2%O0mM5-mq<$;6j5@cqeSAf^>BLTN z_sCx#>K6H2;k8FMH&4U&#*rJ0{HmKbI0i4|3jsg4!=;zrxVFdP;pQtn38-fAHIt^N ztmB9hwO`K>QQP7+BWed`%cz}Lol(2;2%>hu5j|zIk5npUrEfJ``sLfBs&6&m+tg^o zw}J)Iw_{h+x3=`Hk>Oioe|_^flYhKevHln`U0sv)Eo-Fp;nMoLVp#vNShv2YIKuZi z1!J-0(S}Oum)ifn(QVAO_-ePWuB`6%)ykXQ^HH#xmHE470TpbQRS(#oDpJdT7RrBX zBQB0;^UQ;)F)lV1Aug5_>2a~NDB_atdaQf9OA#1Y&E<;$bzHvq%se8gaFUao8+gH+ z{$3g{x9w}4rBZ(=WuSrg=i?u}QVtGD6p-gg4Q6@P@x%A0KDSa|+3*DpI zBxktnlQnWEsXOayk-AM=8mZf2l}z0?u43wz+mX6Q?Rx4SvzsP48)&=VL|mW5l%wpJ zMHw@l;abmZluOSnTs{3z@!5`W^BiWV`y8furnB7BOlRq(&UDHSpPbvQYyR%iXF6p= zH~UW|CBnWEDKX<#BPFi*R;I+?flP_-*BsRjQ$fU}#TsQ^!i1GH)jh9XJd_n-A-TwjA~{W_MdG z{PwV7>BtwtQnJZX4NcOfsDv{^GhaMqCmvN!s=JMliFY?O-2Em?y4zN|8%}p?9n!0l z;6o99p0U#!v$+?_OiPG8uB+T^`G0SWHMqW?W31TvevYwZ*4%zjt2oaWSFc~6j z+u%e9Ub4t*JsnvHPX`&EJ}ExBy$UM$ zrcM?u{6sbU;?G6+pWR}Ff5S2v{>Ab#gx{Fq*EPbgS3&rHA5g;2K=_?a;g9%AhX08S z|F;7Of7Jm!{M841hd)ORzZt?m(AWrn{8AZytPKARhM!`DKca&0`>EltpNadQP2uN% zsonov#_-!P{757GMiqo#Lk<5KgnzRs{0uGpS7rEz_appm`}Od*@AnoyYo_1K>+j0Y z-T49_UjP37e#MJJp9wEMYGiovls4&o?jpSyPA?uaym)-SEDs^D5acnRyB`X7^CDdJ z;&FIU&*Vil&5IYL7p42)g?*pyg=3$eUTDqh;)`W!M}B7=Pq7s$oBpZ$6z_hXf%`8a z4ev^4Y4>0JP49-$yFUB$Cb926Q4<13#o&6K`2Q`~&K}Ac5_@blkKSqBwkuWl0^#102*bU>ntS!7d;5Qfdz*gOqi*x> z5fxnhpbv*5&BI}Qk?y*^IH}a!cUX0%%ymS4z?ltY74gXLiu2b020P4s;#Z3jx*Eoi$zfwRJ@c|$?OrElAX_=CF?2a@4H zl*xg3ZGL;)Svs(67aW+qOBX+9mrwDs>Uc{vQWHj2-e8(U*HqpljSQiYhYTYR?^3G{ z8LCwc^~Vo!|G6pDP5;r}|8a()MljS6J=B`l?DEpt_dYLsXs2Q_9wtj0n7Wag$%mxL zpLTLQx>FZ2cc=gJvi{bMI2+Ji)R5-7G10AV#Mk;%Sr3=FwI1#jx!q}r;@l=nYP)w^~;_1Yb}>R)z9)pfetMASOuC7Y(R)4pHnUE$dc=V7;hx~@Ki zFTG%#ZcfDZo{jpOFT(NWizis2YsW1y?!@vgaC39lem9^?~s$sx1smaf;wck#Xj+1S$S~-<*n%tmzS5z zFC2;Ga=qG*Lvr^1#btg>!j0XkB+ra@Me;mU-!wqdW|FgyGkN-MMe=mpswYpvR)5DQ z_TWFwUFe_9i&FMF7MK3^0JHc1d~5F&HT~Jz`+JQFxAvOW%UYf`4+69$%Y<4kLcVcX zjI~dwu6_|OS7)y|st(<(TuDx}t;0G=Xd=f;%TBlq@0zR2TpWl*nHRP$=Sy}6|Elh1 zen(`LX7!A$vgdu7Ro*RF|w`1oIX$*$Go%K(oZ)0-&cWq?z*sOMRx zVEv=6p|Xyq^0OmUIdltD_THkad}NEhoa_B%fV1|L#thK5<2bq!x^_Cv$|-4$qX>pefHx^+9;DynU`wNRUHzFWH0l5X8# zxOL;^3I_DDr(3p3F|+Y4?0+RqpN0FR0G7 zf^&zjF`Ns~oQspr-Am`%>&^wnY>N1I-`?y&q-2_*ijN0+yoEP!uc6!xN|btaOYLco$k_|H#Ts-dxM@MM{J1j z?-pKYzh5=BDMJ_=XEN4KGq(Rv8f!vh^>t%`p&R^|HcO8^zg{u(6wKUUGIR4g(#*@! zO#XV9S+QQ9ark+?rx^$5evTlQM)LuCnSYI%e)TR=?s{(*>0VX*s+KgJRT(oTqW@FN zknz$Kt^aeFGOnkLYPyWTtJg=EPHLXjYfID14LlS^-)$Jt^jd>o(Wg=s=mDOSk&Szq4&~ciSp4@^U|fqU_u8eOsDxp_Tcz zw5IEn7&Fb;Xw}Fw zFmkiW$j(XH`kyrN5RJqeM!K$bncO#e?_n;>TiV{uF1uU`Z0;GCFK*si<~9_#c$G^v zI{K(vSu*wu`d60fMs}0e$_05`Vo(ZGfyLK}0p*bIJd1I9@YXfze$MeCdUJzJTadIV z<{J+%daqi8=smMqkKVJZ&3pgr;#d|BTVd&-LuAFTEC@Fi20R}lv|P$73s?{atX8D8 z9*6gzU1dmHI#GN7*?vmvuo}{u>eA{pTkWMF_-GTi%eN;Zt?qN(Id7ba*KY!ijmI$w zZlb^S&oW=5<~m}4x?KL@xV?(}ZG{6gEhk2wuW)>C-dGy5N{P1%X(Ha{U1`MIhc+2+ z)%P*p>aIe(1+3DKN?x(bbX2mg+sorBde@GpPO<9XH!Bqfd&9w6CI_Rnx$dFt9|oqc zgo7`v)E!J-X>u@Jd(kPDO09|VMW=4gdhQK-+J(Mwug5xN-XH4M=r$P-efFD61=HVU zZ5eC1xWZNZ_RRS*_qmI8A~gGwQ=a~UJN2quS?;CBA@4`YRpK*fEYAN_HRAKfH?{LW zzcW6&=jwGpbgnTW7gI!((t*^jKrxe$q5Zea-p4@t>3u_x`Im|69ck`($Vsrbks^m+vtwgY`fgYDW9f0U5as&U6m`w|GjOZH|3Z4w@viy zuVsE7#na+ogv;EtJ>IoD+GU@4ZvV_WPoqgR>8G%DtcEaEBRzKEQg#3DE7<>w9dphZ zgfwmc?+&Kpl%+_=*OuxT?)9aziVqgy*EofZV>P^O9MeiuO>>oeIcE|6TB1nllPaWy zn56X5CXI*vMk(8uK+4J`x|CH*F8?fouT`JEs=rw;9PVe!(N#T`<6+vnq9mwXuhG{jR@41d&Qq+zHkNt4qRKeMdiTaM@26Nz+5cFn zTN>;>>}%eq*J33?(_a!1+Ws#iLRY^lBXrtUx!-0n_uDMir%`WM?8h{!xiC^7>WBYOARktyr$iMDZN;c*l%k%kei8}8`#pAgngvWy}7#?4(dA#CRdTjd<9>4UX?(wJ}E1Dnn z*rB(oX4}H-(esAcZ%0eB-K5zFnhi0`Uh`uG9eR&e3m2;Ue^apk*JSlU&Fa=Iw7PU5 ztY$6Lt53i`!YWY9luX3$%H;QdrT823~C;5F#kYS0M? z+F}a&b}i_BGU)LP`b8t?6z@UzSA#yDEQ0>xtPylcstme{3_6BEM;Spk@f5V@%oC`Z z8V6G`CR4SvY47(p(bT^0VQS;|dgj^meZ^V!aQ4)}e{_Md ze{?}5t-|OS)#%&LVgHB8C@zcS{*OEw?L(th!)Uh!l}%Sz4O6XN2dkS;8dl%ctTvNY zPtWK6?fJUZgY*5cdO6DF*!ha1!=Dw7Ha0oBc7${^ZJpdNMn}6Djwa0i-&fZxo2O{{ zc$mB?hX_)rNDw z95b9-|Dtp*QaTq)=bGux1va018FQEGjP>zN<~fd@8~vP1sO&bo=f73rpw>_k2dj=6 zaj-vG#zFWR#zD7ld0ymOCueY@aV1CdUU-#`(GT#wpX2-{*-X;K9h4f!=-asE9qPvI#)$^E-+yBzdVlh zJze*kmN7gnM!OfWep!mx*@K1HCyETQBebdRWw{jFCX45TvyAh>SxOOO4&gj^CG^Jo zxlp=pMTHBcmDvc5^s9Iy^thE}{%nNSwi%o?SB&zjQ>)E&<%9W`s9~o^4Qm#bd3if! zs7b>N`KyZFv}q4o>oS*pPrljrZfnat&5+sF+qS32HvC%2NCO6mj1+1&GSbXvWkyO_ z!HjhAD`ceNuk;DBBVU+C>bYtEmBtuC6aAZhC}+Issd zz51B)mDe%#Xf^1M5Ohyd(C3E9puhb|?q^}p@kY>HJq0bh8?Du#J0a+^hmD};X+bB+ zpsO?J3t#HpjnXf@bT|GVcV7Y@MX@}*9D#5qa!8OXNWdT<0TBX-B#__+Hxlk3N3uya z$&zDZb_3xMkN~oTXcPs+3jvkqiBAPQ{)(ak@>IO>soD?wRTCnc3Yy zeCp@#C!OuCsjjZ7uCA`W0$$lfPN_%0zqY7I_?O(@^f5WCMD7HZuc$x4Z(K>L=WpZ6 z34RwC9;4N+RWiQn@`QNR<zOND%oqYsZbmS4$ z8vOu&vYVjbi_6(*66g@ePlkm=f$<{w!CiK0(k=!S~QKj$IjfTTo zz+pV{;tyy@yJ?1sEoq7^*>PBF$=kVX{zF^Rmb9e7Ms+t$!;Rc-8t4%Tcsi0k+-*&$ z)qWbk2V6I85Xj7&0c5u4$W)@sB`*;&7i>T>^EasHkqb6(=aHLp1Q{JjX7OOL$`jKu z{&R?1lF4E5A6jJxQe~43+J5B?`mKj$!_GY{U|ok;Q#hRv<9^{Yf#{=Y6sSf=YO&4hFsO@EI^%3tA24kR{hv|Rd+Y9=X5tL=v``> zJNCm9l0$Pu-qR5BX3$$mv@JRB26e?bK4)nDz#aeacvWw@^#ewiuqA5<+s&_)u=#x^ zRN1guh{KiBV8bS{P$_J~=JWI_iNostogSZQtE}=35>?Cip@V>*`Lw#`Xg)iM8N`ks z-|GL9_}}(*A%jRQUXhy7dlOM)g+ce;MDkPB+(T*%(eBrH_jT?&bvk_*`fe~IYTI{f zGkth$D^GCyw*wO#Pal83!Tg0MbvNt41f*}5?~OP75+3G#kCMLbjUY+?%=gSk9Hr)i z#DV+8`#)0g{*Ny;dr+Lg?*Dk6+JpOvJ-A;rc$@EMdo6?uQ4KFa2yPSbNIx4725Fmr zEB|G__aL0#`I|!k15Ra$tg9Xi*7DHZRQQS(in@c~s~_aIQfZg=7s*?xxEog4rTuAW zM{Y8EcpzT8wEt~q~=;)qeK5*zOhQzY}o&yI<>~z8P%( zyM=Vo9`};_^zPN})4Mm|`6fp0(5C|kxOV%4etgQjk81ma-X>6g>PDsKo(+bOif025 zV!BuGr8VOI|8cng|5HsunbX<+|7VDV?p}iuTD(S;P}!Q`_6qu=VU$Qi3DVGuqv4ln z?EL%Fgoa2$LraZ@upnvBwB_qpCHwHz7BPd}9T4(j0IiBwg?WSd+X>p32|`T(p+30Z ztiKZT7e=qE|I9aQe{Fi~+JB2}unLD2i0A8T^NuNuO@TSF$8`ABJo04bMA?8#ebEMZ zU#Fu+C-uF=7*)_a{$c8uO?D3><0!+-qwkHqwG=Vzj|%Aaqlx2oP4bxl{D}0%e<5J9bfEw^jQhm z@l>_WzwP=k|H-Pyq)+bRG&_Q_*+#CvKnby`0(se# zK8QX)Htm>bcx?iyTTsn%V+7A!My=Ubawc?yavC%&#(E>298r0fo`4v zs9_B0f2Vl<&xYs!-q(cq$0T}^3kMNEZ7`ge*e>5I z&r99zhQ|yHE!l+I?Hx?DN>8gt0sCAn%jhp}kLXN03L4lFBsC2BqPSx*hI-XIn@u2ZSI&v!mZwlxw!ihHb47&%ayo{QG}26`DPsoqyj%RH(9o zoJXn9&ZAUN6|$G#9d!^rqX|(l@aKN|XEgk|kNz16f9|G#MkI!9tw{hzghSqTniqJtQHs0ERVnT%r&7ED1hPYu z7n4l{{JDkxi4xl^No=hmG4=vC$}5(W*Lp*Fsf`lkRVm1eU0dgyc@LFWD#|Mz<&|rr zlJlVC=w4O)pwYsewB&Bo7@V#4+seu2 zCtf!GuJ9Bts6oal7u2-BO_0s@c#uuuTbgXXuu<8(^8k^}sbwgeL(5dz99~9cGZa|A zUX#LFCI$Et*+g&Ww3RQRyx?!Ijiz_m1asS_B8%22yq0Q^)>l|r9n|fYb=yfcKhFuG zNxci{Bp-2|Wc^Zc{WA{NKX+<^$hv{8f8I~mKbMmA&!yV>=Ta(&Ntz{#{(JV9*KV)N zp{jO5JN556wQT3g#Pbi=;rWL*G@o$oFCTU)la6?JLZa>>dK zi*l((EfS5|v}e`Zo|_%@S+t9Z$un=lE!(B4L{vF$Q{-4SZ0=$~Aow=1@1Y%-m<(dMsI=j+$gX}8wFyN z>$rXYeUg*HX=Si5LjP;N82=lH@xPZe*~}Wj;(vD$*_6#E=SAmh=SAoLtw(70(!b7^dhB0yDygt} zf~2mx1|;>}c1==W$5Bbe-AN>MbS_Hjv$?9I_RsxWOKMNqUl*V`|5%*{XiDaY=ijf! z^Y7anqbc+)WS4uEt3#E+y zx9C3VD}VH$x*%95+oK+;3}$=OoXVhfYX#iZOZV2D47;RZ6geXkN_GdzJMDz)_H61- zyM^*jyB*q2yQ`|AP>Ksdij|%@W;gDov#oX?L+2$2RPgD?hEZ(e-Gm3RkwzQu$j1+f zAF12$I?>OL&abJdVdqRP_L(DzRqRPx8$ChfhgahjUWF|wBVEaqU;V1Cup-zZ3w>i| zo*BzB7|TAOO=pkSsywrl`{mhv(pM_ecCVmJn;ksUnEt=|WB~n7s@1lmL2NJCW2^jb zI|^5~lPy?v@m(R79M4wwwtwgu8^osKkv8vd$_;zFeC_7>?YEcO$bvcmv;B>8{ZVba zQW$YlqK}A3=zZc&M6xfDmLne?$D?Q{-rg_$%0Y8 z-AWpDI1d}OFHb$;_CcP>YYo0_cS|*2daso6$O3=tUXUkHmW-4&;3(_gk5ZOKDN7}k zjZ!HKxjyegZcvI4YyB(=YOUC;)q3klTINp2{*SSouEy~1oic`G* ztsCC|_NXS#W_{WHZwrYyuXd96vz^-e+0M(A`)`iXkt@JkDnUP-8syoYezQ3>VB_G| zizL;!R<^q3_j3Y}&Xj3ax^&Q4TlM;z*=)CP&s>3jySoDa7CfTy??@lYzb|ef{A-Yl z{QEga<=@F1{+w`a?;@sMCUcLqJmH;4{G&W!MaoXRS?dmGhtOasc3XV ztYt)Vxe7#>Y>x2LVv4Y%itz4L`v|3ovpK=pMn`rKGfzCkK(4VMTn(iYPgJ^4a-l^i zsviD14gZ`U1*5`!YDr#XaHf7uyYedh+P)t!6MR{wV{Y4tz{xzER; z-RI-@C$@T4meA_&J3*`Odsu7rbyw0>pP55iosvb~YsykvJv{55*y_qm@&4D2c>n8q zt<_U{vio0Yt7m4C_c=4Q_c=5F`c}hmzn8We{%sy*E04mUQXBZAH~!I)gqae(yUK?B zJ40w~J#6i^hqTr%jHaz^T}oQJGK0LYn4!I|m_b|Hn?z(u=m)TSv6^r^=#CYKH;+ty?}U1)9V4$#^?4{EJ_^a|SA?z2g2pSELb@3*V1eZcfPzs>aFQ&tGA{9#jQ@|TTL1qPTCs5bh$U^@|*B?loCG04IS4; zl?`hsw!B+=X!+;&Yc1d3owod%64LT-(y-M-D?Ivy33fJ1X{ zs_%pk5rhTd`qs z_3XCL>OS{stuDEow%U6$X?5{5Y<2oHwbk}%e{rk-QFp=(nJRF*NgLqyYil%ax9>{1 z-JytZyUkSc-rZD{+YP4v|9K}IcL5{4PO9ro0;3ML0!FP~tud;m3uV;FLc*wXQ;<HkD9-G2HQ3CpA_CyJd+fB{X&+6Op%N05TE2jtq4o3oiy+eVr{>Q4(u)itSD^?y@UCHg5fxHt5&E2SvLubI_#v+VJw zznL|3jp4F~{>`jbYijvs)(7blQ%PQWBS98=YMAXv|bt?|_sSXr%Aw~VeL`40`L>2X?6U`r_6K}ro?ak(w7jG$5 zWR7I4o+yy-bLdFb@j5MB%h8h*VMn!s~_iujTvuxOvV%<}) z?l7+INLF_qt-B$q`{V?5HR99+v(<<>LEDmG?y3_ux%E&yXwe#MJlgj?WgK25^gwHEQhjho*`xISg12veOBnQ zhTJZF_S^DmqtTSD@_aZ>(6gVLf}TBgyQXKmBB-7<$RT?6q|_?Y?nAEaf2? ztyV~Mb@k{w!2<;nP;+OMkcFmN=Oc_O;5V3<-5EL zLPWKe(+e2}ZQ5oN+BB&lw5d7QrV}hIl$%c4bf*p5w9uxuX^}0sHsz*VjGRo{bjWVd zrc*ZvZCcy_+7x@6)}|jWp>4W5jkM|A8?a3;-=MbXl^cR<)7@i&X_L+y$P&r>wmy(| z?=p?NEJoh1Gw66k$m@NBI=Aj~Lon}?UWnGWo^Qa-w?>Qh56L~YR3mp^IJ1A0+=oUZ zxfP>TZr(mRIBsq&3Z(UP-jXQNv#B1?a}!6;3`Wn^>4csxgq{`}JuOFzcZpN1HA&+A z*NC+thqXJynohBPo`hI;C8=0*Ub(0q4zX>b z$`>t%zep^Ss?q!#!LN%p1n!PH#Xe}*J=B7SPn0Tp3sod6PO+A!G$a+oHRrH*M(o`T z;3#f@L6Q?PS}o7n z^nzr-OpxS*KrD)@N7`*5%W*b|MI5N1ILjADU_;6mcdQ@GxusMqPERI8MI`aJ_Z3+@ zk^I$tMbhn-Z_g%P8~_kYjh`P@a^s!4P9{_hmhwN56I{dtMn{yg}T9c0IqA*;6| zdCnmhLnGuXp)saOyah+Sa+3*!QXF3?nVxT&2?O!NO{l=E) zqj}S(h(PgSTlbOsTGdI`-+f#7eWMNIqxx;(#iN6bkN(}8ygNI{CO?#2izZ+EcJLH? zbr9RZue4VO-VT1x7*7A{^wRhzrkfKfY^z^P00Uel8i2IqAV{;Rq{YqfLz;~vt=)`^ zC#`l|TbdRm*QTWfB8^G-+ak$4jq}vbw(v6ckk1l zc*b{ChS~M|r}!rDf- zf?f)Lz7>2CLCc3FXo1V)zWj;iirzYb4_`umooH;y{&Y!dgGyeYLB)x7)&(}E%Aeb?{l$vRW))&B>NLpS3p!?fIzs z&jyK6e!9RgN+|0EnWOv1PXO&^}taVwXyKR93~K3~xm9lVoOoVn`^5 zI8_B?W6)u0c6`UL1yQ`lJ6*|8UiH+)@4-~ZXL2Ko>B3jw@mbyn6kvR&FKehH?yK?U zn}MEHUer*bE8}Z1KJy2M_R?($7*9EZ3fh=#W;slE-}}5n0j)NE9h+Irt=+3WK8-It z#jnl#9=bsvftczFR{2w@@{=8s;bezogt8TMKD!)b4~%Sq&nFguSMaH_$#VkR)EPmx z8iuppQeyLK6nkNu!~}!di~8AklNm(ZMp=Iyt=og}X@;J01%QwS#_AeT# z-yrZxhCXlq6n$Ppc{f+tSRQqv0qRRe1zbnIkW-~Yh6Z`6^pc^qdGu#hcF@!Yw7eIVD(&7x*bA?&=7$r#Be*6A*VL@c)LYukS4*}pXQ&>i8?O((lCk0^4Ew7& z`VU{q)93XN^SDQEUYVxTr{8YUBS=JZy;dOlu(VCi_v}z9V?9ru@336_b4~z*Q@eS` zhMZt76PuqCw3VrsC=q;cgp@H{VIEzv9n@P`@EP?j)b~fWw%`-mfG+r?4krsfm-NO3 zpPF9kc2rAmqg}BV_^6iIlj$3J2}C}87>JzA5m{cJ68YS9gvj~5kjR-Tks*1#{s&K{ zYl>&PLE?G&c)fP6$<-IfDT{SJA?x#1osYM z%L%n{Nm8a7G(&oaa9X-4jQS2?Y&zFyao<_A;X8zn^{)=*9m0cYx?LQ6hctYL@X565 zIvf0gc!zM9pLGlKk)v^1Ek+I^s*=Q7HtRxS)9FdU$EF7))n#nDO%y(R6$(>%wimdw zK(TO_$`=-Yr_i|hZ?mJ%Sd&&TZ;}cJ8ho zs-0WagI|4bWApyZ;y%EEYoluR#iNR!)*TBw$v>rz#;`{+xh`M7>zz1E%5LXRg50wl z%#lB`X@T*VoC(0U2*3i-p(QNw4cx~PfazWR9{uA&WX+B?uE#%WK=(&zF|{K7jW+|e zgMy`&uq7W7NckCr{oG-}u%-JfH2l$(RncB!_%W@Ph(`22Nm(X`WtoUa6|B;gIAzyH z0a^Dl5<}td3PKtvEjtzH`}E4{nyR_%NFQnY(djicH8kVsIQ)~KPWFO$4iUu}5Jh%s z_^QCWIvRsFPnO%F!VLi4xk>Al>RH56mAs?BRMWE!fzA%9M$^WOX4qC>r|${*)WYvK z3Qo+1ZbHAg6}q2q&(Cx|DYT354x#NOkOwhqdmR{<*fo{T=qUS$0sas?J~qFP~B zA$;@<0LR3pxEQ`YI5%VmV}9@r?E$8#HEXXj(my(ukEsS+Q8?3sBbX25o>w{uA+Cef zdkCH2%~$Z@+?TpzI9DOSSAK<`aBks1sW(m^8Z4^kUKa zk-CM-x!2e79Bq>%i7CEbQpPT8uv}97fkc)i_1&oAoUAi|KHFjk^04P1u9 zT2msa6$o3h&BE@keww-?vbTnSNAFTeXKq^~_KU<8j^>b4he#_rSl*wL!KWj3399Rj zhAGnbz(kDU@8ZYz2%)`Xi0_Pub~TKmE`$-Di4+fU1`_BdmjM0%9o@g{AlTfE;C%U} zOww%b*N3Rhec>u%b6@R%HuuR6>gfJd2S1~GQHqJ#kk^0CbP?8nx(e$*Uvx1v7+TzP z$`Hd@`(k<4erJFEP;z1~mChK&{p`y9-Q z?~d^rVKW(FRlNvdQG~Eo?bVYot=n@aVUW7>-f#TfgE3xFFWQm5j1a2739Aq1sz3V; ztv;Vt|3U;-zcE7XNSh)|57$?UsB7K>)Gv(FP(R5~UqVsOB&bt0)KiR5V?)Y@C5osw zA?lkr)L9Jm)+-6>E(CQ86?NN|5k`mBgWP41tnM;M@w4zUHUZ{ERuW1{Aa~BQRhZVX zej_j9H|mni*MowNyihUc^MQ7nb3V!Y zF*4Hotro#Z*k3m6zP19{U+e|4E5~SL|KFFC>>r758dB00$$;y6#ZxYFpXHnM<5=mWG zYwa3f)GmrTQbgSaQGb%Gp)O>o`%%=tw?@>y)+*|wtxZvX(ptR#XAhvB%c1VcP=DH; zpnis+en3P0pb=_?L${8R)c0U_DWhgcb+8jXZKC~tviHqonDhA$$USA@Ztma$a=II( zn;l(j(dO76w-Olg+-_iq)21{rHEp2M; z`;bJP**W9e-jkZ0I=H9tf1+e}gp222-^TU+(Hg%(f1vzIznt*P8IJs#9Io;!HTPuGhHv+{cugr^p;Lq<2f? z=4CBaZgyyCDyYUH>Sc)fS`M|Fpf;|#)Q3A0)He{+gEiDcj8H2hT8m+VSI}0hD+}wo zjH~M*R@W?A*YW09*VoO}y1r>{+KhLai}ybw>emuA)KeMi4ixn!g8D8E^(rION;3xb z7n)Hh*7d;aP}g{_t`Jt&Go46XR#MkMt*$}l&FChg9)YOOCTOT%|B#}dKvAD>M*P8M znm^dgPcxoTr|*WY{I+J|{XaW!zYACQ78cyu-;vaPC#id(T6f5zX8v7y+CsZnOFGta zDObxcAJAHgX)QxZEj_gsUg@ueeZnP|ir8CejU%)R#|GX0gHPgd)k3?N;>uR4Bha{N z8ruc)WK*FL_q~Si?~K$MVP%bYssm}nvZmMwPg6B|Py&BqCC$mj6kq|5$Lp@%>8^%kI+bbWgjK6DJAh#6D09a6P3inO=>}+UYQPe9t)-9wWs_ECHx81_!Ao3)h2mOs{f43Z@#ey>jpY zwsGoo-jZI`!e8vSu}HuyB%n7(z~28*0+wG&{h<)z4~3}g-WPHq&ssekBGhWZTDK0< zYAs^5wxYGpBegn1RJ%7bgbVC8WmTI*c0cpkF4e{Rro`xnoWwYPjH+%ZzW)VIude|| z@~3+#$!To~$@?23$!|AQ8MUjS{{X&Go$JIpKi`h=?`yR>OIe*&ZAhISQfG!%XJ*3- z5zYXy)=aFmJy&Z-R%;2ZwI!+bj|OV3=Nkl3>&2gzINi@YSgfry?dxY89G3S@E^p>f z`289PEJ@i0ENL`UW625@Ey!q1SkkNkvgEh=DocK^AH)g?Q^UexdexlSPP9#Qjq3R; z{Lx%dU!eK^tw8g*A>81#htm8s@ts0)>Lbk)HJT^Y52&!lLag0IkEiPiWd8aBkom-5 zjm*z?Q!*P%?liaMkW)QH*)C zS`Vo$r-7P^ z{u(v?7&S+m5^7ozYAh->A@$(T?6Ao@$N)r4a+T#D5LkZ45@cZcVN0zx-qHO*{2mnF z@apqAEvmXylFB>M+|ZZA%gs0MN~!p=CPw~RLtga&>X-BSsu$HW`f^>AUe!P4Nnn2B zhw5sjE+mzzoZ1nhQg>qOf$c<+tqErnaIZSK8X>Dc9ns>DwN_-UcJ7=JaceI<5&ze< z+Q2QvQ$zG^CUPXKY*@irLAC#Q0#y6oeKplCc#W!d%TS`)*PlhzzUHi|+5^whK{*_2 zP+wLOo>fwvd`8)GosB3{oUR|w2n6LJL0vh5zGsutTN)FB-adl_J%2_e=!G*~{}=O~ z>abwuJ^RCgoA<=@)y<6yLL_arosn$lUOof&KyTX@w(aMzZ8g5J(nQ6#=!$9+&>o=@ z=v!CnNXcj+!Swq>A|p9sJH^1E>NJT|L}0%q*WbtgB6Qs(3>s$l;sV)huKPv<(seuhf?c=CFI`zY zsYTOYx+?LcJ)RcvVXu80_S&yuubqpp`M~HFd}2|4_ciQhYHc;rww@A5dm2Msd#}_; zJHV!oS5VR_PZ59OluFvdQ?(@x$E)U|C?9Vw$$HPp^SqnURV1>9)ZR*_iC zXsl%-S4+x^w3cVZ4K`{JMcrLQjmJNIzej7R7qGecB#OG`XGHzqpHd6}FR72E$2l(D%m2YFv?1L+`Dj#99?mo22Mx@GLPNZyrG`GdP1n( zinWjDYQJ?Gt^JuA^ngOvp1|6dX|>;W!oQvw*P1HU`XL70zV4yb+KtsZnAX~#)EcSP z+BHzEtp9q$qz;{XiI~AQ%pYo3T{pvHuxD+89v+I`Hiky%Lgl z#cz@)u}pSdKKY3o|4c%ixW-c+(qL;>OIa+QiiOTGRJh0DL?bokc@;Dl$}iX5bHU16 zR&{-uj)5=J;sd_RL&z3ja-BRqwFhNi9&#nCf^c~Ra&VX_m%AZMXMm|RXR}`(Q@!lMGnL(s zarV5N^?A4ss!^0KYoqMqZRjLrJS+Et6CwW;gK@f9u>N$j+WXVaDv2-4<%!1+?Il)b z9~WGz8OMZ?VCxnb2|kL{MuPX*^tSJBWF&|^h9g0bW2&2Q#W7P4q_v28DxxmsP;X+W zKR!!P|Mw`O{_?1b`m3X+sCOR~P+JgnCl2)-hI$S~y@8;v)KK4Pj`|i6^&=Qqd!vhn z`bLJjGetd_pib0Kk1|57F#q080Srg&?iqTgiF!t(zR@(B-ZQkRNzey0j-HUzXMaaY zeSSD6L9CX8P>DZE5O2!HwA*Dm6R_`y^wIF}e@X{`1lt=-2cHjs|C3i5{`43@tHwdv zw4;swM-Nl{=}is`w#i3=ZZgyK?@vjpKR}Kqb(1^c8<%jMLp~IbuUdkExXkK344!qn z)91PKt%F-(2!oIdW#@i9Z*siVSNXHtA3OTDTvqaeCd$VKVyM8aecak65LD8!>Kb%C zh9k#^fWrZtCiekATdJ<4B>KE37`0@nvK)){GUk5c%ZI|MSBy?02R&BC%xL~SaS*eI+)!*)3_>yzkFt) z-^*v%n=GYhKHO%V>?Y++mL%^E@lF}v;il5tEb=P&!|~KyiJv_|KdUNZ&$I;D+rE>d zvLpWpmWhA$%_xJ98v17Xn^D4#2J4&Ao(Wt?ZYr`x-G`gS&%u00B!^ksulvq?Yc9VYX3u58%xuLL7D;|?%lZCYzaY~@C3#EPnj5!>?>8nJC(sqxb7UzvnrQO@P^ zg4*NXOBqeRl;@uW?z_ zF|%g=c1}+jFBfG#eJUU;4^)}eanCtDEBQJmo(i^(c_+;3=x0zz01nUi$qd(m##;QF{|-`Wi1o)7Jk|$K z$yhrwthtD_2(Xswl|qkowSYAhC>OE5eb#^fGv;fYfVF;88Ees5|9my3^;mNr>ytmq z9Jnk1t;W*2TflnyOBw5%XAGnz_K$}@G3?XEa*Pt{n{-gFZ^9Y>ax~`nd(C*PaffBB z)u;V)+?du<0c+E08SCcLI$90vnjUYYfcFhu4@o(#>&-#o-QASO`^7IZ-m?ZF6iJp# zPb;!Kr7PRhxE6NsT7j0Ubb6KltAB|YV?EM@#~SggjP=q0SdIBsDPXPtos4zIFM$Uf zJ=P%t){e(ztTTVP5Zm%ys4%{S$hM`;FBigr+XbvCjbyB^p7PHDW2~_P*2f#mSaSnl zHRkK##yr+29ba3W^3PYRA=Z@w*6QD7tgoCr&)pszT-=LJC_hfZeTX0Ov+*O+u6r%s zRqW?+R2J{D(|FfSW;^fW*;M=^ba+soe>h*0Wd1hllj@eS&yNtms&%%YA4WA^PX5^c z4PNv*YjzVPy}|1TUq>)J@!mK2D1q?}UZ;K~fb8CH;{~Gfkat&;AWqoQ_2e^gMukMx zRGuXhqdpq}(&|kNwUsxpRUNW89y<8 zg329LpEj&gq_*Q-MW1e8WvEZ##`+XttWS|NJ6g()rr9x4b_~sqm9k@LcAS(Qhq_9d z6+bP0y4W&RA9vrSwCu=#NXt;MKqt4{#dK1ycM&G~7-^u7m>-S#(fa%t%#YFM$6|i0 zK0glgdad`X;maN(P~q!MH9U(#oPEet!$(kv z?<5FL!y_rgyAlMa;nfe){wP6k8lFyZY?L74G>Gdc#2pfZRfAYdYndZKq-YScD1<|T zNDZnV4XISLZS#9dKk88__M-?8YPL(c*6XZNc7K|kV%!;1 zFQhYexIQRDA@#=e3mP5N-fPesBTaf^v{7$Fx?+Uxn64G{#{#RK zQqr$eO!{@IdB1+4g7xcvTTJ@(stUeee|kUB&<8wz`}NcJ5vV8bH|^KAQ#{Qi2(Dk} zQ;2>NM6}cwg7H}PtyDyR)lW*#cx9#7Ga`{SYlHV$5MJLkXl|Q}b!57z1mE&s#gS>Y zlBsDt=g8#xSJik~Rddz+HiD|=25@k`SrJH0URlAb$usMS@Sb1iS52BxsN2oe$uO%W(Dl$+OKl2pnP!2E2 zcFPscbRyGbP?ok0u+H*vI!Ve;*;$nX0DAm{^B)8LZMP}-|LQXLVP>cp=Q;_T8?~BnZjBM=v@SAW4T0*rM(So4F$Y|>@KT za7Ba7{Cz+9l#b)M&Cr7nm3mGnWG}zCnI8(IkxEZK(4%hY^nK%Fezy(z-{-(GN|bKI zebt5&#EELhNC}|kPcLKQds=XZL2OSRHxk=|WxUw#xSI&%V4+{J-Et3sD!s?lIwnyF zy9B{m#~UcbLJ5L%j(bvwED3^hjt{LO5cMSp&N+UawkM>_)H!~LLhQWT)H$9-B0VVg`O77W3jOvUUZp!6QB?ZHCACs%BY&ZdG!(=_?#ZxA zbiTBbAEYx)k;}d`%^eV&E3MQRNAuI=d}|;d8@u6}GKGgP?B%=RIvWPE6`3oP2>$p}i~1tLUlP^M@5B6WVUi#I*%4=JW+^`eU~J>q;_ znyW;bSe|If@+kQ+lIc9f+q#1a3kq^Y@Cm_~q-hHk z-hTKNZ<5|TD43*c7S_%r;dc?dN#e{%ByW%eQx$DskZ4H^Z<2(PSl%ctl8ua2D#7}# zyjkMRn2k3|>YQM!Eh>e#NmlKH3CMHd1Ho4HyM@^*t8A+tJ7Q$3>fOTIsuks=$9(DV zYpY6E5-9gdQ(KilA=XF`oUOW?Ld=&SI9p|*5OxWIvsK?vYDY^DoUM9k1%Y_OX=x#9M+(0#GCUJJ)ZjlZzJY^t=NbS^Xl9^p}DeiLUTJx20}0=a=1L&&^3t(rUUPt zt2F%9=lKrY;A^1+Z<<>-K1Dq1*m4KP{EmG0%@sR*tbuJtB2<(lMA!C z;q~7d^)KffzJFc0oM_v;48Q#=!b_l9drkXSHpO##rqsVKV)nSBR1xVnFY>y)Ru?;|Y_F0z=tW-hyk7{KcXDhPH%4+x@G@EHJd{Yoo6EmWS`E zvlbJz+%?T_SIsITP-#-Qh3l#@6v8S&a9y=Og~*m5xUL#bA#RW$xURbURsw-f37K@& z&9pr~7MXU{Pzupes)g&S8MKx=q*}Q7TQdib zrX}`sBh#|Dh&L@A7LYEGpX%4NG+9WX8ZI<7Eg#)NAlwoJXIh@4HSLu`A)INslR|uw zFPWALShIV;rRdd1!a{^&pVB$!x&Bw76Pg=!v2N!(g-Vkw!dgbFeL`2+Sor5gEHFPx zKb)F-sZ9%&B`taATl=YzetuEF>*u_AL?`|;(XW0w=M$)``KJ1L4TVUaZ>pbzC`8_T zQ~hj9AyOm=Zs>cTQrlXZ6mdh}Q}YaQa6_MWF7;C9nGSsw6vurM1UK~MQHVtnL~t91 zrsOM%w&qQ~@AOo+;SI|F%Lg`!#duunEsbm9PQDZwwfVpn!~oViPhr-~oqVsH{~w`O zhUEQq16aH~fN?#!x4{6GB!?n|aY`7$baBU2^KojNli7hC@;Fubp^+W9+{xPk_iUok zZ;thA2kfN;YFeqO9q2kK{N(q9SLw)6;L!JZ?+#G6|Ls7Do?<-xR*b&UuDw-?rtfIN0hCQOWp<5Pi zIIgrY99JTwaYcx-=!TU@lVK%VnlA+z^G0?}&QjPlUD?XpePouOon7V9#x5;C)MWo( zbA$bV>V83e2!-w!)Q>E5%d2i`RG!>X*r>{lYKf7;R#k3fNsJaYtE!_K-3^;A;1Wih zwHbkSC(g>?O~f_DM3pb~zlk-BLjCSGH4*>4nLvCeL2xEwFNLU(7LG4ujCszk=tR$l zl`iydBo*$(J{=D*5s@R?SJf$SsQ1=?c-F^@<`NOJ|BqWNtB11aUJfUm|X2 z6yhEUf^$4tP>2N*1m}2E(e@0NDCHcFU3s*Y z8%!OKM<_&n2?ytRETy#^mFVCc4?D&2kp#hQz8Ov-wo4GX8bl`wu~C93(jX>MI_{7l zN`rE5hEG#;_v?Qv9j({2ppHZeeJaw>lZiI;WMT|GnOH+lCeF~4k>|~_BO^?`( zD8j@Y>S*K+DJOvBiFQ0Y0VHqANs?V2>HJ=>^9|Z zIuNc%RfsLRN743Ksej=>cu8s?6*)ecSCM~b6TwZ&^{XN)DbzK&rYbU%LbNwm5i5l_ zmviwd(tEPP^>-doR3u`uUPTN}PqOU#21=qG3W}Dp8(plL%$uYT-2M^aJY&Wr={zHM z{HMO5qA2Hrbef`^3kq6QswW0}-NDBbd6l^(lL&HfwqKQ*OrahbZmKfZP>8b*No6i# zvr?~#3ZLU2Qxv1!M5D2t1Uw}rU?r7=9Tz9?4HFbXwm-^3a}`)4i2Sw*W(UeEu;yCb zWiWC)t!9B=@pT%{i*H8;VSA5Ezv5d*p_*k%;=7o0NaGZq40v1-&f;;kG3*y9l(s4X z&;3g=*7e5K+E|;5#y_8N{FWrBU_7WF1`m1Dxmh%f6FTKpH z=gO$<#C-M%z0q)O10nVk^Qowowx_3_{btlg7 z%eM^_s^<(-zAd5` zW^_tA-29YuATE42TB$N)D_>>QeF{(B7;Sh*iG0_Ox~to8v-d?`xp22i=dp6d_%9m$ zryGC&=geoTM74d8H(@`lq8@*_4J1+CHf1ywnpkCR#HH4FO_J)*2&XMdRw;7~8#ba( zizLxxY8M93z3m293`pz2fi}InlbC&&X14A-Hi@?c*QSylJnbgGmf%tf)jZzR68tim zKyUg4^bP@_I!TO1(;7{pR|*4w{H}49S-XMMzOiF;c09HR-kqd1t%Hb|5uU8ARmq zr%~$q)pErud@-u-Vlhc_LWgDBJfV{dT~6CJ?9vlT16Hn?g8;nRdW2w5FdW92XMy`E{fs;4(EbcxYsu zNA6WEzsON)luGW$YHYCo$hwP!o9Lstm0mhBm}o)TNM2DY#}e@k^1sz{CWX3mV%<$t z_K#4w_x2Ntu-_a}yPGR7Qh*ax5=JT{bdMiV>nkeF1(F{h=%T*S@w_mmQ#NlM?^hUu zDAa@FO?z!y3USAH(_VWjg+R=cAh==k0}7ELL2$$7GZbRH1i=lP6%=B)1i=lPc@(0j z1i=lPNfe@^1i`IZUO^$6OA!9{-JU0E_z}*RhbubZT8cW5h;z{AhjV&(oUV|S9jq6& zwRPFy>L&Y^H$XU5BQ=0}I|#^m4kS_ndHrC00&|XoiPph1ATVb*m>3;QLjrT0gNfC_ zgb%0-dKo-u(fG`@YvH zlKG^J`MX<_EoF!5K`(86ofKnL4$33QzM}vn^i#fc$I<$%IMTSd0V|F+E^f$*V~mSK zSaGazaU)h7XIu=d-XnMjTyZ`2X^K&q+U8WDRPZ4lyf%;zG5=cL8qKzme)Yc1uQeJ= zp|;petx;bJagPMSS)&#dVu1v4AwI;5Llv!gO?q`Smg{zD6 zh6ZzE+{B^0nxu>-!fSWAftr{{oQ@7L+nB!X9-~Opi$nN2zp&C_BiXNNZcU*D68`ckg*!jT27iNJ5TTU0u}T$ZZ{kztQEROjA?hCRm6 zun&*|AT&4jVi!T$stt1A#Vg?_^VJAqG_>lcYeXfKP7djfKRr35H~#cQtkGG**rZyV zC0soq(2Cfm1x89bV*szDJrjsx{L<5}k~W}F$q98g(fsUcb^rb*UX7kn&EgAJ*G7#l zQVFaoIqz-cm0;goK?%BET^l7Z7oN926W(`bQ_!6L!pJ&;2;kF1zhb&6o7Wh1ZPW5(^}?85Zn~>V+!GrAh;>$mN)`2u9N8$)JJjjm2hxV&_W6k zDM4^kP#cA4Awh5(XRo9XzeAc}&DATNqh^lr5ht?H8> zBPzbS6Vx=~Vq`hma*aIuyjuI)wkX7=cr6{ss;ZqGQe7d29542-etuZc` z0ClIn3X8A4Tbr?hPv5Yw@pQG0t_oN8z#8n0?P#<&R*784$@K!g(T4NS^5ova+*7L{ z#%R+l+n=)0V1*pOM0qK-rmau>c}V>Eh5B>p zX-}hFjV#z-mVO9-*2h0jndXlN0sEpJz=0#={}4$6Pi*A?>`u)H{$*Aep?vb^3TFQktMEK|H?w@$la8(yUt{!MP-IBo(I+ij+oxqA{q$XD z-*M0Rnq3J|(a41Igs2p6LR51|CqNnwX)2^`Ae{zj1i*E)FeIPlApQjG3s{;f2$?=BI@G9SyL?~IIgfAG z{L%{u0{k}&Iioeh5ecy)fRqe&Mu+nk#&BbxJ2uI{n zYXwXNl9%qG?_Gr@eqgJBwk0n;O38uj zvYdBp71O|Y0L)7)=Odf9O;pW3d};g=YdK*~15Cj(j0^?rp2qN_W4yN!z8@9}e@DcF zRmVrC@kg6@Z^I~jHVmP{iMgK!#slb$Matu_4OSa@kQ-l0+m7=~3LpLf&yN8|KRpo=C1~W3l*NSbJW5BKhTERN9x^OuG0R8TdrW*zUD*nve}m&@<>}G zc@3VDEW8F^)reMMqrPP{d8Q8~5sQ+DLrGZijWzAzI{#110l#2GO7gyE^S+hneLor6 z+F5&O?rn)%suG$)+Rf0>n^*(trn}`Pkdy)y$v`L zm*D*k8fx>Nwt0^;etdw;=!DFOFk?nAugony9kyf>G1WC;OYS3ooG$IRXt*V8$tnvD z*A9zIJGluYtyi;zX8%~W3Bamw|YOb^?cttxO#r? zve%Ul3G@(4$;YQyztR0mhn!K+0#Cek@ELak5|aqTxUL{%8xjL`wh3p%K#j95LyEp& zb!1n{_-j0)Y!#zGdZ!m8BKibCA5Pn9^_~se8d@XjNIumv9(aEaD&gSBE3 zsl$qOBtac1J$UwnRoomNx2T%&XAkhFc_^#FT6PTn|GmaTsxT4+Nb>0ufU;r+c?Laf z>0^+I;~fm*!%893H+3bBd0|U#rTyXY!O$Nj+fny-`VN4_*wrwK^r?y#9#rq`F$Au~ zt}`_?FYO~PeE99QxLrPcec zuc`LsGh~$|4vbG~1oqOlWa0rH@l+USe~Pd97&1$$&>z#yFnl}bsc3QmjjcQv+{oz! z&GpvuUbevwOUX4=O_V>c#K#?9OFLhaRDKH>%Wr({x%kQ?9&tY4YfNw*_q{*$d<}jA z)kDQ^@@pAJ;l%S+?dx#n{5!=+}@lzGr4)LBy`O=ExYv zpIvcnz=f*W6&H?)m5}$gui-)jO=Qy{-!-=(9$g$VescUIl{4USUmKO=J>%Ovmw8Lq zf|Vu&q{2+8n*4rc8vceq+{;nVJh9}*>bc}cuf%d!^ThI;(D-s!!^HAIr0ffDZxX$} z1n(j5u>|jFRIb^mkL6R5*C zeEIbaA&p8XcZ8Q0w23eKqb5G=?zcgRVRt7)g?T~}mz?%=r{gSmaxhgq?wbg%6l5oa zB|_5|&5sJRgeB%jwMhVBM1xd%BVZ%~lJ){wOP$C}u-sID@iXGnh#tZ?4f5KL6^wy;RlAs~6F}S9Y$xr#JjFyK&jMdLGQ6Dm@7KCjr3NJfS4>P)!Kt=&(#O?cu<+Z@PDY!rj z{o3bRKs@Cn_A{OQMEcRYfm~o`!>D-g=eCNFm-`a4_WS@6fA|TKSJ94-@IAW@PZOFwGK`M_aGamMj%uiTta-k(x|pXW`|Aw zZ+2Mp_wfJM@c$?9|GV)2TRv|o#Q-|dne^mD3()}N=Pk&xifcw(O`W0{Fk3s?h}P|^ zC)OMqw&X`5u3JKB&CkvVRCAbC^Cr?q5<}IR8_=3R?oVs}E{CZD)_ew(l&r~545w@I ziQ!dlEck{N;7U9eou@^qiUOp~XZp|>e~q0ls>Ai1Iy~8jsKX2Wi8>tUuUCiGw;QX2 z?`s`gV(~bjbCSV>{JKdQ5`L<3CzY2(pc~cATHeSu zII^T^)nxjYXNL__4)u;{4p4ES z=w|y)1^yu1JEjf)K_wYsF=}C<|IM_|viHrm7v}UGQ{owuo$M%Z70*d5E_M}L@|_vk zu0l_yt1#P{bCuh5RX@w9%#q|*d{NOTD2mU@8s*G)SaLjheKPHZnT~w&rz;P?O)Yj5 zW;u%CLrdy7^5;lbL6OVt^f=UvVu#)1NO5L*N{ZFYQO?4w@g93&mc2M@l&iSF?%_)& zIo&1pd?~-!AtM=E;(%t9dK`srr>juNo#ZIy^TsL2EO7#_<-9_BfdH3pFDcB-<#XJ*u9EyLOlAw&B}GN9Vvjqmq}b_(f{YS3 zLFk@Sn8|*+W)~{>p=Mx~C(Sdb$dTr_8F=iLbDi!qJLF~AJ$9&}s02`XK!62lg^t;V z`S4FxTE4Ro@n>Oug{~|IB@RAG16HS%7UV01Ma8btIcb@cVNy<7wzJUb&NV1tt>Lm6 zHA05r_n6N<&ju|f%mH30KUE3vOi&6H`itzITse=!aWg6$!KCCMPn5qs4!4Ks5B$$7 z5Ei0X(z2ZHqI~-tMMk-HcWxR{975MDdwz)nrG#pu)(*UP7Un3q9+#4V^>}izdcG=} z0~9(v@N1U6*lCBBBK8cIE8k%+lyWn3ouF!@W~n60RYEnI&s2%wGZ`P{EMwpjXQ5}{ z04@`>(3LIYD5Qje#o;r7Bl=d5+&LbHl%*pdGv&H5ufRShLx$5)j+vg4A{|238k9v= zUZ1O(6teQtGMt_Qdr?{@F^Fl|#jXOq389bMXS#DQJai#;5JReNHVNsE@G@~TjxNJ6{Gx*;n zg>uepdoe|!^gp8Cj%<5LzK7-)+nH@FrCMswB;$d;FyHA0?Y0*KkXkxB&0d_T{csl8 za~#@FsLxg6k$(fDJ-IxuXc@7s{4cJM;}ZSt0_Ed>=FiDtDuNlRS;=w~p-q#sMKaZF zw{xz8+7>~d9kU#Y3CJvVft}8BCcrBugCyl2aneof8kn2FKh`LQ^xs5(_+h;kj4F1k`6wgwM)Lw()AbBWlK%-H` zGB`r1ks#lxHpI=B@Hzr=bT|riU4hFkOe=7~IHG81i6=W2Da}N$52T!i9fFk1B0ij? zY?Kio7ll$jmw?jFC}-z7oH@B3IoAXS)a=Y4qp)foxzgFrERh&zA&hj+EHugbFY;Zk zypke52lUPZO3h~}iq0FR(tiEKK2*v#O<(HFQVSTjY1NF6Y9ZGW@(nnx_Dt}}!GvVM zziwiun2%2Wa#Loorb9wYvSyaJJq6f*sq2q!wN!wgI=asHU5hE z6j%VWkJ;3XQ-8S`aT%^swFLjhM&-H+Tse+HhpWU*^1&~3;Oqg+h6DaBuove^1w@_5 zXv*gzAY{)GbCufE47Vd6CjvS^mJfXZl?Y25`T2_ZCZy*yY3*v-s!x!h2c<+SI!ax|iaLekN5GVBILZn3B!!$sH&!YQVXGVQ!Vp|cRa z^caD(=S4IIz(_m@_e-`S>v`Mv8pfH!ua|^&Ow28(&O(-M$aWR<-C2m~_H5)kE zN*Hr_TbJubJH!7(uPTGHPq}VKk-gaNaq(HRZ~%sWO@0!UBePm`yrrys7fu67{6Wbl zHi0@LQUUe-MJy~+shiJ3M2a`66nSzT1rE`hz@h>&b|}tKj#{ragc0v;b}{%n1+FZI zR9H&7o0K_6%Y=?tk_Jv=J}vRs^Pm~7Og3#w8MG)9a230? zSwH4cWz!hSGWeN-maFlRWeAK^^LS3OOpR|!MzPYx`3$wuER$m|%i;K`^*w!58l^B%L`6(-vjR@&;E+m%zEOHhq z-Gdm%k^)DuGn1Bi?4A-g?cXHOi?v{<(v_0V2OZ7hEMjGI(5kaZGg|M=e7oCiP)0*j z>OiY6WN`+=GS%|xi;AF9MGwVNZ4zKmh%UdY*r)_2YDU>)mMJ2WjjRXbBD0(h>dP0G zz^p7S*U3oN3K3OKu`^4V`0EO#FK6)s$}Ca;X6Y4hmR=XACqbqs{BIWL1|?CIoh!+- z7Ya6>D`MG{4RT?w-L1}!#S)lE6)_9L=en|-*-ji|jj$CJJ7B(C?8wqQXRZ`nx_rpi z;S?P-zQ9A`bourix4y`rdO-=~Le>+~jLO)IRnFEk2H9o6X3W;?9m{mO>8u>rVuUsm z9?k)S>vkAr=NM$FeM-#MaRL1CVo+8G*FohH8I{{KeP>ux&(pSwiV6}NO&}2vQRyNb zlAj`iN)b^&>7W8iiy)l@L_lc)QMyE>3sMB>y#)k`6ltL+5LyTc2`QvJ`M=loen`*R zvuAg9=Duh4%si}|T@5Wfw|?HNC_Y=^0`9zLHs=0V4`=XM_VE!jeBIH1=iiCEKKqfh zdd6P-3u7Y}Xxt&AGj`;xQL(!alCb)7_p`X_rPYSoLPfc_Td#h3eUQJE{i!oaMOsK9 zHv8{|og*9N!bioypvi(;w*)`Dt2^vwKkRz%&h0y2i{Cam1$Z8flVu`(KKVRttMSMc z`LUe7I#pm{`xz_RLtoS_fgcpYYul%B3m$3pUR#PkVfQk{dhzh7+~~`}{U^>+C^t2S zTygzL{>SeY9kW^7!8Q4@pNWjUIuK_q^3(6#iQ*r*)l-1uzNuHfzp=2d{Wg)ZM9~(w zsQv8MJ1igFK}Tfn%$p94XVzZT#}2;!ScoC~4mjj2GWhE<^}S#CM}+RNQ0)uHo*(S} zeTe_)yN`Z(y%!YGaM|=Sg)d%LBA);&z&eS@BXj1}hdGNSfFs_8P`Hq}07m@n`Q8I6 z2{VUio-KA|h=kIg@4lZGp;}cGV-6^zmJWSV$aAwczxl=U$dRIzie>%t2Tl(r#Jg-* z>l;Va?5N!@y5Zv;=>W*4nVB-_nK2zFQ}VaX{#z5 zMJ>+WSoYuVM#bp<^V82+YEaV8Ren+$;&k2KLhQe8!VQ>li>9IWdC=7r=ZD%3?`nFI zZghUL2Os_}&{0xz>_`7H0LX9H{pwB0;eHiLNge7~#WDH`-QP+R65Wl_J;n6vf+{G0 z&iA_+!gn5iz)OIX<)Wm0Zp?HWn}2$CCo@GyfB@7KNd1NOtm* z1uvZm|Jd*8i;9Dm@q(@#KE{tlwIutdlfOeQJx@EY6dO$O>vJ&!n!i8_DGwUu&Q13& z295O!=hl`n3!~%GMKPb~KR_){VR@@phmdae6^dpbzrxG6d zzwkIicMDW?6G|YZzOqph+IxCf&RmgQO5#5%UQ$u;_wnzs#moJEPpggOcRt5o2cHhs zI3_#P@V5}VSa2cze$7#1eEXpj{vAN2V-?6Qy zC8bk)asyT_&Ss|HFg<zY;SiB(X)IGHb)#SFSWc48pWvvaTsWnYKIvjB_R*b{n6s0B{$;TZI?RaT9ls z9liFxNbi!;9g&{{DT3CjmvqQVvR@wmrS@CP31rF0+in#uURSm1zVx%ux!WE%D4E@G z@7mNYg~6q2y56laTwzM4GwBIMC( z%E4;(+l5I=b8JpA`Jjb2`fFixn8`)E$MIX+$vyCUGM&xuT3VX-$tE^|7x(h|Dcjo$ z2NDa$hbK4xI(yuNB&@{W7P+^wf`2)>M2t`$o>YvZ*qKZkp3e(xd(K}FabXUy?qOo_ zU$&s92Rz}KWI<|*a@=*HQH-=%48_MJ;X=fL(UlbNiS!- zPX!Z0ep|)UDYtLVh>zNu2zW}Ebh{~~ z4_-)$y?$m?+obtM_6g7H1x6{3{Ohkw_T3MFO|Tc{POc}V0N)1a7r0A*7WAY$ClnqS z6*JMgnSE#-pCWSOeEM-h%=OcwN-4w9ypcuU3_Uaox^NQYS{?BF4N+PO;CZ6J@qg2B za`dhVJSIS(pjC3mpumPV0|A!{(!Yw_J5z8v#XR=<(Fgp%>6r18p1K9~H+d5vm12F9 zd7vOidhXb|kO}w5I?MzaNjY3#apQdGkKUhWM@>wEZk!ib4@)WfFH~&Q-bCfkQI6rv z>Cs;)dy%};@cpmr$4mhK>*!+>_y5j=1t}*+Jxp3t`ffA{jXvbHR(ovJ=7NLJI{1I` z1U|cSgL$<;{sQ~(=-LyObxf ze67F|(1W1kwfv)unngX6vFF`Eyb)=V8nu(2<6n`{i-(oA*4>Vo`XtgO&X*x>cv|?IacoBJNe41UU5V1b4pbtAzhBNJfc^B?P%% zRqm3^MGgi=*Vw4SX5$U$ovm?a`6D$p;-#37lkFgDDdyP_$T9kLgNWV^9xtw(y|RC3EJ0oE5Z>j` zd;&8gTNxFu*mW)ZxR%RFO*g*4C39S;{pM|_amS~je|XojLP69DBv7 znpJgPfW%kc^G!1rWf%B!cBjt4lxCK#`CMx(sowd}aCd-y%jYFhNoAIA3m>M6cZpNJ z;@~Kb+99@HrjKHmq&{A6PsKher-~1=LpBgZ^sebevxQJ@Lymn26*m!|&YMG1C??9k z-JMx|Fs!ODZOQ!sH)@Ggquht6WcAZY-;rXF3U<~?GTcoxJ-|P=F>9UmX(hJZMN!E4 zDv%`6oexT1)SivZ$mbY7KN~6rN#Am3y_fWbyR(8%_AQ&5_Ufte*Hj$_^_`@j(_B6d z{|f-e&ev!U)2Phe zOt;)mg!9DSiw=KW|DgWlD^8pYZ5|PMsm^)*t~c~R>ztA`FIPUn1d#ArzwQ0;;58SK zmm*;3+ukR!D$AG3`<@5v*4l>c{FYU+UcfX$x=xvPc?|VjRP0a;pG|=6_>aTSzaFv_ zc`1?H3)B}ic&@I0bg5Y^Ds`R`{W>$c(b%s1lSk+6*+9W=>;UQ-XrLS}*r}h`Cu7X|`*QqH0CvPFnrTEvvV&nyMXbYlw}j<@${&c+$g z5cvweN7YE+sNnhv10D-5hV9E5w#)gCPZn7fI0B|(Wzp>&L zJRE>>Q|NQmDq3!Jua0m5r+>9MH7M*~&3A9-K--)7pjV%hZ_ssaF9J~L!i7j zShZtrq?@L_$A&rywy{o2msr4@YNfY~?gV9LNJGvpuUeZZIQdQk$lq_nNDv`WErbwy zrn>_iUxj{7XbbM|<*mH< zDk^pg&PK{jy{mNQ_vz$ofXZy{gOib#OP8-YCEBCvG?n;=E)}>$EKyo)c_lR#1y~HWe_w4ITT1Vy zQlf1ZI>@~mwsFR&YX;i&pHp)0Ks=0y zF}XyaB9Q(`*`1rF9mh;jp^814b&++VW=CGJ z2WR)#oPI%5c@(*?W;A^JQOl_QqqD3B!-F6{58kiLj-IH*^3TK?%6U_KH!IPEwBQuotudvmTc=k*>;OMs?Wek5TZUg;tjo;l>cm88H#rdjF2Y=ORgY=*F zJ69;SojYp?JE%S^s#@$Qr*?%BZ7>xh-bhJ9g#g(kPf3J?s)&5$7f@~t zqv71(j@<0qypSPL$4G;zgf;*a9QuyY<)(JtqWhvg&%7QIP!m!h1(tX3SDl=61ymR# z!nbYyg1UF7lT~pp6Od5-n~-3AYu5u4F0z4(=K7J4ue5H-1>iHi4mZ_9=#ImJy#cvz z8QNzD{eV7Q&!!T~ss#WF&P~82&QUfgToQ3wEMZJd*h-Zj)Nv;Ku-4*vubz`rQ5e#J z?f;0zjY3@)SOT&;JD*JD^WLlnA6#|2n7#x>jDXr~`d@W-$=d>sF#XkIQT5j92ElkM zMHNa&HlwT7T?_&YPG>wGAUk!38lv%Hpox!a>`cm`K~a^}r!o~NyJD{eyJ%|XGunRd&=ps)61K5wn_@tB z7m49+VE8G66OG`?Zy|DVj5w6ZLGZE^j6NW%WrdS3uscC`Q;DYSfKjgWLLFAq(FiEr2mK(OsxUx(mSP zrhq2aQsS}W8e%4GmYWX&@Jl=HdtMQX#r!D?}*ZsqbD-Ws$l4O7wi#M9bT?>b0s?Rx^Ca%w1i$%t6h zLV6qY*td7cNePQGtWyPPKUcu^wpX872t$yT>|U3H>}gtdP+9d!1T$s5Q*VUk zSrHA@XKGyoH5{wi5m)TFTi#;{s-9c2oQiT^JZS%B9_OIGmcd`MyOjDUL8 zVM8dNdCzUr4xttSfU{;OWDxdVTD#~y;}*!Y>%vgiz3Nl&E;G7{Ce{V{6Q*|j*Mnh+ zwS*C*8Zz@;wHR3kBh?lY6Z}|xEXGmK*1UoLmxhV8@hoUvv7dcCsTug33e`r?SUAVS_*{ka=p`!*4uDJ0khQajWdRBcZZ z9O}HU*y%AFd1va~SkM^Y_Ahm*XZ9+K!CM`+_7sh=C=|S#6Dv`Dy55B!_MYDlU+-0q z_HWyAHdstpP~*}^fqe<2u<5oPyR8nDz3R_xUp#~0Xl6GovDa8nTxgQr;b)s5v18@c zv!z@lTFFRVk4Pn8*+|F3LicC05!xBGD(bWe}VyXq;hCgx%MLDpYV8PBB)$=NS z7jqLIYYPaFfaN{UY~y04Vppc3Z8%-b;P;zaf?acBN$oCYk{O>kekaoxHyaMtyT}eR zlVGZR23N%ZOzaCuM5VLhgzHuCRCmDAzx`>j-%(BM+*HiH`(au8%9P$~f}Qnh=OLXJ zSt2z%zC69SkY%ASwYekX)orrXDKWyU;w8v*JDQAAgIBS?0gA9$nFtr#ScIEY#o9CG zj_0`JQj>55R3UazD6!8V^KC?Cj4@|`vUJ7vJ+|817a>JA0CsdUZ2^@pX0!#nWD@)S zWWJq-t2x-ddlvlfU^)+y<}kVuoM)jE(AkbtEYW9yKe_}Fi;@?tYVngSayz_=4N&aXX*e$GqF&LZ9DeZEq_*-`71y2fqn5o>1m z`%FYll^{>Wwq7s$#g}Fo<}kKx>@ zE$z^O@f_P2EL5-0RiMaKbk!M>zBpxAK5J^YK+kUXJMuy8k0cgT z-7uN}^P96&Tx0K^bc zKSdk?spus$I{sz870!%-p=$JZV6$%-Q;8OCqHVEHPU5SpDbaA}!^6}<5KF{==g(PN zA^P9#M)xQpC80mWKGOvS+SIo@4)@-?kr zARFz9_up0r)2}Fs4t3tt-+GuXke8T5zZkeQSmHfV8#E#se9gc*@!8+_HgDvv;2?OU$QOwLCaLWa8TN(v8AmY`ESWqv@#i1;I0 z^GLL+xUAp7ON(xq$&P3#p_6}B-}gWJCULmiy+oZQSDq zJ^|9l0ZN>xo2+s-ZvqUmwIOC-LAePdw$Y{ZE31p|Cx5o=9GZR0b0I-G`RE3r@ZV6i zSn4~3n<6K2J-GKZkFmU$*fI9%E30tVS$aX?Bx3nu7p1jSrGnLqmTwQC4BZ~j2a8QC z{g{fXUpg2r5dm>;Q#;(=Y9#2X%`rE|NYknrSU|rG1KKc%w ze|3=vHRTtX?I-srU?1mA#RB!T(X&0|p3%b-{JfhVIch$a2+)N;rYgx`ji(a;SQNkCo|NE1 z7fd~FCCdN7<2+lv4ASCEvqv1aV1&!n<__gM&1xdoJA=j^gKdKV?Ao7Bv-kA$H^D`G zLCzwM^^tqp3Tbm8N*iDRF;GS^2J9TOrtb&>n9ucmx< zHVVB|=!@6t&gM4d2I@7!2MVQNv3D}ljNA37_%2Gq{_b>MAk=}YO4u1T_J})(_{pgf z&|2I$G=nYpL_ySpZ*!X);a3X`lEdB*4)feq6%ii-#q4fz36k|TV?0iGr=cX11D&-J zjN_3hoAfIE9XDRm+}fAO=lb+3w-=+)s`|+hJnSi(zI2l38~i*KHT_bIBVnfJtJLjw z!($O zwa7%k-NZv%(6IMz)#7~B0@!*r_UG*Ms?Y4>7Nzb?=<%Xu<)Kamh%>hj59J&7;l0Y} zM|z7;TZ}*BLQ0I6F7HWSV+YAZ@6qgJmGN8>IhjoU{A&C(Eez*j^EsHIBYGqktQ~%l zE2`$x5n8@Vw*YCTa+`Bm_6C`YE3M%adlkSRwRtZyc)eRk=uhCS&=C3i|8`|KL|vl_ z?t#_oWHrOSe7rr=!zKl^d>s~OM35v`MPt!onjBb`Pf#%@Jd?Y7E=08v#BebWxyVS3 z*H4{2B+}9>1Gq#qKxG<#AX?(yw$n*-tr$na^Y_vo+oXAg@b;yQ5>{fJ4v!xOXC$&WQc z12z|rqcpj3Ga322Q2h*jht!M@#ktgCwT#rPmk|f*G(8J?-ph4oi}$lxts;#0HMtgw@FqFF_uq zHcJh+X3EfHAvaUwJ|G_O#twd^4*QhsbZ-BpCd20+3Bi&jkC=xDGjifv^1}a2chm0i znq*DaBu$=f#Q{=58y~}fogrASM$R{+L;>;uIc&gC4yh8N&A7@tT0e*7SFE2Dp7(+) zZ(&W~8n2;qlR`8?%4K|I5Hb);vV`1%G0+TFi&OhROS8C-1LxWEuXzppnn1!E}85&f#K0>oX%4Tr|j+R5#`7Q+) zsz5HHe>e~Q9?1_14q0QreGt^_*TIDB(_h^_DZYfcb`!$}5Hv@9z5?xuEA8Q?^J^nP z(-Lb`uMK1S>&}0GPuek-Duv*<(AB?8$uI|G@{ zAGG1M0dEwXT-lGT^VO2(ug{TG0boeT&@KAG&F;_$-OVv2U(?RD5rL!i&4)H}%r@!> zNalKmkgY|=dY0Un75pPdyem?}maG$vf{>4q69|p)CR{77Mp>$j<&B5XfHN=|hL?HOOh8g?dD|d+1av&Q z;yMu$&GZtb%51!>eCW&1eD%Euw)Ti`!hdT}$8+ixc)U6n;~8Z87Rp_dqrFqzhUcOD z9Bpt1aa?;1{mEk4To$6t-ifwB_~1!%e7hOQyW&8E`F$19R^-)mOqzc zDVgzbtbIXkUS`HX>-{F|A2b-9f`t%%=JS^WO6sEN(kS>$glo}3i8C~` z$i|A6ply3lh1T?D>o~qIdom&!%Yl7w^2vd3z)fGb0=@CPQ|XyS7KN~q7h%)*DaR(n7`Mp1qv`JJ@V3EfDY~DugCvAZeW*<^Z}IGSQnEqx zK|wEP1rDA`#OHXCjgGJGKDyg_sgnvTq*+-s~Irz+s3Ajnv-!Cwnlq6}>*a$RFA zc;S>O2QAo3Rbha57KdkR+*9C{{Q~{%(zL+yzB(bBf+20@`E@a zeA|fY)0S;r>_Iek)a$0Rq(8ERc_zov6!wO4R(?|_i*-om-@M+t z*WA~3$$JtK3QAO~gGI_o+Bd$!kk!gT)TcMuE%)!U!7~J@ub;g+jR)p62YH)& zQ+%_Y*;g*%?A!-l|NgsEzrc?AjTzUvF$u znLF4~tRX@|skWIuk&2;?Rip>xQcoItF9(hIV8y+nr7Sw=Gsp;L)4-4FSZ!_=waFBhORVZ#G3i|iQr zBw(%q%U=t_O2dDsFRdfi#|}XVH`DmbF%JoO{zEoL3E=#>(rbBH(VIvv1H?!h9&5ip z0pXPxJX)?IodbG(+lioxgS=Msz(EYOEo8i{x z>QN6*CTVAzat3pwY3>}}V;YzLASvWD18Uw#V8}D3OE<7l1O|XAY86LpXZr2;PZnhp zi+yGnfx0$OmOMy(G8iZ*$5r~*;DkyExmtiD#*(IngJ{Pd3+5oAx7f{d#a9WssITDLu8q|HE37x5A^IdYDGbE4x z`KxDH%@AHTH`ITke>c^%d6a*MDDPjoQ0Iq(efQAvNo9~dzUAGWi}*Me466odHI+Ic zy=NEZGT+^q?ygMpMnmXGg?`b}ZQ>b8np0}6g1uK;?gs|aqJ&tDcIruCl!5kGae)fy zB}mlNDw4S^r_6gM0kQ!LW(G;2tq}&=!=TNrP0X&T+3v7-)r9usWdE+khVXd8=Si&V zJ~`>@cKtX#S22sFO-3i}wnNumUH#%fc+iLto3D7`(fnd&?x$CW?rqYndqA8UjHle! z$p<$DLRCMn|8gS!INvhASYZQtHZlX!&Y;A0q@5%Rsc?Ibqsm#g@xW-6}Db7D3HWTV6G@{D2=N- zjE9x_23XRLyE>_rVk+PzNNWGirW^(j!nuEF_jUqS^Jk!*Fj&!{KN3O@Qr8hew#e6x zd4o76lIcF)dJe5-%-yn8QK~Id7OP6vq3x!$A{{rrD(I+`YxlvNmcSFp?$Glxhc`c*Bt6_t><5@puUS#SFSEX>$J{4#G<}U;H+W_}JTC z8H)3c=Zy6Sm0J%%7;(74r>(f8yv3E~aMJ>t{x$xVaCylVVClCx&)!vh{iO|>l~VSgw-@ZND46? z(%ug`q30_X-+B#9a9s>ekZdq%w!Cc;yY-}_|;B7{R^7$ZEY!rJ*2zYij-z5GMNT-`_zX8%ib zuh39N1(Iuj$u3kjeMWN)v{?Qy49_?&;j%XxD8H}`=k7Ec)aDfEDj(6_E4x0E6zUiO zu6E#BRu+OWFm9$gi~aGPS~Mm|gTT28;^WdmRd52$^iYh?^Dcb6f+LXE091GKhpwp<*68Dxf`?VrNHATIqR zWD~xOZ({J_uGR!8#7cl*{R2F%T$ zL)12t7hgS(?WTs|QkKuA2o&GDEJYySlnZ%x2S{p;H&Ig&r) zkl*KXf?xMj+c`P%8mogUxy;q`Sg2w$E9pEHI;6=Rr)5%J+HuD%U|}814;$e>he$dR zN`$;=9J6V_I;=nmm!HgehjP6*^ZD<|g~Zj!f~2k<+x4h<rB<=oyl@`N%#d5a*W*{ zB%hoW(bCjclYxkDc^8h5sgzl0{nr4ROsh|6uh4bU-_Ujn81q@(U)@LIpJmt06#Nh8 z{vk@B<5F=am6>Km;1`(4*CT(axvRG5#__ZDSLWdt+MziKc<#7BeQBkI7Y0Jz_U@zY zgblP?;_@=o;qm&B;TTP9cwCzA%ySqh+k{;}=p$T?4&MsIvhN;bq+*Q0!`5RJ-pvd~ zehjmk%4eO>id;gzBnDE(ESf={8Mrm~)xRs7q7|O*aA!wAF?iC*AeE`0yfHlv53p4E zhg$ejf!T&rNm^o)_XHR}PQIEI*XPa?Gl`BHpv-aaeXekJ;C5X6z7shTkc9EP$QZyq z-s5axmh4gV52Y&fzu|fVh4V#XPFEO0z4Y$`5KVC;ffL7EPvV>SPkdWbydp=xV1yOC zIgoVMF@Kp2Vr$i%PZ|?7Z@Zt0N?n-r(6=Z|MSEXwL#Euzn=Rq!|F@7gNL|=_5fEYU zw3$;pH--3zF7%q@juV+v!601Vr=nJ$Yq>n65kZk(rQJ!jSzq|&2@bzTrGeGBEwUL* zB(fox6ZVO%_39y>XI_z^YE;EVyQw-c@B_8CIlDh6-g+ihC-Ckb!64i}OzXP1lY5;Rkp1f^BtWB1K&u5ZT^ z?@B!|GHk+8rI54D9YRWsKY&!c#^G1@J+#{Yu^RAwF*N=kedrtPeuK7a6&d%*mQLc8 z-LCyyPbmiR+cgi`giRkwI0bY+u`)NDYtu()NoN&T4k3aFdB>L7qZrtg9;)3^wB7?Gi zc0bQubD8nGGKof0!y&HD$fx09-ujMD(^M|e?i;i-=ApR?#dGDpN6Ygc!F|a;+&I}D zK?C)DB%&;UF`?bc#^EE%==*UVW_ox#Y@nnm>=J{R?Wzk7+Ny`v?i1{G_xlA~xk9}Q zkQC&Ii@o^@4klGWO!yyc1D#`U%M$P7*f>4mM)cDPfNtWXMkg)_*SJR*ge_EcsBy$L zIv2#zlLNRL3yolvsXX|I4>4%89ouvzVyCI}E|-8d=%YTefc*htU%%F4d=cM9tGysPO@~rIf7tF z#_QkUjks`M$DrnPMt~*YOB*R{cRitCp71TtlwEGV8>&pjIE)XLRzfW+i{~fST{)#l zS~MeYQ6%3p%zm7&L^LRi2D;OV;nlxj{)1#s`j=)jlw??O{~5LBh2kv!2eH*5jhWm{ zu`PGeU5ki-dIm}H15IoIcPdmSD=ZU|S)TZr%PhN*!;-(>)tXShv{$cz-d zw|C!ZT!r~B92QZ5*JhR_(BP#=P7Y(g9Ln5jPhv%APuGUk7?U`9A&(FVw1rY+?Lx5q z3mH(e6N#0<%4GLpLm-VP*e;1a_r;S&)S3^9o&Ump9=Xg!=o_i!Jtguw?utpK5z|S~fgc?7!B6NfyKOA{8BKJg+?F zK^sDngB}}Jswb?lArZRFA#O$6Rco&Iq22wcOKh$5{lxtxFj#%vBt^B=qOQ}TZF)fqF69f<*8Jc3K0 zCe6VdUo2j@755dM)2_0p{s#=iGfrGW((9KQ5XK5ThiE)7&U3M4m^pr3t9;i&@ulv& z)v=LGDEBgllQf$U-n&D<164xIC(PJwa2$Jd3A4WZ6_bh%FxzAqlV~cqFpfBb*s<(E z+iQ=fTC^=WVFU0V+R2p+j*aZ#RZe9XIbAlyVs$jZ)q~c%B#Y3B4us~3ynM6K#BxR9{R(Rb4Mts+?(b#1 zs%j6Nc^#6Z51OlOp2fIEJegMBb=E`PN~eyt%~(8y*?=X?=8K-<9+K>#vIYcY1+Gnxo@7dTn}-Q0mFGl zD)0k|b>2M-pB2_e;1XYn4bI z;G;D@8Vf`|RnvzbzqD`zJnFxuJ@bhvjzD_i1aU+SKohhKt3g%`B%mK4r4a{AUir2#uy6_Y*@;qiMuHR8y2D3jffkglKe z)yb1D2k8T1tl$YvyF_qLs!d9m;D9%}y+2_9OJEO#EuF;h?<;)ah3a@g!KGzoF-4&Kez|ppMUanRC*^(K8_o~_Azeun^Dqz ziQ|0m)Y9p9_5K^Z1YdZdLjl)B20{)e%{pli=5@nxfrMH+Sn@L*zWqvepixyX#lQK$ zhQ$$uZFoQOSe;lNe{X8#wRLnLt9V}63nr=Z4Ys1)%RsU|K>M&p1|j_osp!GE+I^dm zN{l4iS0<@(L6WU?Iw|CG2wodYl8X%oH16WJ25?f?#6gvuaGEw2TI`X`(aIIsZRaBM zxtjSQtuf4VqEyEX1^e-c2O2+y{~uf6J^(S<4m`IFD58y|V|qTfvRhVD)+$1T$WcvY4Zl z(YFG+>nQFz3vla4$}g0NXY4NK+^yRL&3yKcf`%W#x!XJ253k_lQx*#FlhtkVQO{ntkytx2#?3WvhfpH&)`tRvJGJxL*Y`~1vwWY<73pZZ_t0Fiznjf7V z-y8^JTd*sS%le8^YcYYpHdo3Kl{tZ;PWdpPyB=pQzx|~*r$srFS)D3U*8YOSFNv?$ zo{>_sgmT)A;geGhVE+Mr8DmGzquRk|bO2#jheF@~{g+S?6X<@c(b1xg=0qWlnp1;* z*X?p!vo(D_K-&Rwfb6IG7O2@W?$MvUbdD_JeVYA(#q=<@b~Ed#zR$I!4^Lmp8;cR8st2x;zrsq$&L$oS3}#rtYw2G-5w|6ZF$k8l5&SD|F#k`nY%=q|TAooMCTRMA z8gcEsoz=PJ-{z#!k|r@!ai(5f-^!FZ*#GD9E`%&0FM*%*KZsu$=)?awC_o7cQ&NTm z@#cJVJMQ$%|GLgAC7=yDg1c9IxeiY5wMrbUN!Pa;X^UaaU+G&JG0Xcd_J4exby!r- z7w|0v5eWeag_V#-x?`0NVMS0%SW>z>SEQr`l#o~{LFw*Ym0Y?(Y6X@S7M5Jtc=!9h z|NdV8xw~_p=gyfkb7$^3b3QXueV^%t4+H)@mDKqPP|f;c;ncLM;R_6F7=0d4u1$x? z2oLmIRy8Mv)FTB9A$@aMv*{qCArA%Yx?$p(c4zj5!qCoIhjQ*H{_|;}jp9#KNxWiO zXrql{9IE_l8M?>~N_Zz^=PzXriHENgUIva^k>RwvxF72R2^axF}&Q&UqUFd!A+Z9c#NAp`3?e zuF42rmF&T#BPE9e^Op4372Hsp-O344d&S%FYeXCT((|4}D6W6I_wZ3#J1C$NKEDhp zUb2??jE1aFad{Yp5;Vhmk~U`1dsArGcwp}SBak5~UsrOk>VDD@)06zrBCpQabY6Z; z%cG0-lzUysqmoo)aM7Xs!_3f(!mw}0Xq@HK#^ZErs7Kf4XW=QN(KBNi?8W`0*?M=H zI_R<^VNvq!MOk3a5e+81CpeCac%*SOYA9ZYr1ijsw!aP4>A?D61$G3V?KG_cPTB%V z&3@%HHKo-FMG6m5_hsiWpy8>&%Kk1yg8Pe5f~sX;Iu`%*=1CWQ;$Y?5&}Z+k!DWTY zTt8bZcM%>&$Fut3NCG;GAIo64vRT9|L(*)`keG=IF4#geRNZdge|F!lvLAZMtwKK~*syLa0a{2jjLFcg9y=2|OS20fl^03$5$d89FtX)n3s zw~dne34V4B_Sh6fSpJ68sU%U^xA(nXQlM{$SuGV zf<;gA9`A?^o&>}W%^I?v+;^Ayub+?ebp0l|quxyT8AIgvt>!$O8x#K2khr7{4{y!Q zOi#qeq>=Mv10g9z`FVEB9yd>#(-Rl$uVQbilg=8`NjsD^)Fs@-CIs)dR6&Mxl~bHb z*?n_@I-wVB3qzY-9%Oy^g=xP9(lnk&TNr8-OtL-Roc2TE3}fziSEfGTPAoUcvOoye zGhPC*;QFMq?M@VA^pzs9f2~DyAux>j2@JY8edbC6CK}qMf4AhPA$T9jGj~diJr0l`DwW8(peLz6)xIwZ z8OhKHbB_0F^F~zbihxaCiOe1s{5Nrf;}NRH}g7ESDvu0t)+G5W)d0nZ~!V z;DBrZB;*^oY-{w0PEWx&mHY#FF-sr4ezC>NM6TgL;$3YRa5ykjhnr|nn=jE1 zH$FCe0y7?m<>D43d$1{?GFTxc=!jny9`|)L27eA(B5mvt444M4z3L!7|KYFge%cxM zb4r4-OFa!4(u@slugWg;(7?>{KaEB{b&4zy1q z0_tB(CL1=;@Bqpk0owbOStDwsLhebt+JOV#bPygt1hj;p@Fk!9lj7H>)hw$&LomYe zr+8G(CVvz00^Rawm4tC7k_~f2tt|{jvgrktKO4 z;Y*$Oy3V3`N%YGtVk`+1>0K@jBqrgqE*VCNh|Yb(zeH5*vcqfmWxtenc|MI2!FOO2 zn&K1s@_18o`D?!_f&@=6V^SD{#IgP)+|9DQKcOGlHhRu(&xn=BkY+N@b(D!EFh148 zGa6KOdsS(Fw{{CJVbeomyV??op17hs^fgjq z(IJB5Glmy)qIm9z_&8Gn34QTE~0^0{&S2H&suFK zQ60ZQE(<7I2JZL`fu;Ap3K=~h_E(!aE-HWTuYT>=L8#z`yB1)vu7HzY(f^St*Lng& z^Cuc&8TKL(nSvNC3^^m7J5A)ZV`zTb(|m2^0fIuDD)0=ca4h>=r~Upt{wfHfR7f0F zI-#bVildqi`?k>{vR7iE*0f5fR-1f0EWHo8ieh?`(gE)tbAM=lNFc{v#T3Ae?MU7n z#6fTw!bucf2*g{c!@#Mt>BLkw9Ih;{Lu4V)V!C{E)20Vk{}f!-f4zV7C29qAHC{0p zXGe6jlbymb@|%OYIOsP{rTURO%+# zp?D>xE_BiIUKy|4>{9-81ARtW?0>N+1Kq?)HhpEUxZpNd(hO+`W0(C0q%A3+fDhPl z$t)|KZ6bVw)Ty0DW|2TB3@Gyfu0zpgixjFO%~tj)lHwCf{zUM=MaWp_xz)}Q+llXH zFx5%sgOgkXRo1zDq5<+hi6o8;zxCkg(AWU_(N;N>{-j54hlEcO%i!6zuif2UT9!w| zp!-*LfbCgTT05n}FKIj!2nO)D3&-I@D9M0Qw*H2_yi+oTHl{Mx3(rYdF)IT$`ksf* zZ$NQQr3BYaVq@-X25V*l02^Pjam1#Nw_rL^+qu;HI!OxSjhelIDeHbC{VYR-Pk1x4 z&yf0fE~b<4omWh2XCeBNk1fM%*cBQ`^K7vf7K7`s2`uS^ye-~rf8)*XK$>PYV*Vs> zu+9WIw%go)czWv+urr?wnW_d`D~BqbXru>=jw^@Zg6>_O5kPDhY!Z(EWlP2@{rkpUVe+A}VHe&kk7bZH!jk&nuSdtxy9>a*GbPvBeEES;McWRIJAhPI^u<9Z z=@c}&gcDceuFLPSv!yb}LCNYVW`B`6K-XYR_>N@j=?Uoa{WWaJ`-0t4HBzr-vg?#A(R1vo z-%6i@CU%oIy~QF)LkZ)Q2a6?C++_3%EahUpVBzwvCQ*ILL&Pq`Gaq)jj%7L#DFg1m z9<39pzPB^0=w3U+(p*Y8eTq6Q{2GRh>S5h>4r%ER4T-lqug-5U@$i)jEykXH96VT3 z#{|rGl{Oo+;wQu1YI{C!Ifu@+%@J4#d8I-#K~*u(b(>3(*@mjS3HY=6Sdd55PGH)z zOWXWf6QaN0>n(A1kPgefZj3^AkFPgS&Q*5dbkx&+bl~ha73q-Auz-ns|0(2i!wj zdW5qdhp10#c+{96y{dp7VC9u5T^ZcjVad3%U6K7kfb;`ABjOBTh^!mO$$yEzxZ`0` z0V}$*+f`@=*lU0nn-QvhxCU8zz?5;aS9;<0wIcyiIN^<5uGAIdDcsq*;gd`HWofYT z%PGPCe)OjLJT8be1Sezz0@pU=!FC475uRb)ykHGc%Dn*O3|BAEf2$7E#J4v87eB3@ zI?CHUT0cuE>w~-0-rG9)xm)9_No<^IHURG^hJJrQEQ1;dqTTLPj();}p)j+f?X&WU zhFWu>nY)`SfRETx!rZ;1%QUr}ST`y7$Y9k31Ttw8HV}0FNSrt&JoOS@?nE*OSkT!Z zh=Sjld;&fIb2AfMNHPfU8S+QFH-OS=x_JrM(tND)tU>USSDwPrpNmpUQ0GL@d7mBL zFy;3f7*6M#%yW21AW?$43bNDZ-fS`}M?;JXgE*GL{M%n|-T_*6f2%T71UqB}Crl4d z`wakkcN?@~%5gtzlm@0f@2!P_rMr~o4v=TF0`q{&0+P+M#&#r#1R&M$(KOS2BRuw&UnMZZOV{`;}Cet@K-!Jpq^mC1_nq2m1o6q-}LUe z)RnK4<^Z6VEQHS>;w6S=BL-6r8crD%n@R^tfC)}hm&%4nnX9`Lelu5dvYZsaUNV=VeOvx2#Kzt4| zVQlzM6XEyw@C`o+)G$jYA_1^t9>9isAcpws(;e0j;@sS`vo8t2qXZdRL5&0j-!3?` z_msp){#~lejX|ZNB>v-ujMVd=vR9J$*lOjw%gHcje-QY^X zX=2=hX&oRbayGs!qxT-rmYCK&QXB>V%1kmHKi9cbApt|dwd|IM@GB- zLK9bCcp(&eHV#WWrO^pl#|Gse%Eg0|2nz1rVH>i_k7pG~M-fAVbz*yh^Z&wAw((W0 zK8=|~o#yh+ca+rGe;Cs?m4CY6FsfXg6MYY423!zF!iLMmTyT)M5y(@#R@g-htaQu;0xE!BgoFsq&}#@bl`CKRmjFtyEzdOE zxfGfa7Plxr#=ibG>JS0LVMzb>l`o_g?)rWfTi66r0rx{pp{h;kt{A@3&;$5~MqAg@ z3Kj{WQ5yNd4L)qZxN%Vo{*tT|;^BF5_No*DSvf{~tRwR=9_WR=HQ)=vQfM*W(d!*f z!Q$&CH}>q$g`&YwS{u|54{{gulR>{RUOZI2ri&U60qHvit$?1z)5{EXD) z=bKZ8{Tyv!rT=?Dx&^=PQo$THmt`IFeId(w^7qP{fP;2p@;-;!`BI?Py!kpZa^0LLf2d6_+C-{tU7JiLUEZATse>8hm6H`T9QFCf`Kp)q5$M$T`I z2%9YCN4*LBHea)N?rlUi-|L^>B!=I6CY$#Yr-+}{*F!g9RF$DKEHUl*mElSqV7O3^&y6%Zkt+_ z3cCBR`gHjJFOjlwugJ}K zYn%VAOy0kQqSOA7Z$8hzGFr_a7y?y#6ntO#jjp%>6H+=Km6E^RMdF{a2d;`TiwT^It+={7dNI zzl4hX^FKVNM^&A)_d{!3G!z(17feDbgA)%lk)<^L4= zum0cp{9i)X{`K}kKm3pWe-1Hgh5$;3Ws}!NuX;nM-Wq)~FQ=-!MNvWfa=*Gj>$#!4 z=);}Vu7J24qfCHKqTlQ-zZzzcKugKq4-|`^C^4e>F`H2F<7TTk$Pb&MQ!IY3hlaeZ zAmw8RuA6Xv+3h7BmTsL)mf?td7A85aL2hv@)=gpiOtgo}N2o?^P5ES8?LM3DcpoiS zqS#LciJ{sbcazAcH0j-Z$J7C;iK4wX42MMfuIWnJ>=eaG%WoB?aA+{wzT1cv91TVg~rc6T{spdQMYZ`zNhQWvWjQfFtLj3RsEmO8~m^5 z^ZwWKfd5sft<1pxmqE2945wZia&i&ipa3<2n&I`~0kz1>4oK}}r`Q8u*x|d4liT=u z9n-;aag!U%F?C<-tsKLEowja^EFO0rCz3luq%j#>_ILch4(4gw);OL4}G{t!MMQz^~Xw=ip~{kHdIYANkb~q)v7&;jNhE9gZ;J!sEuRSsy=`Qkz$g0e5eIT4SZoQ?O{AQMh^*IgJ5rY7Ab2sH;Va z2p|h3>VTX|L4<9wJUEh=q9Vx$gBFlvl1$P+USW^+;42(pZ{yLZ>Bq>)|T~b2Ww#bm!SJ=~B^YCE~K+Cxw)gkU1N9b~gZw z!iNz%4g>OrBsOyuMtwF$@m9lk}p1bUwR<~_dPu}WXH|KLToBL&* z@L}G!<|In^Zjvl#l&tjS=X3a-A8n5 zMYuH_hf}$Jt4WG>%Z*A3r~!xSMZ2-OPL^Cy&As_0eY@#*&9JrWrjC6;BvXX%`Musx+P4Q;fTLFq9Fo*Ve$>rCVfbinx7W9`#|ZTDvt4;c_V zWFz)t7$kRx{gY?W5>Iviojq@R<=3lQrRn(+P+0ZC8*FMiukn)e%$KqdWd7wm%w(QF zB>%T@h@TK)DgB;(8#_g()Hm~d!_vNK<*>kwTrP*S-H#BNN9MO(WvDaL<)eca5GeXD;3jH~JciH?0QhWUg@1a9WLD=JIYl%uo#=$)@e#=L3 zeIj;+fwdbA%TIr`e=I%ED{p|k-T@^kGI)%qrx=zB^%$m%UufQf{O^@N+F|0Y}|i z099xNGW7LTf67~RkUu()a%DhHrS^3hF&JxK%+1h-)IaYigLdnhZ88r$Upy17T*%Oc z1RE&!QJXbyFY3ob+E^+VMp~t~@tVV{mxd|>m!Fgxl)>>pqX7>N4mftuAoP-7IT`dg zKH$#!CH*N^kFi^!U(QJVBYZja=HUspWX=8x0B-4|-JGXL&{=(HC?D6EzWS$-`Y>~X z^Uv2PWO{E7RR6t5(ov>M&|)KV50f71a2#l;IIDi^14c?`XVakoYW*S?n;zKF@$t-5 zIFN1mJ72!JB23_U2Ubscq!P`)-}qt1?E-q`GPLI1f?;CS*jKG{MSgIEnR0T1WL1?% zfgjB`U>A2-e&yPlgIB@%&nxUSnBwy$d&TPTiBPFk*~(I#z{)fR5!idA#|yf6G-&10 zv{|$VQhRWiUJ~>rsMrdcxw<^ANXRKRkH&@)7j)L@F`^u>2IOZhY`AmDn$4h-a=4)r z``mm)A2hQt5GK{&ejhqo96lp9*5JzQ2&zIS0$Vtbi zV7FTuEdm$?boOKt*Vc?a_?CsgpGna1a@Se={`0OGDDB|-i`WF4u2R&cFh+ahRBiB?zVaF3%)414Z(f2=nT@ zO>1FwlFBYZU;P%aVILckC%!p#=0}BSt$kk$z_m>1t_AG{hQoQbapI+qDtYHNUM^s5 zB1lybn5VZv?u38-#QF3SCUB^`56(3lTbb^(B&}SpyLkw{JUoro#w~sqq-otRUo7@p zsy239YTCEps%$^)JMxm&T5}tIV80aOn9C9U{<#zs7PHzdE~wmf$Gl6NvrUQ!+Mw*gu93MFbO)mN0OYMqP_W3 zsR(<%TjDo@XbQ!ZOq@d+lV;r3upyv$w7I~s=7d`+O^Qe6jIa;S(dDqn-kGqI9I(uKd~jna=`HDNv^V`nkDV z%ZS|!>wQh9XFQeK(}kZv&s{pJpVEKF+@knpHvPp}z$a>)7AZfoC_eV6ung&*+OVjf4(YVl*pmOly*gKB4+EEo&DF_v-cB`1Ix-n z&%!ULWcSjp4@#HJb0dCaA(gZ1#>K53c&&(A$=k2>I3^;)P3o>nc;?%5#b~m;`&r`r z{%S{vgI%nu)SElLbF?m0*jLuSpEuol1+byOpGsv#_EUt}8ficCenn;=>D5N-ovVVf zx^ttPm6FMrW5f6LzT5)cUaRyo`uit$(__()G%`7a6l2X&V(@v7+reDIV`{`@Cw2Sq5Er+&gAxg(GRn`w~WR8l+(=TaX6zTG&Cu+Ac2-#vD zTbRFjhUsFLtQ{P>+crR-4@fA@|M!$Ow8Tg1ZiXLRg za!;0f=Do2EOR4L74L~72#=IN_(-W$YZ}ch#7iXr<-^g`Sg@22sUX5`cX0^}=kmgpg z9s01Wao@)4rj2b5r?0h93Q^@RZPBaS5(tQxFFKKit+GdViWvWpfUJ0&W!fSF%S zK6@bZc1w+f^Dxqz_m|UOk39aon{QYr3T}0Z@-h+Ls9pdEZGRODA`&4N-n!SdbbooY zHUHsDJGP#eAx~c&Gc?eWzn49->NLc1H|4>F_t>8(k4e$_)8YugdwAB{2K|(SLI*rRj0VZrGMh&gQ$it(YUuA^ujE zE0RN}T)Xd>r{_Sdk$V5z9kFI=E}Ebc1%01NIZ=zr=LuH??98(SCj5x~_kQ$EH5ga$ z2#k$At}(6b-5#YrxuML$*aw<4XJWfQ9Ccf3mQ0?0!nm9UH0mZ~{=Qekd=TELc zjwhDc2p~?Ui%7sb5X;^`1^AF&$--X;Qw07-Fkdrm|{OC#;4?wt{w5D7oZT8;H z(O-W;m^-dV0<1kx&s0Z51Kn;y9G~$vw13F`zKclbd5f&;q1JL z(Xu4OhRyKkoa!67E!(5;j@T&$Tegz0j+m(h8@7@_#n>rxE4D|0xjH*ZHOUMBUX@RK zQ2F2XYhIvF!CUM;Z+2NGI*k^}Eo?dk{*=8iXAdO5*?qG-eP{Tp`6sy)`I6<>qWtd- zO|*J>tKMwOmtQ|BN2s?h>~sR^%M9hsIF9M5@CyW4P;;7|CnQAtoA6_%tH(|E6tC>{ zIfmdORUgOxar^ps*&=11#z#=Qeu~;(7RR^uwv{=H=|k`i<)zG%$P{~`fyZuV(U`p> zK6J>?FN)SXlAEf)Q7J&!|1P^M!d~hw^uFr7n-wGGV4L;Y-9>+mvdGiq@dT z`%g2cx3{e2qCyarO|K2jEx$Hu@CC&%g>Syj>-cpW?nB~DuS8nWjPg#mU&X&_z0>*j z4*$u;P)q?vB2G2f{FhsL`vOlU-&@E6)BTO6Z+bjs{-x8@e`w!RL&7uIzCVwZpIRkH z6mVAFdx*>u-Y>*`^UV!9dTGNhqSUHX!@ z(-RRN=ZT;q(#P6m0+S~?TEEOWw9(3W6y-X>GM-l(!_?-A9qhw|n8Jj3Lxsu1g!|HW zEMy0E?ke8)|4ghe)BPi)pZTWV&SIyqD-W)25uV$#{lf8HwQl&aJVB;hmoBOaH@x$L zsvsJ2yN$L>3?kU}46NmcO+Nm$j^-*G1Z&z^A98 zp6TbNNCbK@$CKKu~1skjD35MiL`7 zh&>qpr+gZ;w&YGO!U|s53@i$yJ0)stHHYxoO zA!*vXa3Jlr1uaaBE%g$2&+8p*xz$MpsS|hCEZc=vB(~FHu!bK}nO_kNQ^opx1=jXc z)Osbz4SEg9*H`KL2zxs4vbgxvIc$lgw>Qbz+L ze}SK_cw9~}WQmdCwJ4_ozt=JxV`_7cNvH2L<{mG*4XW9bXUkS7FLozXFdQT)NJTIH z9aN*6h_Vo-DgEx&P)c(s`jFs!+vFfQ#e1Y&w`Wf@TV(wqc zplbMae7F3;yO-4FRo|w4e7xWVuYBn210Yy&YtNzJKUpW;V&?byTp7Vz`oc(5xaA$| zK=+m*`cQc0?w7tbWel0W<2iSGGLLpdX|yMa%xaQ?4-x~M0o zXa(-oGnMMN*LPKT2DG=LsKEr<^f6D)%qVAiEVq$HbpvpYd+N#7D!}!wGiL*uhT~SZ zXAeNH)hO6q39axC!7r z8i}A51jluNlh*^0EQ3d|TPF>OYLn$NT2@T_I-a(`viinycg>(+^x46;k7q@hQ2~8< z{q1=DprE->-g-rD)adVinPo&;b$lwig8@O5&ENt%RyG10( z4NfAa7#ccOLfRuDGslr`greAA!V3?*R&(@)Ns4rb`f0;R4s7pd1WDm4_fIDG>(D zs3AXgbMzMW>OyKLd`}#^wKwvZ%JD+Axt`Lf>mUG{KpB{{!oa7f@#-Osd_~w}frCCr z{Rr}+AhP}B-d*@+#kCY#*2CPFe{QMNj*^kRw;uCRkElqy27=O&X}GD9X}G5|ZnY0@ z&!r%H(}-kTz(Aff5sA-SVBdyk%XiqM73mRh&`N)4$pH%RM9FPNA_GSsl?JUvLAj4^ zZ3ftQpICH$2-IOF7J1@;>%Bk~E~xh!py0fego+y-8PR=Nc67o4Ij&XAzq?j3$2oc~ z(fol0m_dy%-M3qXtYy$t$D|zsJ01tGZ6tf7Q@1Q=Xb^=fM3F0&DL}etghD)h?`NUg zM~_)Hn|TXGWzyuV^&cJmdZ1a&0Wz^3c{sIuOK>PCstrgN#Syc^8!swRY`7BLrC=!6+C1>UNn@IX-#I2%b*zrloeHnGbIDy5 zPx?r9Zgn-5g_M>N&b{Iw)NzG(+{J`d@P}N~GpC9xOmsV`JQPo;%!~Q2Eq)w{%Vej! zO6{R`mD>FyO@iM2Nk5Zk^ZgdY6-yb#x^}|-pI~Cr^D3gopA#gPK~^N!`7WI9*9jn4 z9k^OWq}{>2ZzrghVYv=3XvV*%Fjy~ZJ0#@M#EG^`e-TxX?&q>yzp0rJo!B0GuPx%G zC(^S6j(u+A+2yJKQ@zWc%;8JqnX19h;+5QYwOv7ENn0-o9OYJuD(p0-WS0+7nCH(w zFvCK>@C91PuOEj!8ExK0VEsE48!oEOhH63__pbH#=}j!fn(dnI#oUQZ#$P;{syY+Z zZ_n?t+Y9~@Ie6WmeWOcuFZ_P5%b8$sXUC|sF?qrLGt<2tF5hF=7dEli5NLL0asA84 zOSN|P-RJH5yQuNmU^a;h*XD=)-j%vC3&CM*g1^2L$imPGoLMp6oQ|;roW4)Vr^5`Q zG*sp2oT=8;%i=9T`?Z|7__6hn3JN%f@1OjK*Ti1?f#_D;DH#h+mD()9x-+{jpH42! zN*%tQx|m{AW1gT`&(ttpsF^8);Zs^o(uAE61mK1jc>B06}Dm3o( z9XVXS`%OP`nD<*M52`*r3o7@ZU)D9|K=+i}EgQa)TUGL791OucRD>_x3o^gy)p1Fk z7IdZ(`>R=fnT_15!<=e0NJdkHv2TH?V6)j|xstBUU-yq570u#{Krq|0pi3lY$61xy za=Y#VquuT;Ba6{6lM}su#CrZ6fqiNCZ2b)BoMk_xc(BPHhrtW=4A#C<_jyj}XDlRMH z0ru&2d(7e1bKF-g*yWSt4&syi4nVMdJ0i^^VGIDTM=fypUIvirQ1P@~1y-X5!=s~c zMeACGpWfNHl65d#*vnh4{KxUt78q&Un}_ySN`L&gT6e8>>-paRgNUl@vSem={xCYm zS(-u|7!NeJPVT&lxDw^QOv~^vM$?^-(nzP6^2YK*$`11wN?h%;SAPiNrMARV@eEuA zfwpz}PHLz#BpFA#i(TG&z8a&YTn5yH=|S>C1&;^9BOd|YjxFp8VNWTRS|5Mljp?fv zM1_&Rz_3-(W8^5Xu9R3B-MloSEx&HKJMjJDMw?c6C@>i}7A}s3ZT$(d;07$FZ~^90 zc#$_dy)7IN&6-GVQTt%}e)=xMSq6;8HUkpN%0XOu!43BP_Dng`2lHDNbNfOC z^}~e|s~skX;kp<=k$>q*nN`Eg!yaI%!-6moTc6HYbz$@>5DR-mfcn<>%l~RHo z3=2``269Dk0p*|a0B^7jSkNIfc|1|=j}A}@MaB_=LfD5<&tQj~rKD_a5Y>P0q_$h!LyYbZrvettd4Lbt1YX}m?1uQDLXI~O z0jkx%GEFf*DePN^(q=i2chX27HOuzm4@65&fPdTv8t{8m<86D)9+UjTQSefeNq>9| zH~Z<>VH)4BhCux&gTp$C$nk=qM+P@66nRxL11+PHw?3io3aZFN!Cu>`Z$*qvCcLBQ zK3G*(k3trw!!|hSezsgue{cFQ;fv|Pr*iW)j8pSlM(gkge>wgX^8MJ`2O02*{NN8B5@Tv@(GR*MduOtr1}LvQd@{+?}Xj zqZFt(>l_J6Ecn6z1BddUc1JZZehz~u!4d!9X;XJ;T^N-HEYyq}+`y!%93YY%%HwO| zYsL+|*Odyt*Od;ZI%$}i6vE!8H~|i)!K0s#PLe(#%zwTJ%05F>#(wGGN8L;@7@$%TY57<+36NAHUl zi>v^?;oD7N(MJU2{7e%`Nv(#`A~MH(P)o<+K}^0T{wmx=q0d|piO;+cwjC#W{Th@9 zm*mzBT>`tx1wBdQxx*Viz3&T9XaFxNR$mjNJ-IFs!?HY&v`x$#!BizbMYAylWhQR~ zGR0*CZK}um^{Fp=6v-~r6a_AyeaV+zlO0sJ!!hWUab_5`n*5*7kg`UJZYKE*^|n!G zLr(o67n0d$WloA{Dl^BA+_#h7OqyVqHC7KH&px89xp|SFv`k@!TUPQs1I=P-=^kF( zBFJmUJN}8EJor_FlWQ@RPs%pLo+{gz>|}95%ku}CF?^{YAme(%XMw?4x9|1QF}y;O zi#p$7_LT;90)oii{4tq50|Qu(k{tHy>Bsf^kX)~-BTxzhJhRWzV$9=nNuv2Cm987fBO`nvL>7ZAK5G zO24Rfs&Zl@Rvut2R}_Pkf_9nk73Qx`nr46->te&|!Jsfp+VaHB#%Bm;}y z95p7d(+KUY=bK;3o)AU#uc|UvKVb;|q}(qs zcDn#aZb~*pVM;?mN!!&&&OlbDP1()@yYN`|B_lv$_r zWITzQWW7n5j9RS3lOBKQ;UbPH{s5eWZU3s&UDCOpjl2uuMa zM)Ms37DZe>qUjSUXj+kjhz6K>zz03Hj}XXX%T1zP^Q4sfUO z#(>WFP+cW24RP!$1TBFy;75UUWUjO3vfCv40uIjeX|4JxA>{A5OCW(Y8Yn-tVA}`o zI+~@xjh7FxCj(W;&OC8+0}Yy}lnN}_YYUE)#{@sn6Tae_IUxFQCI;{B6WHi+!9 zM08UQG34L~LSs+RjFoTSBi+Q|kg8e4@$zGLcY-D#@xN<-n{F;0LeTXJP;y z8GEt@*?vj}V~gy?E@dwB@G$WyDYLp9V&pj%sFh6|Uum}RKZ4K#iR8DAfWi2dgd8HB zH?n7#ND;QoKSF%3Jdap(y_K;+%u|bK<}dq^!(bW_!Vfn7V@4@M4Cuo`N|$iRJSeeU z@Q9@0q7&)n<|c9f7cRxJ#NYb}s8XfSiUkya6uPm1adHI)vQ?=G96XZmLpI-+7Yu<) zJG+#{Nj8jgQ+IyF?$I)uOe7gvnaP+&82O&t$Cf<_mC?UyA!8XKdMwZy`SRi20!0NA zvSSK-tHtxT%*zW^e4SzWb|ZvBxt2an4g{4gM%+bvtQahHST~nTNv5B zFeV(Ks|GfaTL+|-&U9}7H~EW{{1l*YhLur)xUDG@HMYj3pVHX``sWmCRWu0fG$rw^ zeol->X=hZI0#lB0J7>DvESMgxSFtP>R=ltUH8zca6<@8toV&EK)JQjazpa_*$pSwn zOLFkB_wU}Fv6}@l=B8}d=-L`zjzy4;6#c&SHkNm1%w2H2;6Q7t;E$FmIg@rQq)qq; zpk!l_xcKq6i*?WK9V#WC1%de4uM)hLYa?}m@Pe*k;ljZDJh?+mz($XcYBiU+$sr|Y zgV$|AOS-5HH@a@Fn4%`WRX?ht)u0cSEsE?Bt4gdK2?6#|rFgEH{BTutI*E=uG@S7r z(!2k2;8hd-GI7Z@fY?kAAvULb6PeiVdEB!PSxki!7Ytg63v_6r`^G+zQ_@NzfXGzP zKfHZrKAU}0Q}7cE+#I7M3AofdbO0>!mh@ojN;DbC{V?!~pZ zJ1kNtw&=pL`}X(!^PTt3NhX;jckaE(NiseY-nPU^&O2_Y&NFVl0!E^nf0I2A-O>Ym zV*>(HwqADPp@? z$<|QM?oN7!amdIDxbFbu%U2W-eDS|VP5K=pqd4((sj)IVc`!l51c;l!O9=+mNDt7B z4X_RQ1RK4lhRB>-gFEIdrZJMB+1}KU=W`$Mcf_C28TH{sKQ!ERw-+9r=o^CIWW9F^ z5(SlT8d%P-A?mV;(0qg#s~#@I1b4$y?!{2(?K67^*A|WMRus+b<`Ahi=S~KYk0k*} z#!~tS?wT{tIxgY#eD2~8FOs4AD>$GipiPgAL?t5=Jb*@g=QY@u1e~9elMMeD#bi#* zC~3r!%#COc=h9%#C!i6iAsz?d?v6=f?~Y3b`>p4AaYAgdN~*2K`{c@JL=e-(H7!fb zXMEphR>K5RW}=5AfBxY6{TOuP26D6lu{g?uNl}ErJD6i2oWjB776ypAvoLsO0}RSX zsG?v-=}_fRt(8X5&f7&m4Pvh0b-H781ce~Df$Z1{MJ%WaFCC%7TV;bJhZutIc)&OO z43G%~g_89NP)xyKJObr*76R9;UxD&v#hvFU)1g$MBXt(zeX^|I;t&MyO^OoLofXIX z%vHw(=|Vx-_{sWMefVI{D|_>4Y0k@1irX!Rr0>%L)R*qgU$>d&_WBX1K;DBQAn!r6 zysQQxqQh{2f2$0)^NA=Mj1b~-ToviId8g*KxgOEBbQI|3T<#FD9_PEVG=i2WAP4M8 z0nUK<0CYeml;Fh8C+(9&nWQ32+c6KSh@fXWoD7eht=8rAB^^;SV?v0H4OJxI!*}3J zR^J0DFyvkeLG9-s$sO9UWY;J}${3RI702nMx=kZyb4r?c1bS>godlncW`Sd$+EeK@ zf^;eDE|k&($U+tBNDzY5HU}xVcYdFX<8RpHEHYwv$RB)fkjg+-81*1yi@M%w!f`<- zMEQT(N+)hXnV)(SxfLb4xYZ>%{LW2Al4qi38?8AZVpytX%!u)Yq_ct4y$eA% z$!6{UPL+G>HuWLB?&f8njC+LiYlofG#7#W@+zc99v1d!Ei^k7Lj|Pbwxr01fK;yQG zb>hl~U|&L`j#Q@0@Y&`P1fL}IwL%6~)ZFfYu+9l@V2 zAS3SJGxjH#ORx$oAXpn*Mr;7y3b=wTzfKBpq>JxvOHk=$7TGl5OoneqFkW5^rp&~c zMUZ*S^f5FC@jgH~WIV_a@9*B&@GFQO4J%}zn4F8wj}h{{#~MuFzmb%0X$2?~oRs(! z_W&S^`L|IHd6slIgb|2rntc5ZSDfB~YvMcrX3xeLE?@*QROH_t0EAg>pSs~8C*|_5 zp1050{0R_4%n1$j4tp8(VNurrykz_xV^l-$Sxi3&N*`2xU*<$0N@GpuEpoVPQIC|T;VR)%;9L`g?>R-9Ldz(!Bka$u`?_|Y53 z(FAeqKE4NTfZ|nf!r@n9W#YQ()*TG-vQ5}+it`D(E9=P*^>UPSY!PY_!1 zCBGd99S+*2wFTgj+Bjq5?`8qUHe$0y{d zfnoUwUtr1NzvC~7rQqxk^`?|vb#7tb<>|c{ocj+=m6tBKskk?CODh6LI{7;WdV%aB zi}@^-d8@y)w^o1YZ~ZW{K5nSYCSvDH&=sczPTim_MeQ|pM4#eiX)pQDFjcDa15Z;_ z8&4Tk;%g<|Gp9f7Dfj?vYsdT@8RH+o3Lh>zPG+4Ds2pSb_k0DhOP|}y4;hDB1i;rG zhTEt_6a4y!&(_W#O`jD9Sznev^xZ3zNe>Q1V%#&lBCu8SCyBnKgUdMR@#^)%5yYPZ zItN^%7KLq#0bGAd`srp4UMrRlQ6qT8j-yB@QJ|>t-p-);zGU{F{GKuBnsTP3zwdfr zxfPs#XVYx+(KYe_(7N^LI+g$rpLkS^cxpNy0U(lA36hLR`laPg#{oiLO&=jF2AQ@o z2Xu3`O(Zn0jg=1oWOI9VxTkJH3dX*>IB^4nvmUD73A2fx z-e{4@)Mc-Y*fnXz)=U<#7VUAkbFK`m=q-;!o^ z=tQW;og_`?F1%$cx3=u%x)SXc)rj9at_T?Y${mxXK%V2gtfaX8!`C$fI0fg!7j-4z z(h>X%a@($_R|u3y0$vfa(znn)10bZ7hc0S&J^@3E9ZSQ1po`S2%$|k3c8o9`4 z=`9+|C-04NxHufIJ2_?R6345#s))e(Q>TsiK))1p(@2ZV@o27&Q%%!lb%xGgUga^G zEN8C^l@qvBJ*&CCJMKdTJ}(~1sH$bi@>`pF7lha(mrp{oa=3^c!JVZdT)=^6ECYYNK;(8E22vO`_^_Fw9v zydV044le;Om78#x2rA-#7l4;HvZX%;s^qV;6P9Z41U6Ek$cQ}%}5^Q@zi!V_`m+-2FmVS7xs%f zx}nt7{>JG>>1jSN{JFxg=q<)^g%L^_OOD1IemKH`A_$3l)%}f{$R~*m-K-!@os_sm z?Xot0sS#AOL=Qgw#sFDa5(Yn1PJ-$R2Y(~ff(jO8^hhl`T$61ZLDwNL& zA=tfY9yR)%7BZ$jYfkz;ev)xl{4~XtWR2%DAr^q&q%mZ{I9)?8LviWqCa$57OTX${ zCv_e3StB{kz({l_XT`|h4>go^lV5Mabd#qaP=S6{hNd=*w>JjT? zX~GTcW;I_KSb!ISzpBBFY^{oZR-kWR_PyF*9MyVBr7mdY!WSM|cAvBoBUs5})>N%U#c|4L*vQ=E+b6#rSxK$MJRl-G?2T_lC*ciV~ zyDrtgUamrU@s}`4t#>DdK@!*}Zs<y*%_pBKjI^u3S4s7;7)+csNo92dS#{z*xuvCyys5RO0i5KEKH0tit)B1V z2G&7CTY2h4X>2-~+RXmehM6;Ih?`2HnfaV1WX+|!k8syAE7CHsu6>)DIKb{f1FR>J zeq)JnRAKdK645;xVJ%dNqAj2LF77k|nqH9AJ1Q3@hjk2-3;F#44<2!FbazQt%dv05 zvos6kT-n`UZ`H9Xd)6?R?Zm%kS~pHgu?v(pE95^Zp&C|dCIa3U8AP?dUGzzPOT=3( zq?B~x&d)x#{FOly(LJkTVgU4I?d6O^%#Si)K6h!)3cVYqPUhvK^>blQ0o^dd4ClP>cVYOAw^%t@sNOBU? zhP5#Gcwz#SP#}BjmIOU*q=s}@`+}Qhs|MZDp#h;;xVGcmGUcakh}RS9(k|0$&!iM| z%_C#B^=4e~j5DQfP1uyK9lpZtDd=UHCYXV2WHu;wI5^=p@tu>AW9JnOjDnpVuKY#~ z^1<5YuxIw{0ay(_Boq-y)$K)*$&Pq6K|I(Yo4MeHUKt>d;19EDG$B+phlA%|N8x2S zTYx(u^snS(SH0;kt<~q6%ZMG><;9JCN*HFc;7YvHws-2t^M>qDcMbdWp|hI3uxrXJ z@DCxc>x;67@5j`m8?rF7k_b11{LBg`ym+p|GE52WCK??5th9ZaR^}U{%HaFlf_hY= zfGO-+BFf|FqtEOMZ;yTOgnbJOArbECu9WW;@{WR9+cArp#W#kicr09Ih}V7se=HEs z&fiBQD;@g6D_Lu15~O419<&XQyn!R|KKR(-@ohptkXpgY>~E)_KMY@~r;d(MWTZpF zpoFsdzY&R`(~BS-LXq1$c8V9S%_%n@8)$@UEj={0)p4Gc0P;^E=a>Bj|AbR@cz4+S zEuZz?l)frydnuv#l-ZSK<`!nzTF7H;bMZ$r&bzwtIY=rfM9x|e+RxT?f@!$+=44m= zla9b&OtrODIv;C7Pt*yBHZ(nzQ1RnbYs<5o^P&;FEpP8m7jjh{M0 z`W_X2U1mmw*2wUWGwirePBV0=M2P4pJz4%&EWQ`9JP}6fzFZ{Ra13FbnIuyq0|gO# z;#f3cOd`7`;ZF*QH|3Fp=HbzVf{BrYv{FHr0#cBkjUROZ^9(V_sPC)D&3u@as;K&v zf+c_CE&CR*V#7XBF^=1*8@`6pY8!R@EPR)gm-B8*%=fbh!iM*=`kl|Wj8e)mrPr~u zH^{;7hOWR!^2zP_nO04q>7CTwojC<|osQbcosEps<{5D++ z_HBl%Hs77Ci#A9Pa3_H-*#X_1aabrqGuyc(Zm~BGUMT&HZJ51&;#O5xu3GUrM*v;v zYQ4Q^Wv;hcAdGN*F=J^26$lALcaF4BGMCSeq^l5+|IBS-)U4pVSabO8#`p{X<@ii$ z+{EC?hVMVNrBCv^PKZS&WWN!suVAo6q7Z91#)F}q>{xy*&J6RZo}rzwxs`xJ207H` zj!5opIO7v5!>%xnJnJXKfNM#k31^igehS(j+J8@gIX+}r@P_Ub!rKy{LF&uWmoEly zaDpPBujSq}1&)qCHvHl|h_Uo}$$vZH(?G#;uxHsFi#b~bK;byh4E1BK|BxqDE*t!o zk@r?j<7>5c^n_2NVA8d}WNJfuKBtwLq)l$tBsoDN0UXA=Q@3Rg%UB@*+5eM;i*VmQ zNncJ*1LVKK&YT6|q=(0Sg%i*S{QP}{3cC;r)&EboekLu3(5icKonYd0FhjA z`ryX0Vm^41S-XlulyBy&)}P7Qbm-k6RdN=< z=sk|#n9-3;#L~CI^tu=~qiv1>M_;N_d+h^*_jGUhIOCT-;L&U66fp7rBP+A}%lLkT zc65G5+b9vSj@^`~2wu#^Xv#JGWCNRr0?vrjcJK-$8D!2~!rpi6`= zs{*I81RlLIr!tI9=34_ore9^$qnGSNV}ehqj43|9g?ysR4A9Sq-`7na-&pVm7-ESC zZE!mHlo6KgL1Y@k73ZW~$@opQBNyDv=*aVTF8?%N#Q z?C$FfGBFqKxjHP?ml{b>Ojdq`RSW{W9&r0=_pOBSh>Pi}p>KZ}^@@%F@KEYNOY=1i z^Y1vur`M`I*|AbRvWBXgImfI^%%p`mmge@NHK&vsf=%?mB%r2b;-33AYt`)x&Ff<} zOms3IV`cOr_jrnu)1HUz^uQ@*?bWW~hK3s|eOYuOk4zFeti>$(Fv+y|VN((h()qDb!r5yY^@a)KLb8%CQv}m8_G+?V&(@EU z@pk-g=6-&0xxSm&vlT`P$qK5&bX84rb90Ois$=0=(C=IE$nFT)Opbk7QeEx#oWv}? z8Hx0@&rJZp*z&W zVofVsApU2QalG8*RFhV44J#W3c93#!aY;B3XRsqb<5?2WHF}fZyLIq)cSD@k!7ns) zpXl|WE_y(?wr&x=u;KBL7|8g!FulVWjxd4yLQPbKNNnY2*iP`N;Hdb#q^xa z^+MPBDh?Ct&L@TwOzEHZU#(r>KdXZXh4!W=w$@17LD6J~_g;sf+DrCHQ&gH-g(!Ex z@x*xUN?L{}x!-xhelI*`HjDM*xeseP_BDt(e6Wb5L;RIy2M$6x-|hS<$h-j7o!7W}OgH+;aI&rL0+N5(S#g*EqD!LIE?v;ypp*j%Q zcP;sq+q$zRKa9ihKb?0ZcTOqU3r*i#3}<#`_e#|k7d{G>*}^uv^IT56hXBh4d`2*vh{z8Pv)cW$*7A_I~#%wwbj4xPd zceW#EI2%6V^`^9Sy5l~%#tBO+S;Q;fi3)q`JlvnQC)v*YNog?TBA+hC$NzS5vD95U z+(t+4=E3F4P0=2pEHZHnXn|1{VOa9!RiaDZm4)UW>`cKgEu9k&E4wvu{J1KUG$v+G zq9T(QicA0C?V`fdz0G)A9PoV*+e_eOXLU1rbK=u%%7BTXTsU$T|A5Cy>S@l@ylglz29jLFa1}( z!5(_f5FR?VqtC_`43LU7Vet5xH8^B;uZ$-tK+Zcp;P{^y1pnlab|m>Sde-*6vIG{8 z#R$O*b{FW5<$INY{OzzI4K3q454G=8)T_^b1UNWUo@3mzc)1S;>5A)qe$kA)$ed&A z;=g0;@Dwg?y_z5lo02#8L*_JLbR26^av@&V$o8=VtYWg;Y9v@ zAsUYQa3OyS776DBI~i$(eBkfGXv?H*qf}x}2R5<<9HJP8V0qc$eV!H#Y%+&oOZpZc zoyfK;ea__aVZ+~rMV3Gy*#>$HZL~b9LxVx{7&0>gls5brETG}uy_0Vg!ev%~+UU21@ zRq6;oBY>)^LixmpbM z)F5Kw{D1qh+d<7)H(hC=g6bZ-gVlzFQGu$3)L~e5*?PiQBd8Mmby|0eL-y>7_P2X! z#-mxr5fJ+#yphmSg`(f(uTBUl7RBnVy>xZKt+ERW?*mb)DhfS+U$5_e%Iz>}7jCqS zQ_}q}z^ZaZ$1Sud&F*s-|p3NsNDL2 zQPbcyy^C7poQTV8W)XGUuV$N%gdc>RVtFYC;*3%Pts)gJ%R?*_RGZU%);Vd~UcKm3 z%Au&lj7ix?&r!7a8G1!JE2Cwbk&$&0T1C!>y+aFgko9EFm8L8HFMbr^CcTYWEWs{l;IsZEhQK5yF+Gq7J3U`y&%(zwb#u`=SwNNLeTkfRxD^;1T@-0A z6F~d(C9a5%XYjKdB5aVb#hFOTsug|f%Aj#6P({^$*r5;w?;It2$$|4SqZiqhrM^y& z&LuuEX+(j-U>#q?1I<;vi==@6nF^2Bs7P3PB=Kk4T=^bR$Gpk6Yk}tNs*T1;p zCwBPoBL_Er5EgNb;7VDAY*(KnLI#U)e`m3thTxTxoOeyYaH*;Y23mebg@5yrh+6q; zp;u3%$J~wLZAV@+_xCT_W&4(ZEpHRv3jR$q7p31DrT2#mznWgcC<`rcqT)twB5z-x zH}|yAH@IO5A4HVNX`|K0EdQ34!q_rfKe&@kgDSKpaGGVl`qK&|{k%h~{L2ixujIE~8MSM)&zUjQiio zz)VH%`xOyl-i-5qBQa6cBo?oaZVEYnj(zzVC9A%M{c=DiViWQv{&h-i!t%E+HUTke zncq<2(wOU%e#YM=znKkOEN zhoS~kc{{?jPKXoCURsJ}O0jk=x%Qh~y^28TanuLlKrXo^kBYwG(q}_%DWLE|)wOHT z^oDE{KBiC1)O7f-9G$Zyil{o9m9QiPSP-#<)sOWnq2$5TM; z==+wj7~@?_7U!7FT{M?%OFizOtnJQ|RXdxS*9Ew8-s1C|dUU~EqiL#E20F=SvVRYY zg460WF)0V5`oh@JR$kX!jTL0LdXk2hv6^BTMy;{8{$fs3s~f~Y+vKlC`kRdv_t|VK z^dkoi@l9TBAWh~TOtq5mq@r!Sm9FAd%-i;h$l;H;7ekm1tR1h8NZRi|U{azm)qDud zDESjx&Aue$11Lya>O|i*4+rC>3L6>M*lTpkRJdzaypsBhT!NXZTfm82LODTK@DJG< zB{fRp9^7VBCe=}iHx%NoX&i}pn2Z#OXDg>VCF9%hap))b zD|uCmU@B&pQB8%$#hFbD*H8EF->bX`QvE}!mpL>pPAyt`gWbC@_O10RYSCoHSh>Tc z#8_v-jB03r0Tp3B8WK1mJ{k^;)yw=ElwPykta#!;a}5P~lo@JvT|1Ji&_+i-1R$2s zMz;1xy(O54=OrYV@a}m_o9Y}lE6Hk{T>#K{$^s(5kPN)Qi45~)vOZeQBh0cj`%{3wN(Ws+QEhH}aJN34D64(s|Q zBbZj(AN*ic*kCw<<6ERDMM%iGm?+oqXx8q<*UNZ#DX8&r_D zHI|3WEBgr|N0D4v&x%o8S+5lhwSZ1M_)pl@5!bWotj6j;f@FLH3YVUUCKywy^&Z3$ zN5>P%S+B`gC}leQ*>8y_Xch|cHC%G>Uqam%a8FoLt88Q>fhH0-PuPdaYX}nLe|`oX z=~f&~hXu(9`_m#?ym?d}&`>i)LbRBdL9`$yla>ThcTpfZ4Jpw(BVu*rjPTLWGeEvJ z0h(Z67u*`5Ul&xz04mTXjvT9(u~-vo!eG)Jt~7|dgnEX!k9k{20l2xBw zU|Qt~*%(eLT`P(?>y8U*uZF}1egN=x9s4ENeSwQ2Lc?VZ?k3kI#;Q$f_lM+6pzQvqayeS|~%_9d&YFzZ8z5H~au|81`N^>|SJt0WN ztNZN}0^al5MYo3S0O<_2gM*|D;R3g)6Qkb?=rq*YF%OU@8Xkaj=wEWgLW89KK4U$8 z$;1dK5QCu|5wHqMganZk+_*^F!=@L@YS^`1A5?Y@$MYEZiHBnb(_6xpjHF7s3G0xv zmqs0GLVvVX`D@ukKMx!s->;$hcZ0_;+GBn^gq#!5`3ImlwbATyd=X{aT^&WOcSh|- zMR7Jc{z{3+RfBWzovw9d7MApv`{@O5!|+639GX~F`W{X+ZXJh3|YkYWkx;nlf2*~(bCxM%uxM!ZfP3bK{0FSS$Z<;zmdx9 z3lYCSKa9W^Gaoub2`Ve<34EIxA#yFWM{wvagqb!bcgq;0C@ z4}&>ZtH_rw%&V8jD7(+ks|Pb~ajPq|Gp~rfl_dRxhLIC4T@zN2Og{G0KA?9WN?{(* zU9m^q`$+cV{H9b+`;4Fup#4e*K;KP3%oGzzG8^;-da&*lDGnpM3X?s*7OPjF`52oI z_zWYWbIH%|Y;O(YHt8~^joMmGOAPfLgXIP?|shHf`dVeLQ^i9qRFBA`Dz z)*AsQ;}H7>vR$R?36uLj`aZR^)geDZ8t{)K``cnY5Et0Se@8$}_4-j3BwC5N-_Qt0 z_~-vlSE;p*x+0lTAihXw(D%Cl^@e;waQ`|SzdBc6SZK!5AKgx{-{>hf<))sySuoSS z&Wd%{tm)sVuVnwtls69?73S3XBJu@&zg}VH{7c@@J@IUiZ|#lm<(uY}JlDRyYPUEo zr=Kr=r9!)g?_q~>nhCESk+@8{QWeGe zmppWiEDVzyD|G|{1A#Q~snRh~lIs@y5y@Ts<+TlUseqoqJUygd>kvdgk5h5?2M~S%*sj2F zS{#VdOulm%kmLH}{-2ww{%>j8PcUFlKkf0s{rl#s`}KruF}G|as_6IO4u@@`u`f(TA&AFQ+C9rSoG2))#m}Uz8 z10J|7v5nk+XMlSUL?BO-BlKlO`@9yOaI^8=z{d;cI8s(VwF2ll5(fadv zthevx?mE13>FI>SyAs|5dwAM81JuEaf-WW-O-?s@=M%omuO%bR}Se}n?(it zSq(pOVrxC!ff)wfv@zKaX#)i)&KTfFB_`+4+Z+|Sa?Oeg_C1y;=2iOle&JG#B41Rl zn8fGg&Sbt^Vtj-oT(GX9Ez^hF-Om-f`t6Bb(Z;{uxOT}_cPeqVY-l;wxUQ4Y!d;W3 zGS~Z7WKA|ca^#wgsl?;_*f?$8Ixk?pX10HAZX|zj z%}|xx5c=@x@(>ExoqRKZKm77Q4lRRDXYg-QU+&yfazQr!^s#S1hI8*V7L{-5-amFJ zBTCWF0}6K$WmNo?V1+#f`OJ?Sh&s*i61XMlbkFpTcTf!jgheT;v1J45+`WwTY|dC? zz6>ggx6b{yqvt{bbw11INbZA6K3*noZ$PPzG9Hi1V6?)zo0Hm+ZRUqQs#Z4%M3nNz zF8_Gy*wD)lu`%!eoxzd*clOmxN)7*)k4Q-EiAYG)|3CYxkDHaNv%SmzNx&Ky7(lYr z0e4DfijndDS-??P+qqgmNQ{|l*e4!xC=j>MX}e9jSW%A`qt>kI&xDvWB_o(dUF$hK%T(0zWhAZPdf%s8fVM7f@~kKlY?~nt0ZjN zvywlZ`N~I9rkg$_btd31WcXmm=eNcm<~E)`-wskxMp<@uM#kw#Ha~W9Jd|;VF3=i3 z!xUDQP64DliTuzes(ces1KR5W4U54@gMay&yo`8zRop8lbgGX8K0Qt?L;Lh`^V1E0$?!p51nts^L(hH&|TKN>+Y=)acxFmK3guQ zc;4sU&}07+Z20%u<4NWKkx_Hr5;%X^8)nE_&IsPDx)R0-3tiSc+<-g(vss&XAMc*5 zk0c}p8!le9^J~5@a{hcu%VP866#q ztCo$O^}Vs4j@i9kl!n^_IM`pAma{5dsnUHSrm{-8GOX)zoa0x=l)0u3=cZ|McX>hK z>F?m=;X~`RB_{MG0%dgT$+xkO=*k?l?>V@+Q8buIr)Fj{g&7kr+%NAA^>ua9bv`GM z6zel)M9=pL13Q>>m#j%AFWwv7e;MD&Fpw7Mnt4xp@+C~#VEHi>=lJ#m7Kin zbme}LcD$A4&6UM+{l8q#Q zOy+_4LNGXbcwj!cq?rGcSJb;yhwk~)Bd@B99phryYnYoAu;6SI@+wcGBHI$Szjzw5BH->bk2$CQfae8TZPUV}s8J~VV@BN~6>O9x=xk7stDcTl3mxh!@h&M_FYZ?iSg)>oHoJ7Y! z5{~T8`=TYqv!rU*4WOdq&UsX8EzDxcwR20%-MI&N%$xk5 zi_TPhC`VVaubkptdG=bUckBjMJi6yXKcXr@^fGUc4^3SMR#qJM@R+-kClys5DYH|SM2xHnk2+%hC{oAwyg#nFX!y)-mGl2agn+9$rs(I`n(^-QT0I`&G>c5 z;^r`5Y8F>3csxl{ABkND@+rgqeu8ft$l3dia%MF2?1g9eKQNtxU)P4~AwoSPu$N0s zuG+p$aHHDJqs@3TEp!BzS`;-%JwOlRGnyF{7CYUjxN;{N8! z2Q|;KpBUFC3t63S_YKCTO@9;KqXsG$<<%svPcL%c=8qY{-9cO*ATpgxe+I?XJ5)3K z{&S4(ksnkqeMYD*CY?0`yS%U-WWi;)2x^{F{|eRB97aG0a^JuYSH4|+$}0ub3%*?< z)SxtE1jA#6>(hnYxBlZh#Z?1F_recztA0|{pc>>$-rbcqS7xXeA9jNmBg|cYIAd(K zt9ejdQQ@^O{OH1KpZt;VahDAP%8Z(4)8A*?{}IxL zeYtmy?H(@)bIROO{{SPKiVd(TJ&d~uQEpIoeBc{NDR4t~Y@iH>sFZ#qZDzn6LBT;^ z00*FEL0M>O&)Jh4o9@0&71emf>x8$rO-I`nJoc#e&C#`+BCn3&FTvio zsLbg})BCgTRP!N*iRE)7aR;!#mHFHw0i9EW%Rr6wPusotBdupIFO@#wv8!hsKJ;Q< zLRYh5`>arC9V(-`%l4k8*(ggNr9V2-8aE4az8u4} zOyLDPcE)5(P<_qqsv~`_0xdzU8Aq4mCs(q=unRR!uJ0T{9jjXhqo&-a9P+6%(O|9_ z$7)7n2vMgm+Hgu4KcvnDv^DmxkR;C2<>H3Tg)BZ=as2)$+yD8iR!B= z3_-q<`%z%tmY+6`b=8r;Fc&_hj*UG6(yrW~&5l`iwrRYn=zsMd8`4iLDM2*F=4Iv! zIt;4%JgSahP?Im%W$lvoD5LEe^j*x@GgmQYZ|#%0L@`u2@79@KmCje!3V?jquV77c zH#wSWZdh(N{f>8r%ILsM)hOb0vjwH%IgS-Tk<1#QJz<5dGohzsK|;!pEtWSXN?00P zbj!k2W)6GhORPo}v?VZemg@0PX4a9R#I5!Brs5W{gav)AAEGnbtCjohSe{${Pn^M( zh{f?R3hxB}cbdV$lx#>jUQe~Wk=&ZCRgHMSZyKb5qT|=nF@m>4b8$1$n;QKXQ7Wno zlkfJN?iwVPR(Lc9g$>|iDe9o{OQ39C2Z4w+7IKxlAO;J6Cq?HGj}$N8WDoDxzIk6e^uluHHYCORRnYk%e`zdA#ckhXd_{PQgvP;Vw|OV-3t zjR$?Ocm7fbC)cxhX&J1=)tk)!io1&MoF&)vd0>s@I+eL$?%m#LO8LUOJl@a6C(cns z&0pqr`5*Z)2psBmeJEgh#D7nhS$7s_ZaxYB2K(;lYlOZkC@)sML@{X{y}#=-0ny9}xhFr+_0M>F z8QRbxn--pyo-PpSz)>Jo7r2nv8R?n!3nJ~+Sl(K#h9)~$-Cr5s{O;A?q;*mB=Z{G? z(Pi+PshhDfF^?P2gSdwncq4C30)ohH7}PYx5??G-j#(|mAJMeDiO)T?9pg}u(BJx5 z*74)4EkG%qD@Rv$s!ol!)RRVN{uf#P+u}>Y{8fdhF=reyUot;3--`zlKG`6yalvm8 zDtQVHo&F48_Y8HtZm_+k%iV-a+x#1K`$a{RxJauqAxCw2(t3t$so#)qgNDl+<5LVU zKU#B_))oDRJfGexAe+f!8*6lTnZ&%^G?~Nsw!l$Mlm$1X^mfR%Ie{?tBHh#;mr*jK z(_+O`z)Kbj`@o_|EL+n%Q!#-53-)sU+oBRu86!VR91L}&D~(wG-Ig0CJ4u0h^0^@EH5s%mDMUe*0B|)RJtwwiu3F%fON>9AG?!Nw>{bk>Ki&~8xc!!CiTyC~42J{+4ck5HoWZI5kS9sK3ruF?05XQx~2HSYb zavJ=mJM3xpIdeclST>a{i^n<~$`o^$;(v(N+!to^;Jl@-EzxAK;b|$C*cg6IxIvrO zK@qXY;2{TbWT!lQlNGaW=Avy=2swHC>dv{&7?qL%wReoohb8G31Z#@_N zqaz2pI-|To21DE7->7PI+oClmkRPXn^f0c^oD=>~mEQstxFibrhK==KTT1y?4*ObN zGL=RAK34GREOlgc=vZnXp(cB;wY|ysBkLP$mbNo%N9MWr&5c@0EmIP9cdE>Dx4B!F zBdxZyEoF*93DH58_%|~7LXGkKl)?fZR0+z5-e)aD8aL?Pl$~fDmGIor63dz~j|T9w zLKx0qUo|ama?Lh7#&(pi2g*w{1;?a5wlaq4)n%WzAy4Rys29)ojU-2?-bdd#eBnG) z`?2}cJ=rpxOvinWzEg7UAQ8%wJX2Pv&$NO&~odEUyJ^yamUdy+Td3zGSma`qjq z+R1>mj-ls*ct^O`R?QDB1Y?1g>Mtl_v8u+2Ma3!ik9UDlnJ8cKlx8b8Lz?4Y>$9?e z*>1z}_(sVinRK}++on_9#&621_4M9@v`GUF8g0tA?1Mc%V>UY=;!}p7GJrK zmKf0Oq`dVsG7E+Fq4}|H|1R=)-?AfT4Lm{h9{H@%bRsy|QsckDcV% ziZ$D@P^u&AWX@Nj<%pw*owu^3fe#-8B9qLj{6CG&nZ{VJs(#Xri;%ch`AcoQ5<&ql zPVNa}$VRR%dOJ^<@I3kk5L~%2i{Ub(5u(*-0BB5FK2Fwc&?MD7NW2!dhg%mvPf9#v zt_5(i@~w64Zm5^<(0BCQt9nCYtm?U~&Z561biFs`jfinbDUojk_DI%BzG>$a*Xl#n zZeL%X!hHun%}A3XZ!N^4(A zul3bU_mNbFIOi_Tom*hQv0>x7ARi<22T@5{k}w-aT&UwPL8Y!w z9p!l}sC~=~)Y;@=H}RP;CjLcU$N3bza^USxBmU|K3=ScC&0<5(yO%iYB$FVj$q~kB z>EU9^g5sH# z-VweIO+P(n;+3V|bSdCu@g-1tRjiXhd$Nhxj#l-{joluRL!CD5T~F<6K0Rfcn75vN zvf5EKGeZKS_vYCj5GJ_zuAJA>TXEbVFGc>c=U2Q3ANc}=zRu>2wnb}BR`hNJDjht_ z4%W!$NHR`5NbSB$kbLS1wu9#SA(ntExH~fJlB=J<{0ALHj%0x5@3)7jJAy)h+aVl~ z4D&nqz^J|Bjoyp-cbkshDXrvxmj^z^hw!SQY~S9JCMEfrG`UT^7Gs1gdI`l{35WzluuXpd3y#ZqrDYfhy$ZU|uK`IFmK)AcJ zgWR9E+T&l6IMp?Iv;B9VxTAGDnG$i^GojRIQbjeLXngsUm^<70Xk4amfFp4o@2?^Z zNeMS0lf_IDSueeKCfMENLSKKDmI372x7x_>7AYbsj54!L7l_tbe%6MrcOtR%C}9{CHM8BQfA z!GNn5X-r(}gT;k6Q|>sWx06P7I$DnUF5SaQi}uHqF?P0&%+UB*eLtrbT^btV^Lq6=FgJ zD4X3Qi7yRprO=k1f5bMDcs4$JZHKK$p!oammxOJ0bvmhSqQ~n*T-&(tS|$4E%`{Y@ zK-(w$8JpH$_{QqbCIe)xyLh%VcT!F_M9JmTr?;9>gUjLtCr0O{-g0+S&asN3_s)7~ z315p6HqK6vAbms8$&H4_;@oIJnY>0jg`=LkcSXs&n(Zlg zL)x#LS$X1+qTN$0&q|C!0huv-RQyO_>ES=#oU7|?Vb1*eM(r#jM!2e8F+jGT)Z|)_ zoL~6ANcpao23w}S2Nqd-L)==i@LeV^JZ8A|3rl)zI%$Ra{jE|$H*0{ti=rcuU_Q^S zISwMD+0Fikm=oVX>nvt&yb@C(ik}x5vAuafxNxRFj_aeki*fKW=6K!hYBGlrHuc76gxI4*B^OsMi$0;PY+*I$=*}fu?NdrI4uyS8 zZt49W3n$0^d0X5T2HeOcY9 z$eo|V(|z+r;=M-6^w!Mj&1h@~qP|QK#l(cwLuIctPdM1MW&2fXO=#DUMz|#tPpTJs z)BsfGVe(s5&e^-w%0F{j{&oIEtwuK`bhE9v^+*3h*IPx!(RAU$BzSOl3-;pfGDz^? z?(XhBI3#F-y9Eg(KycUKF2Q96cNm;u1~_~d|61o<{Hrgjt9w^%>8@I}x@$jAao~Re zS+x6yW2h3wJ2A)HT6{-)#ba`{p_GfaJOK~t4NVPZ`dE`=dCllCPIH4Syhih#7yRZ}#8ogQ>{MPAC*e{ZkxTdb zvzFUvnn3ff>my6UOZprx>kjV{ikq`GsB=+e7vhbK2!6E+(%?77Ju=hr*tzQrIDi~g zCZxq$M`=_X_gisqESRMJb+d21s8_ux@#;_($dXUqAqMMa?&IfTy%>Br|K5JR=UrD> z-_Nt8>isp7t{_iX;@a)E{C;bObrJ7n6lGpY*)NGE=Wd)OL^f~U1AtXtY-+-t6T^TW zqzr?~RrvUe79f577bQIX;Qj(9J5&SPxak;7Rg8g|k@9-@2k}rGI$`L8`OKl4#k}H7 zvL7f|7aGJ68fV@Tyw8M_S3a=t@5!%y)B2&=vh>^U{u=D|uX+uF9j%TJ=srcon>KsBrl-61Pi$Zb3MwP5&3lQ7O?!#s zoEhGfH6qr{s+^VbCb`d>Y&3U2>F50$<_gn3K9l)nwqBhVL6SBv%=2LG%AFFnGoG+e zSMyb>rM%i9*3H6>#TULcb}={qL<)?-!j5%nI3fb9>x)G5>PutTT=r6cRYjNQ#>;NY z?tP5$J1l@FR()=Z;d$fwxp&Lj;YTPFn!$saEV{2w;CbDlXf|h_(&d%${=I3zwVsHV zNfT&;?#D-0+*j@uwv|dDL%&hHKvd64&m!AC9&=MW46P9bYDYERG$8_tO|q_iVXC z6I*Ii(g?*&(o(($uebXDj5xE5fHz(E0k`h36UoIctteQogBzQoq;3^p6F6dLb*;s1 zW)CZ-=A-e?LmM_cw*m2_)OmQ3LS3i@rODGwVl#n!!DqhiT5Mfg z4KY3sW9BLDme1Vk7vFks&qNjNGp!{QA5?Cu!0W`#906;QNU=(?+gU-8)pE*r$*()Z zUJu-cS1abXR{V{Lmf9a~KbO^iVZdTO?wbZv5DNp{=c{Xx+kM>cCd!swM4O)o7h05Y zIHYtTaR-(M>gY))$=~c_BAACMC+N_}4m3em6L>6zvA?^TgHu=-S;H7+&+2>G`|lmb ze6dniaqcFq&tokKBu0x*e6{Mxdovc_fD^IQ6|c`k+5OK?Can;ik#Z_~(hz^{VoBtG zo(YS_4<)rQgZ?}L(^nd<9cy;Gkx+!0L2u@-l%JGv;0%IC$`b!*Z4l{uZn@Ic?|(X( zjh?Dq*%nSkt$xN6xc94hzNWHLL>i_%vb;Fqk%TgIeEze1{b&7$+kz5^>20gWP3B)OwlN4WN(mR`!Wv#7x zHa;h~LS;!U4!AzTEMjd%)*6s4XUr7^_U%M78hVqRz=)Jo3k2e)1}`9L7*ky^;nSuc zrk8rf@_ESPvhgUWY|t%weZIRz`XZj`6=c7HZmf^h-X4k`w8!N@?e)=8*`0xrrWBas z!{JW5KUq<&WJZ=0`^mzKuhninY_62Q$abN8A!>>I^sB-_A%s#ORKc#Z`+{}PVpLdm zLip3U&FiYrt&Tw+e?iKqiGX&{b zfZ`wjY5}rKKV%lcd6^eUvCr2UAEzQqoM8vj=k@PMGr;!5$3ZwDF4e}1LV5+;i3MjX zgQweS0shQ1G}J9Y$M%U|O5WE1c$pDQv8nPT6ehKc4@A#0g3q}GkC}m_%$I*D*TU{O z@5CoOdB2XVWVbFqQ&7-%|FmJ&5)}PH`jLGMeU@7m)Gx?KaEm3qclBrm!S>cq!lBGsF z@YGmWE`eD?fGifEUGSNgGo z(iYQZQQ;vVzNB*$(34m*$yHadfN*bCm@{hw?r_2CymioK>FQQN=TVWlKG+x}Ub; z-h(3^=^H!%QcFjz=hqS%2j=p(wIl_>J{vGJxCAC)9i(_l$T%{;7J9L>(D4m;z9x(E ziu+vnN-_UdJAI9KQVQz@vP^mNnM;=WR!TE%-kt+sTbI<^ zh&H`*^_VXBaZHXan$U=@O!^peA#hsmgs(!qC@1Q8cmB40zw{1|Wf}8#R-2@nX7Fq= zsisGkyn2@1P2BEY>yIFbz~o!e$$N7g^1H~tIoZr_ZgG?!RF0l$G;AB23XoJCFf3{* zHuGz7L=`?v#sYT$^1aXYKb}Hg-#?W*KE3_o%)9Lzz3+m#G)cVP^~J!JBNtwaV9RXw zrB5rU3vmy1LcQ$sOGejNkAXp08{xz=T$FpkqSv83Rin&-I}duuH9&nde5hJ&!p0{9WS8D(jT zMn(0S+KnQ&J^^3)DD_okkzRIpTkOq3p0(QLXnW5suhqN?8FzP2%>X-_O3LNYSm)bE zDUMb1Ya0bwJM+xUGwSb?Fgk-;pdNocEM~vSe003%4M8vX47(eHSR1H4;qZRuh!t(w zfh^FWv1G;t=Y_JVMfZogRcK|N>`Kb#`!p58!t3P{g9HJWe>g&^gSb$yk@TF~M0Y;1 z6I$CuZ=wllLl_fryK>mQ+(YYtHZXpW=#-+m8n8n@j_x+5Wc>6eT_IaA+00b+ZTm_uIblXA9VmL=USB zhf0Gy3!`Mc3UbGcco5f`4w`LrA@gyg?9(AEqCS54l-@eO5a6)h@g>~_loBw+G4148*C=JLcI$UtaDnB9}tq<$T=lsI+KR41FV3>`1Ju{dt(Zzlr0i!$8~!E5cp{gQB=XyhT9SZ&c;ZfEa?b{shX%Lg zV@m&=wv>AR&gaRj`pf?+<>^?_{p!=>7watfKq=u4Y&b3S_Ayz%Lp$(ZqUdCuCNI<~ zJI`)C__9{ujGOs0w$?KS(P3#Ep*x-T z1wkp(yUE(LI z_wkyJKkRj7$WDU)ySLO_KlS!FrC3?-hIJMJE|o!5|H9M?Xl;Ihq?zybAb&t0#`qG) zS>eKSuYO;vZ+0kiJ_=B%@6nZwLG~xky$FAb+=k)v$gitsgiVrShqUWc@HPSF90efg z==HPzAM4K96pdKB(cf#dhN}13vIx0oUNFaTu*7{18 zLY|6WBU#bXI}1Ra6d#dwn1k*(?V1Cb)Qc6@VO*^giRJ}oB3WR=oD)u86~UtZp)xGnye84$nvE{J0JFQYd^DhfryAo#$o2wis$wF5ecg4wPGxntF)uQ17jeHPZr(oG_ocr5Z?_uM^) zr{aejE(%sf-kT(;*22V4?VZ5GW#f;fYs!bqU*YgIZZitU1OEKFRr6_8G}D|O#4Ft0 z{SCS^c{fBloWFUND()YEX>D)}WfYR(-F`!>$K1{3j$4rErA2FZHUB)2%B{oINlbzb znE}Em=Dc2VPOwVY{FO!*WA2n$ZX&^rgUje1T*HSrKRHjvsKQoFeWk0btAp;3kpaKc zAGj{Pminq;cIBaz;ZY=ilgYt}qm#wKh4(%KcmCx*;#TcH&jZXNZu`u-J^*qPNy=dZ$#Z4byJHDDv?0dD zd5OjkUMgkBxgg7w1dHX?qGCr;XrW|G^;j?=D;JJ+ zYc#u9d@8*hB`-xGEhfD#O?6o!`5*Ov-Gk-0|Ipt$Lg}E^K}rfIj`WM1cgKo;&{Qwx zoW&}J7URXEu1Q}>1^-9=)0K25qT?uufZL7i?PkcVi$)NE9Nn94jw8{xz)(ovSaylN zIS~8>$B81@N7}SFBUr<2)^8SC45g6)%nqa!(_8^!&E5v8Rwe?(TdGRTdl%|Ag7~aa5WF4EXb@6$35=bOm~5zCgwvA7=WFAABO2$M6ytJOEQ9eR0`)30Idw zO!@O>5n;~ZjsC{d>dF`+HDAp$aGOr9j{29p!Ho~Xfoa+5a_fv-+aPrCnQ0CV!=h1P zyfFohnt`>c07JIJXx5hW+C7P`tvhi$^H?)0C#gELu$b3HbCDS~mi0LH-)mi89Zk4; z?>h;^7w0Z`Aw#9m7*V1*QRS6=RPTkaZfIcmBmSc!o^GgbxEBBsi27(I8>(6Gir$9k zy`jH1sixv3PIRc^Rh#s{*gk;XQ5Fq6WJ18q zB%-g`MH@_F`Tka|pEBF=r~SA)J0t`w76ZJwVJHv$ua0`J*=dygoWu53v~BgvazcQq z_*N6cVH3l0ZnOt{P1s#)oOPa0$ngi8@TZAN?CYd#?@riETn2{7+a?j_D-yZ1tvWh0 z#si1l;1M(sRtOb@P4zi;D~WWjQg(AcqbouUB&4O4gI(gp)Bfs9v|;BwduZV?2% z>D$0YST75J>s$pvsE0Z6B$V#WGQi@z=cX#v>}(<1j)3{j49^|EYMo(?-d}X*-J2fHm##K8kR2`+@Gh8huJ3xK}^A)9^c zPyHzf?w6?QQlUP#Q7OnyBL|6Bh+&I=)` zFxu9m-4P4l!JPD(=)B_gj~^@lAboc-L}1a{r0(58IZ|%c%A*4Eq4oJb_u{J! z*h;MRVfbohKk1UdW|!s>AFM~v6S-t}eh5>UK`6q3^Afx;7rt;*1g#A(2&J^9#5p-D zSohkz;6TX1hC!XRUtO1NY`U;R5?Ef*`zY3|4~>=I*s}y6Bh9z-A z1cm2`lmH0>48X+h8=zn?>8ZKMtc#*~cTKSX?@|M_i^?ks{YAjj2*4y!h}ioMe)98$ z&r0A}0cYtIuMW|9%lY&3Hd;+P01Qkjd1bKR*nsszpY7Re>cy-)4_@E5`g*()2F-A< z#;k(v!vSeGp`%|`!+P#tO3>>NdrD}ahYmHM6u3%XLGW}|Si;idTLC}_@dZ4Rfxk;^ zOjA7B@-E%F2DiOls*c~s>|PpERNmR^0U>;nXGQAI-hz)5eXr%iGY>UMTmh_{ei1TW zY_x|@4jv-omw}@-Fb9j!mI!xPfW^x@h;v_WYtj8j;Co_&q$tQH_#qOIcXOi}!u3d2 z^s_qU=2Gw~q2HVrp{N<~cq+N=H$IC!^UnNL$^31F6^sSH2kxAsZ1E^|5tLn4RtGhf znDf1kB)=ev|Bdd9SiYqPWZYXg%#4IL-qAp?`YxAXd6qr4rvQi;$~FM8$F;6{dKv1x zV^6Khs zovQ;n&-PB=kXa|sae(xXXO-9gQleyuYm*l25Kazu-8H<5Yf2z50q>(FGpkPr76o79P;WQbfJa)X}Qs{qfp_|%z*lm>z87R zMM$kA2=i}r&kI5k3D*4_Ru|SI6;lW+q!972uE-9tl}bFUhl@FG8w>@?Qe_FPpB_4> z*U&(-5QK4tpX4TA=C;oIYcdM|lr%frlNUkhVjs7RhWIiCEDZZ9p5K$dg8{w3x?!1Z0Lzn!Yv>Z;rH z;=jW%WL|3vC0nOnzsvVZR=TE4AL8A$uZ%I1h1MF5K%b0YY8NXv8!0GFb9=snPKHpyMW^+Bmgr6QfZdG2flV3I|o;x|8{9SJ^SDLV90ieTY zFm5HY2-N^!-V?b}$TLW+azUhE!jYwNLd10FxK8+4qB2j|b^A6{4aYMmb%72JIs2gI zzH{rRR_QI8wV5{oD&Z<>eEB{yA*Ck$movM^sgQOHgJq&4m zl{D}0>6#zE+b*WCFaFdi_sq=F(aq9jPe3Om96_No($zBiboFRz+s;sy$-(y5ys8W( zOa%|2uCz^&6<3JG)rN2+NAW5P1f4+1$l=6&|jPa#4gi*rW`eS4SL?RkFP6YJ8l}reQf~lEHp~# zR6ez50o^LJcU%7=)7`IM`VeLBD98`a540HsO7y&_-W6r<*fWo4+3UH$U9?{^DjuIi ziqz6RU)w!r-8^>WW&q;LdwG0?6eP+Hz`GTVjccBL4jhMTp`sWPfA=RAU zz3H-`NSO&ECfq-Tp6&;2roVQul2jT6j3GRo`sA_n0I}I2G;H>HPbnfV>lKfy@Xo8% zjgCsvFmZ-_^A}Tb4hbL!fW}m-E`~xCnz!qZcDSx%4F{O^y$q*sdN59qZ!V{FT|I^U zziC5>1R5j=$Os6uI0y)2|5w_OuZx$jo13i{hn-^p4>zZ``=Y+-XG&edp2oioDk>5D zD}vAH1oS9Y`M=SK!!&T&v>D@dFuybX{Ui1#0VeBj_dPYYv_@6Qbm8`^P?CCA_HBQ5 zH5aKOha@zOsYhwv;tylJxC5j=`EWXap`hKi9A|j2F$a19g&u5q9~K7NtJ55Z@-QeR zezKe4tz{u9-1_QQe&U95}X{LSoOf&R=Oy-Vcix@xxcYw40jeTQ)L;& zpu7BVU27q}jX6vp|9!L7i8(;|e3YQR5E*a5zV2P!HO)0a%u`y95LfzPGqRi&QGxCT zafCCfiTPHWPc>}x?#>(Z24r+R2=3>Krx0ib!fWh0C2fvu<*?T_{9EMC=xxtXw}WOY z0f{$v*e1R%e{;Sf8SXk1;hxR(H&Zkij-dYt6w_qtnNI5UhXFeYMMg|n&0_kkk2_c z{}K?~+H10)9HNjTwKXZg89B9LI_kZlU?st zV07@BZ$k6rN3Rqflm(_H7hZ1cf4?=aP7S^LB340!6NBetV;A)%9kNX^ZKmoKu!Ah z>+?6^|Mq?Gjf9#wu<7;awM@@t3Qmq6#MF{^Y4NKjC5Pn{f;T9i{EJ`ccGLz#zcewy zJT_yH08_F^o?JTuA`a@MYQECYSx?!S&fS3BW$rEMf`e_r6V|6!LilUOtO$cLR`+e; z#YJU$MWH@QZie0+PqUok*P%7*IO`f^?cN*>{=fHUDJ{(cuVFuQ*&J5`*GVl;3|fkr zu-L}_4UYZx<=rhQco0vs+fv?dcqfNyzV(?x9M_n@F|X=ksc!9naAOluN+9jYVQVxy zi1#~}YmabCyv zzUb4&It=Nhg7Q;2W>F7)ERtks7J+;Wi5`hg4jtrJoR$zEICPalS9DOf;qNU1lmGhSF#-s5?*l4S*P95L zEFvOx*BBkx;;{%FkutGAA0cT=VGOVu(Gpq)y5&J;WfB~hu<0V}bRWt)PcNRdA1Y5T zI>L=oQJ%C)aE3mhAqRdYr7pKHRz8N$wENFjG}~@o!a_kbs=SAj-G_zV7b0(S=i#(# z$*L${q9uQS*miD_6Rs1v+%_f=2;!-a*H`ZRaXYxo4vB@L_3Ju z^)pjRG7$mDB(=Bsup}3@qY~{06f`bpB4=bFW05m1kCPEI^pG5!B4o%{7LOq+1L`~F zaY$uRfo_+WF1(ZO?hvO1&I0`^5jM*S`#ZV5MS9=Pay_3DfcKb>9`F}ZVb z!hMF0pz1TB5S*~}>(;OW=zRoS<(0eF`3d|BRAf<`IT`fST%_2E0qA)A?%Ssh^5;wn zvfVm4ZD#u`ucyB?#PMufJ-{Q<`py!VGtqujS1f2B~3!ih13Ov)jh#I_2k!qSU(NhsmIz`zFx8 z--#mj24S~3S@!|8n4)FBK+i=kOq4;AFUf^%r*kW(0C*O`f)F*VmlJhu4Bm=PF%cK*E%i}n;$cjo+)Yp;*7_0k5-1NdToOh6(~<<-D=-Djkmgx0*<`;`2P=BJ zx}WN3Wa~m>->B(FrHQUnr~TqJqE46LqH#lYYcY)_O!kTNp=kCeu#)u=3Qo$;uUz_V zoR{Wqo@zfcHn;cNq;tM@%n%#kG120PZpLZYeSK`AI{h03Wslm zs7_ns?6G_QHf#)x8qVzWYPK7j_YL%$%H0rfUUCg5Pni-_GisTx_wjX0cHLaVY7d06 zE!R*1BA9;$o;I8ZvMoyrL~Xn)l}}r3u~TZ{aNiFKCYkS&-rr>zN$s)~QE}<@_&qss z9x*XtTF}W*#Q!iGwp{qf=DcCCef8Aal`ZQx&}Ym^!L287bz^l^WEj2a%x7eLaU%Td zYwkVLZerI4G>eDxcfk^vla@-Kbh_tJ}f2YWu`X-BFS{^1ly|yp51fMj;{p7V9ak;)P18`+vfM z0*+H1mHM@2-AFDXVMDi5Qb<7|f61|m4{MEcdfhm9oH2rYUk*Yn?As5j*o|VgX5>K^ zUvrbU{t?LijBXKPF$O2DCJXQl|A@Pf5V}ta>#GNnJt1>LZzM0RkH*X`fzcF2xsC5t zYz`4Q{_H4FkDFQ5=dg@7ZGG?kXn%~q&f1vt&G1{N&u~ZAhEg>Ff-xIxkOJ19!rY-c zVFvwYbTcmStn-{AH=IWkh=xIybkHn#RB8*yhv9nz{sDb7%RVr|7#C)`8CN$Ss82$@ z&$XMvfF9NZspU|o<+~2+d<4XPZRf+^GgDD*IqF~SQ!X3NZ!?_h>Cf|IeU|jw(Veq? zpT~U{_yr?zO}Zm4tuYKth|e_EF$qf`UN(mHX5IU^ZFQJMzJxyxcKIeZPdv@JoLqPL zI{*A-%?EuZDU}}VO6+*&ex?vekX2{a%2`*Gm*(-?Y_$%VeSNm~=LB_k%**G%cIV9B zCJ7NI#4#?XOTW!iQ9U8VF@@vZi>fdVx7r@vsm0dn_Io`6@36U0zI8W#4%5`$`OHSn zA|JAlLrz2M`hKh*u0yF^l)+o68TTP*dJI-M{Yu+Q(UcP%a7svFvvc1jdWr9^n<<8G z; zLn{7TA%)qaZBay~p!e&?g6-qL&N2{083-2L;~3rJFm8won%RebZ@R|{ zTzDUDh%wqr1a6m|yO89+LSMQ7L8LmI{!>}V6eV)jIk zzo(HVm6hsK0gFb@4I$Ftwvk)%SB_~>x<0f&xv5)no6q^f+ybRN_L z4yh3pYSCfIe+oy{!R2T;OxFaDJkfEuP;@xjKr0>;T^wUu_#+^ql3qOhO#&51&)L*`-uJ4cs&dYQt&1s zL@ypc8b+{vgT{>i*8IaZ!`saiS~$U5af(Z=y2tDxFo5z_nZTu%eN$r=efjat9@*&2 z|CvPKAE78FxCXkF<9>t-}|X7b`~Y_a4z~YY7?-!3IciVVoFf_Yi~#4Io!eqE*Dg zKlu@+sO{P!@$5*p&9HdFN{-aQ_!ox-EGeW&S(CdPrL3=BemeCh{@N7BuKX z_CNEnBkz~{3s}qXo@+lC$k(cTwXH*i?GXp?5+wwD3I6x7#p6cgib;q|WdkR=XS^jI zh%0WmhlTEys@p{FqeYaRzL3ZIC9563roZ$o>jitQp<|o+GS;hW#eqmno4gpo{hC}i*>;ZU zKzgKDO*IfDZJZ^;E~}MWgC8obU=!H(UHjvd)rVV!>s@t^gf`|j=61^0T01{(cL#!v zM@D(5zo}dsVjp&R8?uWRQpe^2XDQ-*CON3ujXVw`m4Ku8Th^6(h>{q{UgEb;ywpy{pcl9X$(JduvrprvV zXw#aNR3h~Vym!7wFHQ?^B+}X5Oy{iXa}`V4KSLA&#cXf1VDv^->?vLluq);|XE0gv z4V5X2xRX{wX&7cYrkzIBqDp029)HF%7|G=#NJhRl%l$5QjBiwAq`mU%TNpdq`>78~ zw*T38{mF`jRLJz}TH7PviFBCJbN6v_(*{V=-J#1(F!#^TCrGV}K5^VpYc%0BJ#v;s zAQ3d_L`DgHL6Uwyza!aBhcCUJ{GIMM!{Q1mnk0*Am5q4>OznsQMNGhKWnrj@qU~_S zTvwQa%$@)d#MreB8Z-6s7M^ zw_6BH-hWyW?8`Q-Tul|4Ro(Oh=j(uYq!n1X*Y&ks^E4J5)h&_5#nAlqw<;lK;N4iv4xS$ZkCtWJDf z+MSouvu)8_$Xh*-mdJi~({Q&_AHKD_z_obq@H+dkCVr}g`aAW%G`lmDerq8c9~S5@ zkFsJbMViOI!HSwcPNx=6vbR1?l7Dh!j^T#&6>r^PhO@9Ds5DnvZ=*|G%HO9q`&zgg z&R{YuUZUPklof)Z%y)>tZBsI+5qBMHu9SZ4kva;RbatIlY+2Pw7YNS#)-2sg8*_^gfhQp{Ew6KUEqjFRm07I|hF7ITtOL@;Hp_=_3YUP7)7)EM67o z_aLP0!nQXR@E{97?TMtDI|)qDXol<4(O$1_O9*nN^NpD{|u?`fEf$ZI1xTVxDhl~I0=JScQA4})3nyZA7Ly_ z$1V$h#IOfUTMeyo(k`PBZpbmU*Y7xNF^%C|Z6)a$M~Ihm_yMij6x5GY;rYP{@-EJ< zwA+lte=)FFy*jLo^cf7-VkPE(nR*M4H!9)$p!!LS^;NrgOSZaj|9BfY)+T}U&ljvn z`?%`Vz*tO;I`Mg|had|_={9DTn)r_jidnVNd4i1K5?~_j?XG~A?9f+6(l-`JIe2hQzu@q+L zCtNeW$~P-m))yzKG(*48SK@IcRkiIhvSj^8Y5#m-ZbfHZp!i|pFY*J|RkE0078r5e53bMbw%y~L1fLm0rR6;?AVo_k<675>FCmUlSxb_d&Xxbc0&Gn<&P2Y&H$nI z@W}z5PgQnBy@*XF)Hz0yX>%cckDFrkq0Op=SJK$2wl}Q0e4F)hc0c@eb<-(_pzn>; zu5wLvlbtmNj|3JQs5ca8S3)%2+^)Lvh_4@a`qjj(zbAGOutRE1rC&>x%KG18WaD$k zk5v{q7D5(e z`=`8B5%Z4}d@2WW4(!V1L#uw9R|5YjdZ3gBJgB9Adl)>{S@cjQvajX{5t`7mt=FYE zwQFl;eDAHHhRc?JXfTtnvy~a6N@8RrIlyAidox6)?h=X6cqq{^6`*w@k#3*$ud(YKyG?4yR%lId7kN zXm`}pl)1s3hzL!0{vDApvlG_58^R-m|F4g_39r3q7tdF9N52+63IdTUwtc>7eX2&rD0s9x;S3+%+60=;U)FR zirG2>)vb(rwE5+uwu;S16xOQnRHZ*!q^&zO)3Wwb&0K#pj5*Vdf@|x5`d^rFT;wL7 zLyLln;1+53uqf2;IEm@=R{qsp_tD^g<>-i&}8`VE!IMJ%TMBl9pBvVHrK>QRmp28w~J zLSp>TgK73z_PNp*LY#NZuz(=*-}~Z$%Hu@8&Qt4$oH|p7^2e;EjY8}P*n1jr`x^(@ zz&LzH87GqMb;BqonHizevT{pT|kWyaIKW0UmaTf z@~4gxh>5u-L4{obHy9ipoyXi}L@WPAh;nYl2^{K!RX=t_6q1FXM6#a~31h_CW~F`P zte(UuWSNnnxY#DHC$=S8dB8EwVNEb#d{lEpHm8ly#^5!-`WO;s>4onnnSR;2_7a^% znOL1jn1qg^`<=Fs8S9%_F6VG})i>72)6!jUat^cw>H#11Lig~@eAt)#`$Y>Sk|APf zEiI_fVBo}J4{zmoE>YNE|5q)Ed2ic`IYdqd--CXCcW=;lPLi#xIzYx%nd^(zH|9U@ zi*qDnH?nJW6B5{bh@$<3zlukgNB($&@}0GR4gEUXi*>|>`qYZd#;|bBuT`aqZ?^*; zJPOo6J^&xTpHv{Q%*|w91Puo6^2%OQHyAJrdW|2}wVIK*q}pT9yGGnbkYthJH@Lv| zi`48@DmP+uE#KHi2shCD+jWcl4bo?LM3#){(B~F~ygtVat$J>1{%GLW&cAMv6Y0z2 zbnBjKIFb4%TiB%7c-6G}6OCp6TI_8{!lJF-VMxsC^T=QFv8a7EU~) z5VxAIB+c?!{x^;%AzXJ}J%};9ve7DmBoyb4%p3HnSoXWrT>?w0^XbIa1&arKdQ>qE&9x=SOF9En*thvRoNZu42xg9Ee6@j z33WW5)$;e}IZh7(?nW``fR@ zRYWAzBpkw_YY_2c}{vGb8${Y!Q(cp--advkA`;Y0(D>H(U$kp6rF zStr5yTwFRT(9ekO*yhZy(W)1oPQ<_++;z^fYQr#bqgJ(`t6(4BwvWQcFW9-C{%#WZ zoLjF0e_yqQk$h4P{sg9XnCMMdFOb3EYk>b_E$ z@9gHp^7tH~?+%RzDgAkKOWl&Od0)SZ!(HD&MoHYi_AO^3%c@t!Wi2X+g$VpBksw$a zt}KdVcr63g>_ReaB_0Jt;8{>Jo2?V59MBLHh}dXe+6f{>=ro=-(H3?-*~7e;kEJ0m zmz00^V`PpmOK;D)boV5Y4Py!9!D+&2ojeVY-@my+8GAp|9z|$rij`Vp*e=i21nu+n zeQ5b{(F9Ufw?y*47_pVeCP?!Tmn-?t>8?Nh*eElAi8PItxPpw9;kXRbVY+**I4#Uc zuqFIb_8R>qt+|?UI8c&kSQsPVN8`ER$g!45A4*^WiF;bC3?lJi#QJY5jQe@sCatTk zxrxeruPsWgN3Q-QOuAWu;FOqlCLH8yMNFAs>LA zMg$+QFByMSO_J2a}3nbcJ7*oRQYhwIE?P(a=#;Lv|2u!?T^X9BkL>`0XYfgVg z?R~qc-DS|N2O$pjC=7OvTD~`%lAb|+j~LJ}$l8tI@8mve#0R@f(0C?og?LhDghJ85 z*GBr^LXl7i|9xHbi}&`6{(doZRPXyG1l91jL_Z5E14p!#2xSn`{Mmb!aEi6fnY5MG zv|&lU3Lib=njwGI&j-ll=d^s3cI71rQKKXZ1Js9Vt$!}VSh#H?4d}x!2}YD7q;@;S zm3?`W6vQ^3KU{?2 zdamnIG1)99c2mS(-D)dTgHL}hh@xLjhaYmyyJ=b;j zxy_Y|BKzmQ?fZSd4Ewfb<6?o$C%GU!!_Js3wDOkHO^fOF@LOMp!xDdMpffGNe=ROq zle_G1HgdGFalrjEQ?iJKTKjN<(!7m@S&K*EDz|I2OcQj8jhhO8qL?uIM^0$3_P`;Z z-9kIo)9eMGHRmULI=??ES^lT~8z286aw(r6^&9eF5njph1*;?1#?A~i&pW3?^ug5F zl=*_|S_vy>@KU03dbieC0%EtGh0%BF(0k--5WHPllQ)G(B*}P6$+|+qvulDecXAHf zu*rt?3#H%-I9*D<%NPrCI7PwIG<&SXp$h76g2=@}$!u!Skxu8#H`pUtY zHcF+5*DOSw7Uw@@bsx-F1Vr2>*6dtL63bW&?J#w_q(+V)>!!mw7{KE5x$&^5nDvU# zcnh{aYeK7hupQvT%BgWI@$xEtzOp>sbV9*CPPQn9QE|hcyjr)E`O$5ebK81D7Lw0I z6m|axTR^10ZEODwm#=mQ*SQNHGkUkR<2rf4-0qckiV~4OiJgmtE`Vpw%=X>1!dn%`Y&DycfniTc+MKmk}4hFf}}*BrVK8U;GN$ETXKDdlL56hT6q=fSiQtKtuo%a)sW!M#5wQ}!@mXtm#M5wgR4k#H4j@M<4D9rxRe%uhl72@2yF6aez;=c z%0Hh?t|a58com;vk-UID3psTLa|L|jp>~c^7<`z}RH!}@sS!+-jxmCBi%w%-$S&|M zXUF+A3@p8m4qwu{FNbnwSBCdD2T0svr1|HU#KRu z)(W176{!$vf56BCp>{M-(L+k&_?kYyA|tJMn451AzsK>m!zxzH7r#)S#Oc}62c-L< zHV3mP`yHj@RCW>&<#|-GcpMDEcOuM|s`*bR#CO-l&4Yv7w|*Otkafz=qR_Tf6bD0zONeZ5t2mZieDSQ4iblq#3kA?=MI zISX%yfyyb(5Yq&vTzcUq9gKs6EX;6RRlzw9)F)#x5%Duzoly#{q#b9 z-Uo;-X~zLpJRfAT^@x;(Ga~Vnnn)z!aORnJp9T^Mc>)X`0+JfgqY~VbhEw8OS9B5n zs)M8;+>-#1k|8sZ0NfL+k&+Ltx~lvU6HVLq&^n5I-paJ_DQBeLcc?6MY4V*8(wJjB z*Af*5Di79QiX~g3wDu9)6MeKg18*-1UB7x1A6#}lsGV;G#o?1n=YiZ;0;~v24Tf)ApR#8c#(=E$k%1!`f(f%bkR#$xzJ zmK*qDlYK_T*pellw7{HNJVKo_KMryUrPxbYWs7myJg_^YM$0~It5>C;LZxCaxkDd7 zt_v7@*o)FurvZ%4;+JXo@~YFb+&J0pF61fUow{RSPl20>)wrP{*c*g4SVn{ zLGidM1!+%vLQ#qe40q$B3fh`IA=uNA4(;1I2Mv1ojN~KC@_{(Xk=^q(Ddm$L29KZ= z4)&c7gX!LJ=P8D1o;*K!YcdqOSB`;sxZ*A6<5E*i4F%0OA4`Rf5RV}DGH@@Cf*!ip z4AB#BrT9UKqK{`Sek}0wZ!t*1yzK+kC16xS+n?7%f4h6>FS7AR>+hSRpug|Ps-^RW z!tQ8*OXrPE?9M~vLLrKDjPFO4_(Ceo_Z79>Prk(z30spN<~^&JThxN&JhTU%{g!C; zUbS|p()?SRd2zMj-e3py-!-?l5lc))eFpW6V|A6}Jf^B~cUNSAF(zn-H0|xf4oi>! zNIjmAUN{1J`~%hF%_;0od~6F$4t)Jl2pY~3>M;>7JxZ{?{HOn;fnHJ_lQ3dC7BtZC z|74%TT7RH~lN>tb3F(fby^V?LfKCIyS0}V{Q3L5AJ=%{3(t{eH>9w@;8&v}+TB<5q z8j5y5ik6O|8C&)YA)S9trN8|s5$yi|AQ)Qe)D~hQsUTPy_?zjWghIpQhZ47p5^GiG zNl)=vFut7t@9~K~=|1QaSx@d*s0N-nLNwsqKbQIvj(XOb7rq=1lPAwC5bzs8L`ucK zjwW|!F|2E6G+7@cq>9otj>EWARD2qD(JXWfrizFWOh@d40?VRN#?!28uZ}txQOomJ zk!jX7Ga9$A=|U$02t6mBX}94lguQ z4&MS~U}Ok~ZH4%xx`aqN3e`vr*^Her6@g>bsZvWcymF7 zBb2kB2xAu+(!nOc6FBh+4O_fRN5jXWM}QAJl!0@zEk1@VE{3|>=Q7SyCyLskpB;qM ziZH?StpFhtv~eC7VoNuLmEs;tX_6|>D!>K~MN#dT8qg{hJMM%Q1#yvK1vJYRpJr&2 zfrp{!*9>KOF)(cZ%y<#k7#FjpkshI@=z8?V2&a@PXL^J;impc>ut0v$BkWUwN%DO} zu@;6Zb}`LAv`n?ueec*uIG0zK|i3L%Dd;C=XZ3dJyQ zOBNXtr6j@!)Q(3I$%G+(I~3u!!)?~JJQ8z6uINUpJ=|=pbMgjWEAJpe{VcLus6N_| z-faq!rc{wWA4BD2|5MFM~$9wSby0JDVRnK&zJ{kBT@ougko6P%&H<>L+^=*WfKb`$SeP{Se1AGzwF%OT=NyD(x4S~VPG zY)^HbJ&{q=X@)pS#R!u#LkxN$3^0>2wy$TP%zgGq_#O*k-suhN?hJW&#z$l6Fk@=4 z$86V3xqqv6ifQX0Vy&mq8FOS?haO!gSaSY;|*TV~StGqEh zQz=K_;=nYkXATpT%E5{it;ptmEMLKt$wM->7XnAh{aeq$w`uF2uOO_FHEb)ag+}4l~w;&ol7F zRaV;TG&HvfJ$$`AN(vhW(nU+> zhvA;%$GqK?fmI~V7T-y`ePH!0OxPFr0t3~q?^(`efowaF-Ks{wp7##ny-Tem!JWfU z>k}ZDk@r1td|n)4eu3Y%;S-HI*7XuhFas~c7wz(<`bMQP3C6*jN>%+?Qxm(h0SlDF zSJBC^jE^ACIouA268NHR$jgZDvBlRQkOlU4knMb|3s=nOTMJcW*Gzq*36fTJ=TgK! zj7#@+upmg1XP?Kh0yKrLuwEJieH$Qy4pPa*(yQsSM9h*yx7OfO;Ti>c@(aP%l5KS_ zGQl~QIgdGn$yrkcE;$TMbn>_aus_j#8e@+9a4lRs}3=Cvv1 z7E^QnG#OI5N^1%_WnO$1ZY8srYV*bm;$cfltu+OWW~1RDPu%TBC{ng@1nzsWrhH>D zRkFYG$}yd6R!X%+Y>+*VKjFu^mHEppVl}XkWh&e7I9ys%>MW+Z4MPNvc8HMTd&r~J z2qs@%m^_70+%PGNDYGKSbsO&mv25i6(3E$g9V4sEl#F)Ms&}RuwkU z?7B#I`{Tma8R#=0KH zee?kvK7;l)1(j;RnT#TmB6%*lNy^7dGgCu4( zNw|El9;fk(`=kyd_YVp*vyTDK%h|2PwA_?VvUso%w-b}0!DBU8OYxn#eYk;AsGunV zX}s7_qF$zyr&LJtcXZ=8tyW-|E8WpS`X-s$VDJ`X)w?uwkVsbN1jUBeuye_Nmn^#zZ}jfp9`Ld&KXeJ z_N9BuF3zOn$ra?yVtFnvwi)XL+l1CuDCYF0a$*f9eqF*()VL>D>Kn6|2Bkd9R3lEv zkLSeeRsnjH3bX5U;k#r+%35_aMzGxmWV2S)#R!ms zTE*km(Kw9MUvieGW|Y8i;huox>w$cYb*!DPMu}QOme^pyoqJ*}t`+6O=*W*$hzHH=CpMnXEN%TnI#wv@L+ETyZz_nf4Y zEn*JP-B@0duB$EKMs$wslCX&09-S#(7sO9U$2cpv)^9BJ-&yLVl?wJ+mggZQORV-O zv6iGVr9uu<4nYqsnvO}%*X}gvp{BeGw1+sNTX`HWdT}GxdaYM(~R_3rg60(@>36aVnOY4!D+Y^RNFG(1}mn1|&N?;btxRf%^)Lw9u zW1?Z8oWG~&SFeF7wULb#UxCZCUctw(7V%4R?ebd0uk51)@w`|;hEMWn=13{s&Ujfe zV3cNEbx^>bgox>+%n>NYi|42UNZ~+xC+@8TB-z_>J8s7VDI9u8M=HXwqbeft+fQG@ zh;k2tRKn)NTxM`Eml;OiDO9RlRWz50fXYf3c?8~!GPFXwhq!s1vpr)z#{cTY9aUbKG&1UF{l&khM&UwRYzZbqU?4!-DqlGcN_zeWI`bK$#=lLSyGi8?gQ+x^F zBsl^gqnG;_Z*ZTG?@u$ZV{N!l=)%(wvs{o;ZzGYih65*ZUZ1x0F?kMLdMUet?YL0? zosiNXCuM;lm4MrUf&+XCwhG{MPnr&bmeGsZq)kV{P#TH_u@(xM0tGM0e*tp)kfBIR z{dr!jaW*narDF~4B&%2+SVgvV)#AnsPWepm#ENq~aW4umg{s26$zWR+&@)_Fe5VpE zOtpx+yc4k;s#ahir@kWKJ`ctR0pE8LE-FDu)7a+)P+nl1f)+rYYH$lIU0Wd3!8{8e zFL%GGs4n$?1}O}ky$P7K;r>Ap<8xA(C27C?E_`Ap9>_fV9zf zda!)wp24)6aL46IFdx3;Boq+pPeB3kOJe$$q$&8^89mEN_6Naz@f(ZyHMzuN9yMy= z^U`-W&|hE>gdRCZJVmPMLaA=1kg--C0YPa~yjA=fn}l{Sf4U@@pec8_yaGh{ydVp@ z8wzBJU*ountO)g&!81S#OG*XVLLrzcHbhC`9pq)AmoW(q&!R6Qq02V&_-dF!-gKG> zB#X8T<;A9QGq@*Fm--UaC@%QcSnsM|OKXv#0pOxqW1Y-;I7P746cr5Bq&hCk^G(%a zO@Yo>2cAC?5?`eZ?kK5_OY+(6vBOKOLt&Z?9VXb?1m?1PfG16FI#_Tf#U~EnQp^xu z!^`;y7fXngC%h$TxQ&+3;?9# zqNg|#r|0F{5HxZkN}MIKc*w)lhl2(7kLxPva6Y&lx!YH>sa-o(h@ z(WXtE2p3z&kk=4H+J(#@L;CaxE;J7|g9Qd2fj*py+X=Ts1eW1zmz}Idya^E|9O>|e zVa~Rp8#6HNPmqR^Hx;~_qHvYSD-VT)5aO>;dtQhxLsVHayU@$E9)o( zu{%eH({~LMr1d|N{E>9zuqDf;kVcxwcU$0er#gv(nGv4WpaCl#E!e7qAXwl)%|!Nh zKK7$37#iq&t0AR^WA;1S7*uznc|;5EUJa>d>tX3_+#KXQtK}z;d1AaqgURidfkJSf zO92_I0)%n8C&6bHzsLNd{tQmh$Vo!*OHOi;3}2I(RRV8nWQ+Yo8T-jA2+p?1jZZ;x z?uj@#3DeZLU0`oYQ4(v{t`$hPKOJ=@4_MTnBz&iH@NF=l*VcT*K1J1VY-1E zE+z4>QYJvqySA;9Y`E-Nmnr<2v2I%@*>E|hJ1$U+c*Q}?f3wb!pdH|mbq>bC(nKr` zUjn?0=9Q?L7cp#UQaf4TPcYT9F5JBq_#XF~G2Xxeg|1R&glYhL*6$QCxJ_-K2IhATA82uT;@Bo%O1~ zWM{@)lx7(UJXaNXArZK>jl9Z`1}rVaBI(HBTu7?o+lgLr5x!(Uo&a(c_i-d#Wgn}e zn>N_uZ`3CQf>ii1i^T}z@Aq#V2tdhPB)1;?JdNu8d<19<9}q~(F>6X=S( zd?!935VIL|XCC-PV7&UUCT#+F*aZA}$$=;F_CauMHRuVqdf7l2&{N*N*&F2YN*gwz z)`7Bmt0A5p2Nh7PFuMCMh^>5jw!=#H4NeG(y9)?ey12HE2Cnf85Gjq@CaGtD><)Ij zz>_!89wyUXxRNL1`X0Uq6SPQY6f?9e%KUFsj=7MCZ2M;UjR69%hu$2z}#ecO?$(Xi>iq@*Q`id(M}w~gMmA)z{>E#%od#29Jd}9_rV=J+|nAzmcY%+U_HM9ZVSd&yk z$ig7)bxc=8a~NutU(Uqc;*|>`>|QqL)X?q2k5!%n`1Oa+Eu2W*6Zf$d}Ml zKCL@bJF{Q;5_`(0b7$)Mm(LKCPt1&UXU6s^TQU(0h=GhiRs2lM)VnkFh`EJ)_x6x4 zGtQkEcMAzqdrBDZ&W!J0!nB?eCb%;b`jn7oYG!)k;@vaTRS^^2nTbCTF(@6fF$F{v z53-fMgFx5dhW~RCddo=V%hd7ioF)b)jEBL(CEvj?V*`)>*Z3+&Q4@FPBm!HcH4)}C zwJ58v>h^BAfl4e;xn^dn!8Wwd*d~%?ghHyaUcNl zP}gwhT;M&$UEL;Dfq+#&q=EKDW<2j6#W9t@0%2d}9{mK=!m6%hb9TbkLeqap*OmlIfsztP0)4iPb1L*7agvq1qCmu8wEwc=zz2+BvZf z2w*vMCU;Jo=bx#R0`{8`mClqyk3!8@p21Gjc2{i!#`SO*X=P>m(@)EKRe&I@`8Ra=+JqM)usHI~?y#?YNXEq!ZD%cJR% zh7EmbtlPW$(irh>sxhe#3k&*p-!?{^nkwx=UmEN3?LIXI{+!cBHP+OZ##GPlQ)B4Q zIUQ7G*ZNYK>e79y41JkutgSDN1)aKYjS+XI8tdq7W2#g4VPlD^jp>NGhWBk^)SvrN z7jbB+E^S}x>T>A5)TR1!%@6&VOKJ|fG_-r{liPQKz|l<*{-aT$*zyIH_c*GvST2Nn zs&jKvb+=!?tfy3n#`x&Uq$VH*`bFh6sifkm0(-f*IjOAM!~dCzWJEHEL_^9{NvPDJ zGOwvr5~%I<=jt<6fyA0<7|*<1QZ2V`b7=)&~cPXM4~EA>_ff5AaE=7YE?29sCo^3s5cl6ZlPYC zN~MXaw|87XX1a#`poT%SM9CrQjaA89qw4J)AyBQyRjRK2|;1*%sK6t_@s zW}Hf;gX;HMANmbOj4u85(rr8?F^a0T4Yl8gdV{g!7V1qJIysR%7m1OLI;-P;rc(WEId$%xCU72{dh` zX{Q=cAYs*GU7S^lMs2BbQJWhNC0RB!Zzq%_c%-IDID+YHy)RK|en}pXgRv zS3bdY%@3%Zb@cK;QoTE=P!(sa>C#wYcS_N3KGBc5)Code&j~_ZU-?9c%5I%dbgQc? zqxhk=E`+2KeW^=z^ZwLDT-)4phG6bvhR~O~uJxHTbQ@T3j?lNp+UOjiu4k@L7wXM@ z8CdTGp&tY5P7;2gvZy{XgHVmB?%apQc&2iuCsrSvC4{5|)SVII&iwLm&QlzQRdHkh zl1#a(HwJQm8Zq-rcV+`99|Nv9r@@_hjPZ$cj=3`%K|o&2Y;?4YxGm)`Z4sf|!|Mc0q$>_Hnzh6+Xpl^0PDj7N$+~S;dMJ|JApc znzyGxEk~^2*<#0M?oVD&lq~VgOi$Wqd~pdtEI5XzZdbl>5f02z`kFd(NfOf!P&K)jtGD&{P0yLpTbQ= z_-vy}q1&>^Tk!5lxHf(44zi|?=i6(#o@xle#~NfU9*<@q7s<{4OHY!b$1~unf1Gr* zTv3*@hjB|Td4k`)CZAL$n9A5!!pWQ6e)ZMS_4I9kF>l}kGQQ1951*MJ72MiatS~8;kp}Tg&iJd;65uj979p!RYlvF=RuIbzTp1 z3&ytRv$50Z4fi|S!`wowqgF1bPug*Nx2w)EP;61Rqh#vj zB_eNV1TiJDiwDnJ;SHyaRe-sG#oT=C1;KYJQHZV~bwob4LvRMPg7bQlTm$Kv z1YaYTfXg-{if6W~C_J1+Z1am(#CE}V8G5i1w+?|dl)>i^IUEq%W$-M+PJAZd6(G+d z`-iX`b4c)LC!zO~zooOhA`_2$Ul1#9)cb)2h7+NM$`N${J5Plv5GrCh&n_igquJKp z2~{>G$}z?|PVs`d$}Dd87N6K;wlTzz1yj?;Q{1*TEWUMUk0vTI$=^Wn!(9d+kN)5; z?t=l3h*}O&O6T8Y^tvC_n-=IDqw!Q14fz8Sf>Y~@_?zwAs>eh1?2&?dgH8?? z+z;Su5Qlz;&4Fd1k4Dg;c#m;B+3|hU8eZ`CW{c&&O zFMmgNNF)oQ^f*1aP0FXVD`pVedmQ(dlQ)rhc3+;3Mpw983tl%)aE~;bE}6v(W_I>j zv+4V-vytXIH}FUqwitIR^Wr5wrG@v{Q!kkzaty@#`^ZkG1M(_-|1YJ*%v_V+Bd8PP z?5hKklR$oX))oPUeT>kvPl3C9$|c^j8fjfbS|_A@wdg2vWXL5mb3s~6P?yOGTYhO0 zZOkI(=q%#>REJ z^;;)Q(;eWX(jIl`ZWMBSq@~CE?dk66decmO*Rgr=7?8-u3Z%xduN;u7Z{QO&E&JRb z$rIh7JTw4KWYaz?FlEXqtJ|T;a!)aE>^@+L-M2<(65M z%Y%u1kWIPD0jcT+edkiAah${L{CeD;M&6%4@oPnq)4I;uJa^E3S7D@5>99gHZ40+2 zgAN@T@>t$<2HJEaNLFAsuuVgc6eltj;Rb*_yjqOXtuVIW-gEIp2K!36bn`Vp&8N?@ zfAPAy3me1qto)nuxxdr(*R%ZaM4AgoAtqWJ5&R5c%WDlW$3 z@&Fi*9iL5A^XO~z)W>8&Jsu%l@WiGHDdki#xUKugDdvgSdMc)0E8=gVnIGlr80n=! zul4X(dIBAZt`E1)Bm=Z#pn6!_3{=I+<-B{XZaKuI&63r97an%;Cp^xO-S@rN+vTM06}OxG<+D26rTnPXGf{wGh5VY0r{^0lqQpl0+c@!7_c&vV#ch^|`y0K1 zdKii_+0sP=HJr0eXJ$)3W1m}(FD{A|b8J&evzS&+IS1$)obz%j=TxHE=Vp>kwC#fn zBRHi@o}`oo+zdS>Y0$60d!q1QzK{wVlR1*CU=?=fVyvu`!O*;_Dh6Ne&9YTlnW})m z^r{smALN|ho#2XYnAu`{J45~*=e$0aEv_T9uHQ$m~<94In%7fI`9U{5iXt$iQ z-z=WAPPRhme|8k%rcLLFr2)D9WiY+vT#I*r9rWK81{%Qwom&}C!88Sihm>-rd0RWC zKkSY#;g09SFt~QLY)$5vW_*Fb**;{eN~`lFpfyqu4@p<9D$3r@a5591_da}?_)dJ! zupc~<6!Pt%`XTfU*2clOUuIs-a&}yG-e{VuWL={N!|IS!)XQ|chfewenI8oOYd6HP z3#yDQxTPn9eYIT5e~D~r-tkiKL{Z|gk9PU2>aXCsT`s!|iEV@|W;wd-X>{2KxYlEM ze*MK}JUOp%%P7c}G!E5iz1T#i^C0FG(p9YdAldfZDm7qnJY}cC0*T9+X52|uZjYtO z7NmX_<^XuFRScj5UzT14LS6n#W`eimQ^Ix~ee!8m`Ai~wvZZt`c3GN_+)bx!M`QH| zR8GhB;B>OesSf$HIKR?_a(U&&-dqOL6l06pjj5QZn1hMR>RyQo25qw6`i`q)Tp>fs z2r{HhqC?7^IHZilLs3u26T9+?LEl3Zd5K7JLLP&dnJ=oNvYKoR#}oqxp(yD$FJRo2 z?D(^k-M2+YQyybgN(C0O^Mcz9sgTxStg`IsEG&O(t0!C|lUKKtLl}dsCU4#qW1Xe7 zNnm^ezRgl&kFc7mHk`H8N)}U%;H=f)AHzB!HNFr$eD~I)@`{&kH7Z+vc$AS&ng~@| zVbmdAk7n6ug=7^^Nk4g!PHEHC!;T)=MYx?C zNr2fv>$m*Pq}wmSpt)}q%!zM@ z4DdrdUW6FQF_$>clZx|3JG%p4 za&lc{cmAIIVwj3|457gTqSX{23s7z-|(~_kb7Zt_yt%IJ1 z30P@7An@-FOkQn><;9ByH6(VEzt6M-ly#ZRpC-0O)4@o$0}nYAa8w(e&s-5BL3YyR1nc0$>B(j zfu~mR^z?R!;EkdCzHv#Ij4XI!Xgm!qwJLEK+tfGBPPp-vu7><*@@CXO7&)q(zeCX& zkE-iEZkmCWkdeoo84D!;hpZ}2?>cE$dS(Yzz;Fsate&i)o|^K6o16_YTtg2{xgVEo zO^|ZRV_aSb>nzkWjvj>vlC-An&_z-&3#5jbMpHLP3{+WXs(C7qWN}EAnf5;oJ$B_-DFlB0!-@CYcq;wFxi@(NPo7p@cjC*+;#D`T z1m62a{m8rdZx6U`&Li-zs>XbL@Qqb3y?JvHfs6mVbcgwg2b^`A%Qr>y{Z~yvNOlJXs-Lnc-N8~JhZcJw}fwNP6X^WLl zs-|`BAn^RV_P(&gHTzeOb-qmCPq(J5H*CCq!ru8?0`J^?X7?MPp7_ho&bJ8s@)mo^ zp`8C|`=8GD2z=-_Z|{6*J-=sv=RXO&YACy}tx322MCT_2{>W&|oj>M-FRpj)C-8fP z&(xHkn)Z5}@;QMcCy%{nzkbo_amtqju75FqU12EG&D!~_%=@Y zc;>hE+2?T1xuAWX&-2G~KcC~5z4luBW$k_TUi%>Tlx&o*u=JR;`%Wee-u3BwGJC}n z|AbZ^lzZQrZrj?{-7Twc_nuR`@7sd6t^DoOMR%56FPQw;??&*qPrrAs*WX?|xV|x% zzco82`P_>K$1hzyoxgQHz3}Xg>fpmivVVe&pG8&HJYRthv@wru4UJ{ddXrqt||7sbuL>v#K_J zxGD1Z*OqFQ{;!hBUk2@|*m}cK%hJi4HoTczzW>@Fv7V(5esnf*`o|r+#)-;bFP{v1 z@Y3MRH&(=nO)R~&Ao;V3gZ7qrVhc;3nEt?vmw!2E!|%j4mY(9ce|^XMRVORN4whc` z-l^p2pB}5(DR!}Rh--WGu_YDXw~FVLv2;~$kBeLQ*KfruEIp($bmzrYJ6`d$-eBo5 zMNe(BUA({5ZuMYa4*Kx8SI?fVwpC2E`myxuewCZQudsbH!#YsOzcO{sF?&F5j&&GI zFF&+>@yuzi!dI;93o(STU)5SeyB_#>t91-Zua;die_Hw6szz%NONZ?)seFC#gtjlN zAuRpY&{OqmzP#^^t5z3FAA8}%2iYI5`}S^+IF^3?LQln1@%Mik?UBgR@hSSZ*K40b z^#|AGJzZUA=f-iinDgmF1yRUEGp5@LA3Y=pRmBz|)Ok1k?VZ3D{=b(`eW_WN18}{D zYcj4_T+iUjY?fug@acI>;mA;hPb&;vt#HL&h4Zg778V7D5nBh$Wi0x9J!9H|14R3* zD$z+zTNpiwxTS1J78rWE(y;|B9nsX#4hD_7`n+GbP}n(X(cZ9dq3Y=Jr-BxS3(fJ* zW&QV#aN&_xXLlui9UfZpe!=iR-RnxYc(T1~$TZiQBaeF?>M7)}f-jwDaLoVW^T{{x zOSdfl7}t1QE?gpFsR_6;aQy+-CS1pG_2BYFBp8HiY)tmNjO>j3MM9d8_7lMo$jei| zxL?7=-zw7UoINTv|Ev0H+A`ESH4#f0z3lczku1|2OoipLyaMzAtH6_BH+TsY5CF)a zKR5=l9ldG(mtY|{58MFOfOX&i1oq8f9F7y-o`R4M+QI7}y^;P3P9=3<08V1fU@lIp z-d=*R2DF1sU@*8ChxY<-K$$GlW7u4<8!QKH2rO#X%5oAoWgXfL9tZ2d57t8u{0{U+ zXzIBE<$`m-V6YO*0UaBm4_*U%KwE_@Ct$*RR-*slz}Ha@m86Y6ET6g&s+29w^G3JHad9bvm1NqP;l#27wN+1Iz)_zlLAI zm%dj1a`$zx72F4QgE1%3K5V}&paX2D@BIhQfV08SQ z${w^IYz6%hw8zqyy@K^%I%vCu{(~;C2CM>G!8WiP9DNz%H3ahz3tvaj@rkOE5Xa7m70PnQ(D%qvQoExsiMhe?hwDZnY& zosvHcoDxk3fg+Mih4eSd#(F98I0yM%Iyu!z5ylC~$9k#tMo_&@t==%L-dL&g9_t-m zebwAs4@CqGzn5jUc4tU7RZctqVaQno87bK!U_^4-;rB!CG_{xBBGdzUhE7h=A$@_e za|QNZa;&&fvXxMSi>+ajeVK>2M)Kafza^Z0F1D74VS6S2u>ELZ3u_{kTMxUCKVS_o z`M<@&o=V6?n*W`e|D8rZi1YjDKb1qjt6za~zSqj>^{3M~j!r35{Ib7sOh!2&FJoUc z_aE{k$mx5gsr*&c&XrVti3f@)rgkp#wEoN@1%#O!$odcaIXIBpP5y3yd_LskP4#s_ zUIh93Y{HPcU7EWieRTef@D-OcPrB9{6N_UlP7jqJnwwO?qmr_(|z?4Q7qeHIDA zBwI1nzsxHB!bh@&kyRKYYcHKP%Ao&J2_N5H{ghrUWt0$d$^>4Wz;_LA8e4r)b`MtQwF=g!|pL&{xUIEYV^ou<+AZd{yF6Pn#UjV zA(MZ_6ruij-{Nf_NxTFLz z#YeJ7sqTsJb+K-6XOVp}>o%&D-`k~j|xQQ+mt%6vXH!wRsvr^E5)6r{U^6ZD8Z2pQrS+!3Fyc*oPSBscGL2SI4~= z6E((dqGIiw)lY`A|LvO1yCx(ytmWUr%-SEVwV&-LE~WiB)^dwm2XyO%AI=mK&LEFk z)cZY3yWeA^R*Ut=7X2-*trITT4cySXpYic@Y2z6qby}>WEq&cLRBsXNN8n6jHP*{Q zfLPRvmD^k`+gBQ3cMf(p1^4#Sf`3|PM)&)9=?D(KuV$Me4dvcT_Z?YI5mkT18OL*)?wpZe z4G?ePf4cuXaG+?bljT2Z_mN9?|8;sJBzL?2sGlLQi~O_u{*wrKD&(t8@=VA(ARlLv z7eRjQ&-^~njaMb)e(%b1tTwOXCHo@f<|02dAb%qA>x}cJulOKRDz#Ww-(ud-dg3Q+ zePvKwP1AM~AR%~y2M-Vkwzw|t9vp%Pm*8%DXK{Cz#odBC!JS0{1Xx^_#ob{)o}XXU z`|CP$s(Yrprp}z{k-pydwq@7(#V-tP6*86$X#g$4VhFl}#Xy5>dI1Uqu+2fnvcF@W z=-UKh?lW=QAG?Ro4HBsos2zj+KGhgUscrVy4%dhsKhVzM&I_Zs!GC?(_G}tHM>X_r zVDl6UKsi71GtilCY0Kv7Z}k6(z8!1I|6eg>+)iqv_Xqi>%)!k~;7!1Qc=v~UNu?)+ zU_a>Vn-;V8+YdM-P-m9s;K$+D0WoGz*)i5!nXQod6`yTY5yr8xhDXNlmP87?sipxQ zVl`MkFN|WHZ@kaMZ!Q|CRN542KudHbNQ@=)JM}~$>bY8|S5|c*g9Ih+ome6e|6F(2 zm}4Z88o7rj>Ugl79M??Ya$_aDeH6E>bWVhhL%IJ%QOtC*xpT0y4JV81b`68jrc+sc zdgME#q&Egln!u@so5?rfPpcQWZwkq;Zu>@6LyS-^k6#j6IfUt_P$LbuLDJxtG!^Bs z?#7RCE6wo^7fDATMus-66b73sDfyVYQTyCSwr!})v!;JQQ-bQzDTx>$J7otsy|lqS zh;|R>6=)D{gV5?dM!F1wMo%OqI|MW?FjsW$rhEx}7QFoj-OYFhSlO0{2&l25 z3Gq!j%YyC0Kj_E_$KX}wzy!y?A(w$J@|sL2D`U-W7>gBR$!3U10lVnx6nI`kF*WW8gp-_e26C;99ouKBzjE6O>W`jp)kN<~_D1PN4Szj*PC;s@UM@`UGL(B#JLoQ`J(G)fpbqg)ulBj)&rLodd4R-frEl=+#a zlWk8KI0&6p0)$-tD3Ul(pNlVq^U2}uuE@j=bWiEO>(u_adQIqA3eB!Of!VIAcS