M4;k=-(Qm+_SOQ1h%yg*z@+88eVzDwcmp=iL8Vfe09T7}`G=3M7W8^fvb>|8O zJhC1D@)8c^CaJKxlZ|OTtr~ zw*veDKW_#23w~Y-;DR4jET!RFlGgYH&x?TcZLz#nvn`5s!5dX91@_Otn^V>xeOv6* zzO5T4_if!YFS9oyv%ctDP=4C0?&5{eZ?L^uLYskqObI% z$sOLR3T+GY`|%z^udSy95PB{8%f2vUf8?tH|6~4%B|AP-pi}n~o=K=MjpTj)b)er( zkQ5xqxD*)~iG#aSMhJa{nz(l9?Lfbipea0 Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>DIc-TqK~#8N?Og|) zRMpi#uTF0}+Z!ysH+u=iC@R)3w%8Mm^@}AYF`C3Arub=0u_cPJVx U?K|hr8(3fwH0 AQYSAQDnQ%)2>TIBai?A>~=dyqU_N~d|Qq_**KzD z``P%DPFt3jmmg6t@vXse0;q^YBK*rUpPAeh^us@HsR>@t z;R=DEH31Jv^-} LvOuzVifl{)L&pfBmib_cpY6tVs~DmGkOT!T&gV&ts$KvBh)jHN}bSefqe- zjuG@40c2w?812O%A|b{W#9+$!;w!Gb`i6Pzgno+eGyxuc_&1Nd{lS_C9iFIq O_+WG3o{SMz*0#uX_t`|jhr#rBTbD{=PIvWVKGRPS<70iQA11Wz* zoyJlg{G#0s6Tf%vo@wV^2wc(WaOLIagGQ$TlSv0QGkS-JR5A&Cs1vxP0$okju;kM! z`@99SPh=nVGkj+WFyq3@R!lo> r0A_jp_ z43eSXSoT3b!*`YdgrDE{%d@9X9{H&xq#z|5ASs*D4xNf*A`ZdMy|8Xu&CtKiTK3zB z&kMPN8$R0HF@1QEY5f(a73@B7XkJwytVD%mqVb*8H85`c7z`6tM*)2U(gY?xjE-Sx zOZs5{!go3f$l$rBAA9YQXBS GzYkej0egB~@2L~b5H#Oa164~BZ7cC->H zNJ>Vq6%PO}XM;ct;MX^wpIq_TPXAe_4{v__Uvo#tf =QRHCqsiQZ%IP=+)B}u(l zKf-YWAhxahc-T*FyK{H6vzZBhz6C<*M_cGpv9Yw|10pU2PrOb1&3;Q(y_zs^7#$ z&I#Z$js)Idh3`*0J-(~9HBi^=wu-?vz~BKUpE6>jtt|iCe?I)EQ~gT6!f^s1T3eg7 zw_bT+xMgn@+rRm`OW>j@6CtK35DJGtq1R{vx&jf{-Q 3xo_A`na?2JyFUj!A-|vUP zC3c7>5}?H55ROD4iW|Ku(FlZMDe(A1u&Js6s_WaKy1EvRN5QWvhXS(}bU9Y|`7iIg z`}`S~KCNE9-y$8ie8;iwgV*h=zu0=Er=!K5Ta*ts{m(QIc%E$%#)^8a2J|`|==3_! z8w|KFf!Uyk!NoZ+X7~U&WBgFaEh+`Srwv*i0nnk35DPm_U;E|uN7bL`xA;y+0U0=2 zTmOUJ{7_PeR)Y?rsPezq!MuO)20}~|2m+6Sm%(C066p1y)oCFD66|U0gvz=$m^!Hf zF1uhd=oANx9%6^VMS0(fXM0=&Ag;Uq8e5?pys#sjfcn}77&d$eJp0ZnShi*htlzu~ z+B_i`SeV1QeFLEgY^Z92nRA!Hmpkgv(c{2uF~MF(1S$$Gux@KLj2b-zhLx3t-(K?R zW9eV$zc{V|5GHF*y fjUBZ+CTVmsP b zjt-(0BZaboL!X>@{xyG6Pwm$@P5>3-Pn+hs;unvdE@@0Hr8zoCB;ugN =|o>w zFbskuu~9yEV83i*@dQNB41~gA)B*|W{xy6XY<~vF32+buN!*@F#+jgFN)#f|7~70m z2{vro4Ry_(;Pd(*ib5w)WI$cUL>K|1g%mI7*azPdWSsy@-<$W;XY-zpEqnFx)O%0; z*1!DSH>Rr}^$Te1CCwbKIlwz62QSZJg!`{K2L_cDz<`oG(8v-a0cijtN(>2rp&z6Y zi6dtj^&@1R0Af6tC;RHfsG|;?^;O11B3Z6})Gv@fWc*qlLp@2BL6l@RA7FQV2h=&E z(BR{s#hZYJpbVQ@6j;5vnzjB|3^fL-|0G5aDPA ja=yLP-D5yy>12v-%8U z$4}hkiiqv9R?pJE$-}qjSfJI(X@O6+*5NItiJEJ(K%PAZwSg6k1_Lt>1db=+$Kd_lb?|Ap_2Rc)d+rzPgKr7? zh5#u21cq9KWUPP8r+jnP%(GWDR()RS-uOn9cjvT1y9rJkSq|HFHL-|*!1EmI3MPjy zg9PfbPiviUQeGakxNBj-wpSj1?4S31R#&?>Pd%kyqt8qLy36SYi_TMDV#oHamVZC< z@ITi!uUghAw~X)xU0^LR!vMP$it?>6dT0rJzNJPzI}OSaAfN*a>#AB|kWm1uQ3poQ zLDDF~diR&7J^I2uozFb`=&z&EXulqD?GpmTlPLdWx~~ME;r{;BEaQ`9i_7LMo%PBV z|BicO`Y@Nku#S2FMV$r`k@WrL+DtH_q7Xj*Y$po`2?qR}wlAyOAV-rzlb}tTGZ|%_ z;z1B(aHf3lapl6l{QmJfcWhX_WCHtQKSb6EKvS%#X zg9#l%(r7iIHZY)rsKfA)29AgboG7rrE^G kc>uS_{0*b zBZV3ut0v&JS^xUk^2+7!c=TP??)gxn#Dl2OfTzO+9ebJ~90 ?pDvhb1>X9 z>2;8A$4J0v1X-hH;{wZ?TfmvaBY|R|#G)#>$;nMSx-_A*HPKW!Z{9O6vjhDIStkIG zaX&HueLtE}8`) k1wT789AAN95^V&ZJbZ*nZvwTHagq#8k kP3txJElVj5(DShZGyJ{7G z2xJkp0TO`i=Z4{>bz4#3Y?p!h|2Nfkz?{b2;LV8vhnhq{yKgH*0u9QA+)^v#mF57i z74{KagW5uep=FcMSTTReE9-*6V84hCdX)elyfO3K<*)vA?#Hh^wtDvSk3YAoaz|-W z38V1CEDA_O{SZuI?9_{xvvBs R_Rx2I7|eE!=F@LCBvTCT3m@niTqSh5%Ggh`N^PvpvC@VY6pFbtn6v zkI`!cSi9o=%Wa9y`6rfZFF$3FdGg6caNCl1o^R@CuALGNgjipI+vlaZb3- +ZQn5` YyVe%r2WS^?@;C$JeW0`9|2YdzWd+ zlG%T%30L18)&^NgpRa=i#{02&j3szPgDHr1IYDPb%Mlg8SYQKAlY}H{iG&gd1^t6? zC YeDtZ) z2N~|_PJj$nZmxkhmV64i8i0GRzZi=0@S&oVXyB#Vg+7 jIK z{|R{>B8^R zgG&hlMglxYXf{OZ1i~nRI+7A8{2U_z8YSfLn7~OGMpAgVU&*;!yU;H%!wLB(%=y_} zf4)jRoOSdX0ah%UUlD8BRQo-Qmw7d|CV0`wYVGX87`p+kojexGutjwTnatho0SK9f zTzKZW|MP)*_^9y4%THdswRz{WZe3?FcM$K$7ROPzk!TcKD@GsKx~UR~q!B?tK~f7P zQbeL*^$Rp!iRWN5_O*JzU^al(h^-j!PeJegV68&;9Y^7(VhSWsD rEh7Q_PE?8Fz7Ap`1t#ZB{@ Hm469 z9zQs}0VF^G>YWj|cIt$E!YAQSG6jJi?1&;Gh|XQ_Z`-=Lf*m{pXm; jy(Y2!%rFoWr1> zX^UoWeV_RQy%LH{n>g@S12n$h3@)~DI-LgAY^}kkpg=Gjft?*8HmPDz3C7zh+L0hM z#VRw%LNIFuZs(4w;y2%4F;_j*BUZ0mG-%%9*-!8FSKgX59Xjx$;O{DwvW03rfe$_r zO)#M(FdU6xIGR&zglI&8aF-9F;RMG2C|s0nJef8hL{253pi^jufua#OB#^6dFLar; z^eJoU2@-r5eF0|(LMU`ntQwsFWtOr)A}Yv{Bt|_6{8a`03LZ@e4TNDBoZwSPuqYC^ zt@IB+f2d>LJBv23AI&0qMFW^(j d>wZ&J(ZrXwjUz)I$eg<+3FOPyXW%e|%@f+n+Y2_Fk1T9cu4M_)%2( zENu%TkC-ZaYP+FO9P~yMHri;dMGN{8Gst=l9C)9OW+$XjpiHlGsGaB^s-_jeW|-Sm z3)L9!llm3}5yF8mcw77s^`T17(LqkR1
Kr=u{`^@}9)IHZa~G_cSF=so_E57@Ukt*5 z!Y5Ki(9TCfVMqi6pm>9jP&=IBN(u(&PVkHyaH2z^5^vp%IhBJo(%_C&1MaR61UrIi zw{ALuAq4dT@H!oH4v8R96r!!f0)|2(@aRvZ)a&v1_XdJqlfAGsbmw&so?uMn#Oc0R zCKNi1R7Z#$L^@yKC1?q^npZD+dkFh-7SSsND61Iy*In(=O%AfH(KvK;xFO05V5dTh z_)3pqcO$kmw-2_~w!yHXT-dQ=FKPnMK3^b%V$>ujmk8$=JHK4EcFvRTPgX5jxUPQn z 9SkCYNG2mL;yK+M4?H&(K=WcP{?K4F z1{ sj6UV8=4sd8e?o&W=+2$( zQ81hJFc!@~9K)d~Y6S`ni|i(-YjeXkbi&H>%}|1ZY-w|X(~~Z))E)GI1{D}#pgCr} z{j3R=JY%=W0Y9^%eRd adBznlbvht22EN^2@;PF9U~9)RH=`E{2m%U)(U!bG|PIwU;H5S3IQ^> z^r{=4tthewO0k8T%_b<%GlMS@hIUs7svBCN!|8`n)Ph|BmKtWpC@X@Vy4#0p`yg{b z?T=cqh%Lx~j#LIrt?FfPcX}X +Cc%KV8k;b9+dLp71mI+uIdlXP7DrvWhy;d>mXr(zNY4%;B&iIXj>Gd&xOhzQ zVYJiY16kzIN5BwMZ}u*p``pFV?YqyCXtWV+3@>XyOI5`s8AS9UXp=-4Y??gJgj1(% zWEajNdX)f#q@?f2&9Q+N ~jSnN!~X7F1{us zqW9E6Qy#nedl$?&_kydovkz3P+O}bGBpzb#WsLxOJ&&fujU?b8;#I(i4xM0-nT;k> zAaXq1Hta mkx77dbgf9KM&?VtW6CZvl=0h$XD zZ&V{|L5I&lMl#SKQ7V!|GdS|!27@8%`Ek8YfVjTs*2=aBhJaD<_(H&89E=(kP8wbU zdFXZfoY?ZvA#!6&4Mq~s=8ZvzHwrCXUTE*=gtoSJ3X`B7?R;y8gN2L{Y~`VFkj2qa zj~8uy7(^6eIs{8QdPVw*)*0i@`|-mMJn_K9bAEV0m9F;}%*%BI>+aRsP*^%_g=mjO zn+~j{CJ1zS7?}h#1sR8q5XaDMb8Q{$soM(zhJw^vNM?aU^6HFu4g7tbVSi7b2BVr? zm0w=c;cL2xdK}3?6wv7;p)&^(AyLmDUZY4ZuR3|isK?pCETdQY0lMRrH|Esc`}@Zi z?Acj4+Uw{Xkc%OgnNp>Y46|2mMf;b69Giu;<~YX5UA{1?ZHyc+dI+KeM~I+Z4MeE3 z1vLe>{teD1h-vUW3O>`~Da;~=PbbK5%AhGLzc=dav#$Hm-TO &`@t zo_5MIg2q?LgrvZh-yN}l0$TKAYEpjKlS1FZNWF&CrHmgYVrW6q-OAJ*ETWLfLgeV| zaWrLP_IB>dCHFx_qLHenmY*R2A>~iODP^bs |gJ07f^E~ z4(&VW3I6r~*h_NJ;WIGd(Fh;) (}P>2f{IWRII11iKa=s<1K5pD&UObMD6 za$V?Ei{8H_*T(u6DiWpdJ@mkHm#9CLb!3$Qg!i6)=q*Q0 Q(XSh^63vfcI$`cQX^ &A0_aM?{O)URb7z3u}X49c7# z %0OSQ{AZyK#6pr2vp{g5CKKatA58U!Z z(O>`c`e{GB`rhY`mOL2 k-mSb z5yX0o4uksER!E5{B$|K`fkr*LALvtpnbWUQcl!)kCqVb1VH#)e?hqX z_4{(I}41zuZJemc8d;>%( zk%U+z%Ek=D2!?-t1LWrvt!E$hA+k;Y)(5T%JxSfdxMdR+g9ZnTvg~G~cqo(v^%tOk zsn-x4dnRle-en;keopqC&aL#e!-rcpLuGpd8^XoAPhapbV1HsP1rc``xYi&PcZzUI z$OgmsQ9Exs<=XLgT>h(}7hiJyKhkIS9kNaULdFju1 j9U~E6yy7&pkQnDyCcA3co)X-Z;7V{HbWqN xzKi8-<`h2DIn{_#+TSfjd#S z-mVaMIz!-hP(MVH#r@P*;qC}R*cAqFM&X7NCc^Z=gP|hV3O2I|@G=-Rz(*6NU-en~ zhx!Nlh5%}w;JzXeSq9A$h$ckY*BXGZCjv?c?Z4`q(7=&KFM`pGp&Pb*C7NJeyD>Ba zXdTdp0nj6945svGA(;^Z )RIxrR*L5JU^8bY)PC5?W{8V!sXPy_-OKkvg=9}6#i z@h`DO&;8|o_QgI%)(L=t5lCwC2PIfiS8BGgX5G>&I^(rwOTHc?>heu-`?_%jo@1$8 z0iOcFK#WcDq&xu{yC))O@V!D=z(^DVhh(N+J{hkQ#p6*&2wXnulIGc<5QUany&mjV z6HJ*f2=+9!pt)!iJKO3X`)u{{3)w~b5ZNaH#^5|YOhSsXXS1vY9F8uzwqwhkDQw*| z@~71sz?^RdozaL ^ zXnl^3`3f4V7R*H^sPzZ1#nV6&EjUTZFan!kT9jji8ebf2niRCJfB(&G>sAg{Z>>*| zbpmt;Pi}XXv3Sv&H?&8(Cdeo>jjUn4do-h0uQT9#6X^6B;8AEKbl$87jYa@bKyx5y zSf>#i#>Kc^q{So5Iy4trb{$#LgAQA{tVNR8^svF*0{$qCa#9VUbfhCA35u)+C@`YN z(1;LIf~LKd+xm2ZNY)8J>XbmCU}juBTk!e3;^z7_*QfO9#h__ewtK$>y{GhWtwswn zuS20qc)!@XQH2vx(i_@%Bn+LGAr5Vn{7T=GkQ~(WC*%uIPZ>_}^av$^e$y=B@vZ;_ zBT=vz@p?+<@0QJf?NarWtfLPIpfUiFWbt(N@X3eoTo~k)$sFotnyu4a_}#(A>roTv zbw+ep$@U`=@Z3!Q1-++U!^|<-C_$8Z6G E6l+Z#ei4H5zgK$zrlIbywOi;}PYkgg9pA?nq6BEF4V^R1t z`9kQ{{o0#&^R;Kkw*)(;h^T_Qx8@^(JBqOi5 yY0lfo#5 cp^;#B>lt@rBHxQVvb$_lLo-$4FjjQ zKbFZ=%r^Rj02$|xM*_rX8cr{xYQw65&PdCT1>MoB^H@xQFiqh?mDRMq9-1l}z}eIe z;c)l>=ap&(_T-tjl} zYgtFu2@p#OG^dJzd;ld2Cg788TQ2t`Jco|S9tJcr$ZR;Ob%9GjFv)?@EP;sOTRfpK zHNPJ{>o9Fhh8b7heMYBL_ENn&0bLjoxV=H>a{Hjo8Gw$U(9Gu!yXk_PfAt&nD}92j z6M!m&p;yG-$MBU`vvPV9se~kEs(~F1C?%a{`X&Q3tt<%!g9wy*$Y%XA`@Ub@v&7DB zNJJFyw)>&O-2toCzW2W~uDk2zLDQ}u-f5lm(+;8J!GM0?gZg3LyJF~>7oBkKb$9i7 z(WtBwfCWXW%FNbCAdhZV1l91-fYf;$36P>`ZR}Q4V3GCgGa42qkG{LTrx20)1Y(gm z_*+~M4a6a@C +ktCq#n>4#`vNPEoRl8oTr8 zq*}E3>^&W!MI`Y*HuKoq;Rb)F7wlzuU@5SHAc{<5&^0J)m{Q$C8W+6z@hh{Rnfce7 z*+u#pvQ7XVA8tkoPze>j7fZ#`u?g0n*~5Q5Ie zPPFUkn 402&ngAJ~-U4^KkNGq90r@QTiwHrJRs1dwbTxHB^PU!Hu4)8l zZ3mn% -`FSq5(2t6DnKw+FMw=ZozGP-Ft?|!sr8pBM=JtnV`BmaXHv> zbBk=yUfTvSJcnX^s{TPWeh3^VL%zurVzY~BpB=>qhnj$ NaRaU%=<|LM*C4iszxQq5uU*wE^*q-2M(Kf#Xrw!Zw;Vz7P1J6^Y|+Z372h z*V*EPq=G^>NMJOgQ)rM`t?$ jYr=^eO|$pGvCU zc5mRb`OjH>O2<$(0MQM}C>oz|3}lH1Td@)J79LvbJ3-V5cz ^(BywTLo;5eByZs8;Hv#y>n5Ud}wbX;XgT`>`B&Xs$qz90O zgL#^&ZIYRqrgAQn5+-OwbWHKCx{*>6L>M%DVx6{!^&s^o38GZ u-Ja)qkC?9_cs8IsuZXTS@Hcn3~@8o*CHNS$#uX?15x7BB&LCr)gn=1~j4; z_&7!+`n2#!17siyVl=O?WTQ=QR*w+WYocc1Bnf1y2CCPwvM+4akRA}!1jXi#?S0ng ze>h}c0}z yd4XjD 0(3#L8Qc2B1yMD z4Yf*(r>1!IiVx0KkMs-l0RhrJKqdl6N7u&|l=-{8Y5f%`Y=k`WixN3WFeH4Ms7dKy z-4jAWzA$J={bTq?NOrp?PSB+5yKoX4OkmyDNPx_0p>!Ry-2wq;daQv*$ Ptuw zk&s+8!zkzoL6_GHOTKvjA$DRvLEmTqRJnrM8N)^gk1^hP_obU0O6LjG3@Ja4s&Y^< zrk+4mzmwr8GXVq=aHbQ8Aj(=c5mgqt-6w`GtjAC#f!3l2N2ePctxgOnJE3iF8?;lW zP`wjet$ql)BM^_#C?bHz@9I};3-=iTVwsV^Ug!&Gb#`9W) MJ1OQ|@1c@&;lNu$S0CiK8abCBT%g1N#6I o3YfT$CTV3FH1waXgz}w{oPnR28FxvmjbntEw01?MvwKEgI@%!UI)|Y`` z;jDW9NiNPx&QfJDiZZCcRI=N(2xYm$I?g)f{5RFzeu3;0KoDip0DLS8VSGRetrT&L z(Y$B7>`yw&1S!D=eDW&nU>j%yYmo&kHWT^)z$SW<8qX=Rq939P3I$3lnq@%uY2bKP zyeXLo?^E5>i$S%1*?UwjY-)Ko6`|0iQndLr#f;7)BE=#p7;G SM> zG@&wv4>1r+ftWb9Go-0h{fZ5%o@`CDPtzN iX;-0(#u*lA<5Cg5>&Y?0*TatSxvHzO3 T03@Fu;}Hx_P96RAfa8i71^2NMe_h? z!Uiq=K*Pc`Y)cC^P|Kv$F9HcZk(nZnpq8DdEAW)-`Ss4G-T8q~y5V*U9`>7!gCUEc z&C!A0t_NeD0W2jZFqB!~qa7dLvwq#$6V 60?V6EC~QuBoZFuy`ro2v@1FcE z%^UxiGN&hX$QlW(h4y_RA0=}}6 ehI3loP=dGw^|S9W!@ zH jIcI_Pkf}3yOBBU+EXf zIsr1FlcI!yR^!M_nm861JY-nsxSTNyBAvc{zmox_a4C^E`gjTo5mi0eeNz2sqp8YA zchyf{&C%w$uekX7=V-sh>n=oH0RojcJs5a04J^e__!M!_iXlo!fp|bcyN`bxl3k~l zAZAct<*pBY^siZecyG?!|6H$ryU&qz0>ssU7FxuC+7jz5Ifl65$3MTZP&;sa^X^9Q zJAJHlJgNLhBnolWR&ywn6bgzKY>44&_b`zhI%?#tzB=3ci|5@=$Li{ ofLT1pkRB1iy2>KOX~gsGFx|Fzv{1)nznoz 96odf z9l~(3ha!6aC1zZB#mX~>PMvAi8!`OLLDHdcJN@9T_kgz%V}DN+{9Xk-t_XP1UI+aO z8_K1k5H_6|D$K)cM-`sfY%t`oG_L=>^Y{0iHTa@CQXTe~yT%P(Bm>nYrZA7z3?$O{ zQOI;H+9-P{x7f%wo1In)6JdyH$mi21UV7P;Ctor7$LHNS?x%PB?sx2F`xrfYG~W<6 zT{iWeh(GW|*}#epL2p_#c=YI Hz~Q*r3XSr+>H9QTh3~4b_{V)|G^U zNt3Jo`NG1H>;lIauf6oQzt7*i@V=x@0ZA Apv zXI^m5P5;;5`Tz*WlmK^Jb@n|CJ2pK*!@PtfCt*x1fho5b3X03nV8*tT=32|Nf#%YN zJzHpzNU#nX VNGqFPiy>x0~G!S8b$H^N|in zfWk`D5D{YN_*)IOM4mCfa#;S5Hz$oa@s%^qx%7xdD0gpIHQ}vyUzzETI!^XsSQ$x# zA)F$I7JUn{5b%pHRJ15^}K$GGFe-QbNVWuD1TZ?9( zblAkJFT3%cx7E}8Esm)VV9PBE9%*=&Peh@b%Yk~o9)*wLQ!LK>0yZkwZR={T%l~he zSdT;^{JKq>$G@~-1;+d-FlhxSFv?&+ZR6)97(8^soO3R?ZpSy7nr$o}vpmF`y#}2Y ztX31)EqX9un5dPgdWZxCC4+0!Q~NcJDFN~=#sIp=>R!)?qOI&o@ZgW7*9y*zs~?Q^ z))v0Gdi#In8_awAg>l maXv0`>Vl&D!--O2`fKa50x7?LT6X2RXz1< k=TC@PDjG*pROZMZzy^#eQl!l-(& zi60Pq_XNMk3p;jIPEdEh5gg4mQ$t;8lfGfym#})lYp{CNN(lRX7~(~tVdsvk*@s^f zbLPH2cIjLHnfb-am6mpQ01Px25Q$8E98q-qc2-uy>^XBDR!{HOIHm+BuNcv;)6%kQ zxoGTjfz#N4lP&uQL1A=|R`kLL^%ZUcLjpatwVy2guh!A?2Vz4q1Nr3n=gUculjyBu zD<$}Ro^O<^U$=Sfts&GNJbHXv_cpVNU(~%#y^X;@1Y9l;?B4$6jLOfK7OG$Aw>YK* z;Jl4h8k0JcRuwuu>Lh9c0?oUp9>649Y2!%1Fe;) +rBSU6DKJu1*_pqF2M@~ fg3vg3`1-C*D$}iPdc~@HL-L z>6riU1OI;Diml7%JU>zWN>*^p8D}f6Sf{?-hs^pr0?h?OxIBK)@d{f%kW=k(PjI!h zLQ7}M`ReWwK`M3amd*d^dhhSQ%n#OCZ2}6UhszV?$}BvLmb-) &wQwMOT3 zJV&*oz~MV65kwY1M4yBF+jukzZil0=sj)%--oJl$actKITZ7v^{7E9{R?j{VGzU@B zuzt*%B`>euwP{V(>IWZl0*o6q_6u4P{QyujK;D3=_d~%DbcBTTU`(15JtB$z!sg8z zj@k??eDxpqr* ;m-^|)n@?%bb zl7S;W%q=>|`9;B}s7g4W{yB>MGp*;~ar*<2g0{xi>FUl`f?B_~rRol2^6=$f4hA`f zZFnD{&KCImvybjl4}T@znfLMtNA1p$1~CDn@{%yAn1i#1$Z-Bp0dg@KG^4*eA)g0> z!~x&c$nh=P$0q_kxB8wyBxu^Xc1%@OWmY1HV@?2C#jBvC^ni7G2^?wxwz#5-h!Te` zUwq$y%;8YGeT%hn=k~*T(;4{MYK!BZ_M?7ieze!sjtDq|={||D*Kt&R4xcW5?@`X( zv@elW;4vD|@gVo`B)u4Mn7)RKNdZc0APxbKOZ0ZNWo0S(V@?2qsi1JN`gR{Us>P4L zMk8h!pjXVC!monQ eGe?!%4RsLBzAfVx(4l9xc@;$Le$#BYjTA;1YIp1H1M z@Q_Azmw|~!G_V1a?pol8K%rKyUo(8mNAtci(Zp0(;nMc-=a3+%M0=e}fm&CTC8B1K zNO4T~?70j4fmxwFz`}>iB>V|UIFM-dc-{5@w%l%-K|#TGpMTgLM4v!B1)csFnxQDP zASo$Rnn2U%lac}S_Tq0Z0cbh2;bX_mIXJ}QL>*1GgglD|i6G$vI`rphFh!CCFz6(p zmBtIKT9D#lSh)0qpR1?zh_dnl9z|n`yU` &Bd5{-izg))#D zcSeM?@Y4s5=slVSjB?WY&^rD=Z=SlD9VpD*Z9%9-(?TZ38{XgDO{#?m;l+DvWkQcY zog)mLNE*r^Adph=C<3%%bQF%K;x?Nt%s%L4^a=q8BZrUtTmH~NU@XlCK~}SIaN{*r z;3N~64H6XPAQ`MWu%n=9E3na~N5SjCVlslHvw)zp1E;ma-d*)~zCG(-C$S6lfG7Wa z-%oe!+?%Lz#G%crfGeOtB!UiJ4|bU3;)<#*9}&gY= lSK(a%7 zkOxmxfKZIXYw!?B2oOcP9m7!2i(>0SN3H`&;0wiITU!`*; uguR$@9oFp4;EJZ^P&4TJ6c=VKm^SQ)(9yU zb}}W@{d%lfrU1E-_5<+V$Qh*Hyy%v@pZUYXj~+H0{LQ^Ter@U8+1DLPDq1xp^LGDl z(W+>ARx*Ziq-eGA^l#HW&^yXXqFZ3c4gzIJS%;3VSh4ccdi7ARpw~12;l|s3am%RT z<6oCW5e#J|U>Q;hvT=V7C@09wB=EGM<3qUwJqcgLhp5ZT0jJAh+qm|lQ`N)!5DR+; zdtHw7C=`OCivUWP%ENksxCNs7H`+%}I}%MNqfmD)%Lznbc{|&_($8>qIfnG4QAmJv z4YuKRG8-X;riYeEB;O&GOr(=v)X&qo5Qhq%Kxu2^PaM 90gFw9yCXOv^QVf z>6HfgMJ9_GPMvo4%D+GM>}vIJui?<<){9uV;I&J4HZ)x6@wqQ*Y-={gG4zfHear*q zB@7d(P7jq{!3Ug*#~>L?u=V$K2BW{MsO-zq!n|8 g5i=7oV;eJAdxme|9?^ zGn)5SiEd{XC=n%{hMsxws9ne;aIC_&2s*1Bax6xV-JZW~=$NtZopsUGFCJ+*`ntNB zfr~%<;Nq~Od33<%FA0YgtrGTGlz2jmPNpsz^2xD8N>1UOqiPc4iXti0>QlTxl}C7v z6Nw0HRssH @@53b@t-Yt;0u+{bJ0-)0ThTrSZQBvPyvNXliV*eY|w` zn61^jtz8}M1v&O2lZK1ysd7M@+t*c;XN|@p;Wmpk*LCU%Q<~1d RX1q{QO8?Eg5qh$8j9TaU9=K0Q^5-thA+4w@1wY0000 u -- Gitee From 393ffcaa85dc3e3b96e2a8d17a0b2ff7bd372f55 Mon Sep 17 00:00:00 2001 From: xufeng <1277032935@qq.com> Date: Tue, 5 Apr 2022 15:27:06 +0800 Subject: [PATCH 10/14] =?UTF-8?q?1.=E6=B7=BB=E5=8A=A0=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E8=B0=83=E8=AF=95=E6=8F=92=E4=BB=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/IndexController.java | 8 ++ .../index/PluginManageController.java | 20 ++- .../xJavaFxTool/plugin/AddPluginResult.java | 14 -- .../xJavaFxTool/plugin/PluginManager.java | 107 ++++++++-------- .../services/index/PluginManageService.java | 8 +- .../xJavaFxTool/utils/XJavaFxSystemUtil.java | 121 +++++++++--------- .../xwintop/xJavaFxTool/fxmlView/Index.fxml | 2 + src/main/resources/locale/Menu.properties | 1 + .../resources/locale/Menu_en_US.properties | 1 + 9 files changed, 134 insertions(+), 148 deletions(-) delete mode 100644 src/main/java/com/xwintop/xJavaFxTool/plugin/AddPluginResult.java diff --git a/src/main/java/com/xwintop/xJavaFxTool/controller/IndexController.java b/src/main/java/com/xwintop/xJavaFxTool/controller/IndexController.java index 9432362c..7e228b33 100644 --- a/src/main/java/com/xwintop/xJavaFxTool/controller/IndexController.java +++ b/src/main/java/com/xwintop/xJavaFxTool/controller/IndexController.java @@ -93,6 +93,8 @@ public class IndexController extends IndexView { } private void initService() { + PluginManager pluginManager = PluginManager.getInstance(); + pluginManager.loadLocalDevPluginConfiguration(); loadPlugins(); // 加载插件列表到界面上 AppEvents.addEventHandler(PluginEvent.PLUGIN_DOWNLOADED, pluginEvent -> { loadPlugins(); @@ -112,6 +114,7 @@ public class IndexController extends IndexView { PluginManager pluginManager = PluginManager.getInstance(); pluginManager.loadLocalPlugins(); pluginManager.getEnabledPluginList().forEach(this::loadPlugin); + pluginManager.getDevPluginList().forEach(this::loadPlugin); } /** @@ -275,6 +278,11 @@ public class IndexController extends IndexView { private void openPluginFolderAction() { JavaFxSystemUtil.openDirectory("libs/"); } + + @FXML + private void openDevPluginFolderAction() { + JavaFxSystemUtil.openDirectory("devLibs/"); + } @FXML private void xwintopLinkOnAction() throws Exception { diff --git a/src/main/java/com/xwintop/xJavaFxTool/controller/index/PluginManageController.java b/src/main/java/com/xwintop/xJavaFxTool/controller/index/PluginManageController.java index b356e578..b7fc7824 100644 --- a/src/main/java/com/xwintop/xJavaFxTool/controller/index/PluginManageController.java +++ b/src/main/java/com/xwintop/xJavaFxTool/controller/index/PluginManageController.java @@ -4,14 +4,12 @@ import com.xwintop.xJavaFxTool.controller.IndexController; import com.xwintop.xJavaFxTool.event.AppEvents; import com.xwintop.xJavaFxTool.event.PluginEvent; import com.xwintop.xJavaFxTool.model.PluginJarInfo; -import com.xwintop.xJavaFxTool.plugin.AddPluginResult; import com.xwintop.xJavaFxTool.plugin.PluginClassLoader; import com.xwintop.xJavaFxTool.plugin.PluginManager; import com.xwintop.xJavaFxTool.plugin.PluginParser; import com.xwintop.xJavaFxTool.services.index.PluginManageService; import com.xwintop.xJavaFxTool.view.index.PluginManageView; import com.xwintop.xcore.javafx.dialog.FxAlerts; -import com.xwintop.xcore.util.javafx.FileChooserUtil; import com.xwintop.xcore.util.javafx.JavaFxViewUtil; import com.xwintop.xcore.util.javafx.TooltipUtil; import javafx.application.Platform; @@ -20,14 +18,12 @@ import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; import javafx.fxml.FXMLLoader; import javafx.scene.control.*; -import javafx.stage.FileChooser.ExtensionFilter; import javafx.util.Callback; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; -import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Map; @@ -169,13 +165,13 @@ public class PluginManageController extends PluginManageView { } public void addLocalPlugin() { - File jarFile = FileChooserUtil.chooseFile(new ExtensionFilter("打包插件(*.jar)", "*.jar")); - if (jarFile != null) { - AddPluginResult result = PluginManager.getInstance().addPluginJar(jarFile); - if (result.isNewPlugin()) { - pluginManageService.addDataRow(result.getPluginJarInfo()); - } - AppEvents.fire(new PluginEvent(PluginEvent.PLUGIN_DOWNLOADED, result.getPluginJarInfo())); - } +// File jarFile = FileChooserUtil.chooseFile(new ExtensionFilter("打包插件(*.jar)", "*.jar")); +// if (jarFile != null) { +// AddPluginResult result = PluginManager.getInstance().addPluginJar(jarFile); +// if (result.isNewPlugin()) { +// pluginManageService.addDataRow(result.getPluginJarInfo()); +// } +// AppEvents.fire(new PluginEvent(PluginEvent.PLUGIN_DOWNLOADED, result.getPluginJarInfo())); +// } } } \ No newline at end of file diff --git a/src/main/java/com/xwintop/xJavaFxTool/plugin/AddPluginResult.java b/src/main/java/com/xwintop/xJavaFxTool/plugin/AddPluginResult.java deleted file mode 100644 index 045d7f73..00000000 --- a/src/main/java/com/xwintop/xJavaFxTool/plugin/AddPluginResult.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.xwintop.xJavaFxTool.plugin; - -import com.xwintop.xJavaFxTool.model.PluginJarInfo; -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class AddPluginResult { - - private PluginJarInfo pluginJarInfo; - - private boolean newPlugin; -} diff --git a/src/main/java/com/xwintop/xJavaFxTool/plugin/PluginManager.java b/src/main/java/com/xwintop/xJavaFxTool/plugin/PluginManager.java index 27dffe21..5172efd3 100644 --- a/src/main/java/com/xwintop/xJavaFxTool/plugin/PluginManager.java +++ b/src/main/java/com/xwintop/xJavaFxTool/plugin/PluginManager.java @@ -2,6 +2,7 @@ package com.xwintop.xJavaFxTool.plugin; import com.alibaba.fastjson.JSON; import com.xwintop.xJavaFxTool.model.PluginJarInfo; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.io.File; @@ -13,10 +14,10 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.function.Consumer; import java.util.stream.Collectors; @Slf4j +@Data public class PluginManager { public static final String LOCAL_PLUGINS_PATH = "./system_plugin_list.json"; @@ -30,17 +31,13 @@ public class PluginManager { } private final List pluginList = new ArrayList<>(); // 插件列表 + private final List devPluginList = new ArrayList<>(); // dev插件列表 public PluginManager() { this.loadLocalPluginConfiguration(); } ////////////////////////////////////////////////////////////// 查询插件 - - public List getPluginList() { - return this.pluginList; - } - public List getEnabledPluginList() { return this.pluginList.stream().filter(PluginJarInfo::getIsEnable).collect(Collectors.toList()); } @@ -62,20 +59,38 @@ public class PluginManager { if (!Files.exists(path)) { return; } - String json = Files.readString(path, StandardCharsets.UTF_8); - JSON.parseArray(json, PluginJarInfo.class).forEach(plugin -> { - this.addOrUpdatePlugin(plugin, exist -> { - exist.setLocalVersionNumber(plugin.getLocalVersionNumber()); - exist.setIsDownload(plugin.getIsDownload()); - exist.setIsEnable(plugin.getIsEnable()); - }); - }); + this.pluginList.addAll(JSON.parseArray(json, PluginJarInfo.class)); } catch (IOException e) { log.error("读取插件配置失败", e); } } + public void loadLocalDevPluginConfiguration() { + try { + // 系统类库路径 + File libPath = new File("devLibs/"); + // 获取所有的.jar文件 + File[] jarFiles = libPath.listFiles((dir, name) -> name.endsWith(".jar")); + if (jarFiles != null) { + for (File file : jarFiles) { + try { + PluginJarInfo plugin = new PluginJarInfo(); + plugin.setLocalPath(file.getAbsolutePath()); + plugin.setIsEnable(true); + plugin.setIsDownload(true); + PluginParser.parse(file, plugin); + devPluginList.add(plugin); + } catch (Exception e) { + log.error("解析失败", e); + } + } + } + } catch (Exception e) { + log.error("添加libs中jar包到系统中异常:", e); + } + } + /** * 解析本地插件文件 */ @@ -98,48 +113,30 @@ public class PluginManager { /** * 添加本地插件。如果与已有插件同名,则替换已有插件信息 - * - * @param jarFile 插件文件 */ - public AddPluginResult addPluginJar(File jarFile) { - PluginClassLoader tmpClassLoader = PluginClassLoader.create(jarFile); - PluginJarInfo newJarInfo = new PluginJarInfo(); - newJarInfo.setLocalPath(jarFile.getAbsolutePath()); - newJarInfo.setIsEnable(true); - newJarInfo.setIsDownload(true); - PluginParser.parse(jarFile, newJarInfo, tmpClassLoader); - - for (int i = 0; i < pluginList.size(); i++) { - PluginJarInfo jarInfo = pluginList.get(i); - if (Objects.equals(jarInfo.getFxmlPath(), newJarInfo.getFxmlPath())) { - newJarInfo.setVersion(jarInfo.getVersion()); - newJarInfo.setSynopsis(jarInfo.getSynopsis()); - pluginList.set(i, newJarInfo); - saveToFileQuietly(); - return new AddPluginResult(newJarInfo, false); - } - } - - pluginList.add(newJarInfo); - saveToFileQuietly(); - return new AddPluginResult(newJarInfo, true); - } - - /** - * 向插件列表添加插件信息或更改插件列表中已有的插件信息 - * - * @param pluginJarInfo 需要添加的插件信息 - * @param ifExists 如果插件已存在,则不会将 pluginJarInfo 加入, - * 而是提供已有的插件信息对象供调用者更新其属性 - */ - public void addOrUpdatePlugin(PluginJarInfo pluginJarInfo, Consumer ifExists) { - PluginJarInfo exists = getPlugin(pluginJarInfo.getJarName()); - if (exists == null) { - this.pluginList.add(pluginJarInfo); - } else { - ifExists.accept(exists); - } - } +// public AddPluginResult addPluginJar(File jarFile) { +// PluginClassLoader tmpClassLoader = PluginClassLoader.create(jarFile); +// PluginJarInfo newJarInfo = new PluginJarInfo(); +// newJarInfo.setLocalPath(jarFile.getAbsolutePath()); +// newJarInfo.setIsEnable(true); +// newJarInfo.setIsDownload(true); +// PluginParser.parse(jarFile, newJarInfo, tmpClassLoader); +// +// for (int i = 0; i < pluginList.size(); i++) { +// PluginJarInfo jarInfo = pluginList.get(i); +// if (Objects.equals(jarInfo.getFxmlPath(), newJarInfo.getFxmlPath())) { +// newJarInfo.setVersion(jarInfo.getVersion()); +// newJarInfo.setSynopsis(jarInfo.getSynopsis()); +// pluginList.set(i, newJarInfo); +// saveToFileQuietly(); +// return new AddPluginResult(newJarInfo, false); +// } +// } +// +// pluginList.add(newJarInfo); +// saveToFileQuietly(); +// return new AddPluginResult(newJarInfo, true); +// } // 保存配置,如果失败则抛出异常 public void saveToFile() throws IOException { diff --git a/src/main/java/com/xwintop/xJavaFxTool/services/index/PluginManageService.java b/src/main/java/com/xwintop/xJavaFxTool/services/index/PluginManageService.java index 757d8837..85671fba 100644 --- a/src/main/java/com/xwintop/xJavaFxTool/services/index/PluginManageService.java +++ b/src/main/java/com/xwintop/xJavaFxTool/services/index/PluginManageService.java @@ -192,7 +192,7 @@ public class PluginManageService { Map dataRow = pluginManageController.getOriginPluginData().get(index); String jarName = dataRow.get("jarName"); PluginJarInfo pluginJarInfo = this.pluginManager.getPlugin(jarName); - if (BooleanUtils.isNotTrue(pluginJarInfo.getIsDownload())) { + if (pluginJarInfo == null || BooleanUtils.isNotTrue(pluginJarInfo.getIsDownload())) { FxAlerts.info("提示", pluginJarInfo.getName() + " 该插件未下载"); return; } @@ -204,8 +204,10 @@ public class PluginManageService { FileUtils.delete(pluginJarInfo.getFile()); dataRow.put("isEnableTableColumn", "false"); dataRow.put("isDownloadTableColumn", "下载"); - pluginJarInfo.setIsEnable(false); - pluginJarInfo.setIsDownload(false); + PluginJarInfo pluginJarInfo1 = this.pluginJarInfoMap.get(jarName); + pluginJarInfo1.setIsEnable(false); + pluginJarInfo1.setIsDownload(false); + this.pluginManager.getPluginList().remove(pluginJarInfo); PluginManager.getInstance().saveToFile(); pluginManageController.getPluginDataTableView().refresh(); AppEvents.fire(new PluginEvent(PluginEvent.PLUGIN_DOWNLOADED, pluginJarInfo)); diff --git a/src/main/java/com/xwintop/xJavaFxTool/utils/XJavaFxSystemUtil.java b/src/main/java/com/xwintop/xJavaFxTool/utils/XJavaFxSystemUtil.java index e3e83bd1..02f32af3 100644 --- a/src/main/java/com/xwintop/xJavaFxTool/utils/XJavaFxSystemUtil.java +++ b/src/main/java/com/xwintop/xJavaFxTool/utils/XJavaFxSystemUtil.java @@ -1,16 +1,9 @@ package com.xwintop.xJavaFxTool.utils; import com.xwintop.xJavaFxTool.XJavaFxToolApplication; -import com.xwintop.xJavaFxTool.plugin.PluginManager; -import com.xwintop.xJavaFxTool.services.index.PluginManageService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; import java.util.Locale; import java.util.ResourceBundle; @@ -47,66 +40,66 @@ public class XJavaFxSystemUtil { * @Title: addJarByLibs * @Description: 添加libs中jar包到系统中 */ - public static void addJarByLibs() { - try { - // 系统类库路径 - File libPath = new File("libs/"); - // 获取所有的.jar和.zip文件 - File[] jarFiles = libPath.listFiles( - (dir, name) -> name.endsWith(".jar") - ); - if (jarFiles != null) { - for (File file : jarFiles) { - if (!PluginManageService.isPluginEnabled(file.getName())) { - continue; - } - addJarClass(file); - } - } - PluginManager.getInstance().loadLocalPlugins(); - } catch (Exception e) { - log.error("添加libs中jar包到系统中异常:", e); - } - } +// public static void addJarByLibs() { +// try { +// // 系统类库路径 +// File libPath = new File("libs/"); +// // 获取所有的.jar和.zip文件 +// File[] jarFiles = libPath.listFiles( +// (dir, name) -> name.endsWith(".jar") +// ); +// if (jarFiles != null) { +// for (File file : jarFiles) { +// if (!PluginManageService.isPluginEnabled(file.getName())) { +// continue; +// } +// addJarClass(file); +// } +// } +// PluginManager.getInstance().loadLocalPlugins(); +// } catch (Exception e) { +// log.error("添加libs中jar包到系统中异常:", e); +// } +// } /** * @Title: addJarClass * @Description: 添加jar包到系统中 */ - public static void addJarClass(File jarFile) { - try { - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - URL url = jarFile.toURI().toURL(); - if (classLoader instanceof URLClassLoader) { - System.out.println("DEB: classLoader instanceof URLClassLoader"); - URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader(); - Class sysclass = URLClassLoader.class; - try { - Method method = sysclass.getDeclaredMethod("addURL", URL.class); - method.setAccessible(true); - method.invoke(sysloader, url); - } catch (Exception var5) { - var5.printStackTrace(); - throw new IllegalStateException(var5.getMessage(), var5); - } - } else { - try { - Field field = classLoader.getClass().getDeclaredField("ucp"); - field.setAccessible(true); - Object ucp = field.get(classLoader); - - System.out.println("DEB: invoke method!"); - Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class); - method.setAccessible(true); - - method.invoke(ucp, url); - } catch (Exception exception) { - exception.printStackTrace(); - throw new IllegalStateException(exception.getMessage(), exception); - } - } - } catch (Exception e) { - log.error("添加libs中jar包到系统中异常:", e); - } - } +// public static void addJarClass(File jarFile) { +// try { +// ClassLoader classLoader = ClassLoader.getSystemClassLoader(); +// URL url = jarFile.toURI().toURL(); +// if (classLoader instanceof URLClassLoader) { +// System.out.println("DEB: classLoader instanceof URLClassLoader"); +// URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader(); +// Class sysclass = URLClassLoader.class; +// try { +// Method method = sysclass.getDeclaredMethod("addURL", URL.class); +// method.setAccessible(true); +// method.invoke(sysloader, url); +// } catch (Exception var5) { +// var5.printStackTrace(); +// throw new IllegalStateException(var5.getMessage(), var5); +// } +// } else { +// try { +// Field field = classLoader.getClass().getDeclaredField("ucp"); +// field.setAccessible(true); +// Object ucp = field.get(classLoader); +// +// System.out.println("DEB: invoke method!"); +// Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class); +// method.setAccessible(true); +// +// method.invoke(ucp, url); +// } catch (Exception exception) { +// exception.printStackTrace(); +// throw new IllegalStateException(exception.getMessage(), exception); +// } +// } +// } catch (Exception e) { +// log.error("添加libs中jar包到系统中异常:", e); +// } +// } } diff --git a/src/main/resources/com/xwintop/xJavaFxTool/fxmlView/Index.fxml b/src/main/resources/com/xwintop/xJavaFxTool/fxmlView/Index.fxml index ed83982f..cfad9491 100644 --- a/src/main/resources/com/xwintop/xJavaFxTool/fxmlView/Index.fxml +++ b/src/main/resources/com/xwintop/xJavaFxTool/fxmlView/Index.fxml @@ -46,6 +46,8 @@ text="%openConfigFolder"/> +