From ed15173b8ce5797f454e5f7c9d7c58c019407d02 Mon Sep 17 00:00:00 2001 From: hns Date: Wed, 26 Feb 2003 12:41:54 +0000 Subject: [PATCH] Merged in changes from helma_1_2 branch --- build/build.xml | 2 +- lib/village.jar | Bin 31316 -> 0 bytes src/FESI/Data/ESLoader.java | 6 +- src/FESI/Interpreter/ClassInfo.java | 170 ++++---- src/helma/extensions/HelmaExtension.java | 6 + src/helma/extensions/demo/DemoExtension.java | 4 + src/helma/framework/ResponseBean.java | 2 +- src/helma/framework/ResponseTrans.java | 35 +- src/helma/framework/core/Application.java | 118 +++++- src/helma/framework/core/ApplicationBean.java | 20 +- .../framework/core/RequestEvaluator.java | 18 +- src/helma/framework/core/Session.java | 6 +- src/helma/framework/core/Skin.java | 162 +++++--- src/helma/image/ImageGenerator.java | 150 ++++--- src/helma/image/ImageWrapper.java | 8 +- src/helma/main/Server.java | 20 +- src/helma/main/launcher/Main.java | 30 +- src/helma/objectmodel/Property.java | 14 +- src/helma/objectmodel/TransientNode.java | 8 +- src/helma/objectmodel/db/DbColumn.java | 4 +- src/helma/objectmodel/db/DbMapping.java | 91 ++-- src/helma/objectmodel/db/Node.java | 50 ++- src/helma/objectmodel/db/NodeManager.java | 391 ++++++++++++------ src/helma/objectmodel/db/Property.java | 218 +++++----- .../objectmodel/db/WrappedNodeManager.java | 4 + src/helma/objectmodel/dom/XmlConverter.java | 30 +- src/helma/objectmodel/dom/XmlUtil.java | 3 +- src/helma/scripting/fesi/FesiEngine.java | 17 +- src/helma/scripting/fesi/NodeConstructor.java | 12 +- .../fesi/extensions/DomExtension.java | 26 ++ src/helma/servlet/AbstractServletClient.java | 43 +- src/helma/util/HtmlEncoder.java | 20 +- 32 files changed, 1056 insertions(+), 632 deletions(-) delete mode 100644 lib/village.jar diff --git a/build/build.xml b/build/build.xml index 71e90697..c9451c10 100644 --- a/build/build.xml +++ b/build/build.xml @@ -8,7 +8,7 @@ - + diff --git a/lib/village.jar b/lib/village.jar deleted file mode 100644 index 745452530a9f607629ff8a4b3f5ee2ac3c35d0a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31316 zcmbTdbC561vMxNfZO?B!Yi!%L*4Vaf+qP}nwrv}0u}>v5qe1$=7&kjdD+^mQV>>ga{}ujUqx#o7|2y8*!rI!v%;bONhx9-AQ8Tc1 zF`+lIHgIy<(em&{Q+=$v%rH)rJR^%A2E!tfk_h&XBnmf#O3)W!gaTwI8AAmn1x*Lz za;kQ(fwF`LMjpa&Dx@Q6Ad1+p>As!SUInaq=2O=Gc>??O?o-ue#{)P4s~P_qT~rT8!D& zI{EVX0cWBhF{vn60cWTsa>zx3%uIa%OchBnR(|TL~2%fEXZxq+& z%Rh#&`2m1y*Q$)@IW6bVydwAT+@7--LbgbFvmBFKpA^)(L6yF{^POceSoH2CUHzs5}ZHf;PY^E$Qj%{1}N)Pfm|htw5h=%#Ru5V<4Rz)k5& z^nE^pbt&qHanvVqMNaI8{>V=JD{5k|}{>ZXw+i$9qD%n2!PRh#}r&$Mx}XA>JfLcnOTL<5=Rx(WHk9 z$k^u+a4*I3A>M%F^7y%sFY$3qpz(TX=OiJVC#A#3y${p!mFxaJ!WUW=+tP~YI8FbY02e#C|hZw2T>Qzsa zVii27#VdJYY^deWz*5ZYnnA9JZxcMmDi`Rs<|}yI#Xul z*9T}RRmGpwvnm~#UR63n71V2vnUhZ(awl93PmZ0Fe79AOyk_M*zejWC9)$3yf9>;8 z|4M|WOLa(t(xf)dh`$P^#cUKBmm;nTuEuB-8-Ee9MRF{PZxJF$WF$CkQuPfEEAI}O zQ~6Nj6F(Vq&!5!(piLn>`=S1u(4^uU*rd7(s1ds@aYRK_>&S3Yu{{tJqF3{PJgfXl z)^Aa+f1Gmu29cxrUGQk@V8x#I%eXxrHaJ?LBPJ=GRulA*C9`e;BnHFMnMc`i)xWS~ z_G4BjuBnshEwZtr)^SgwsdHWZthP3oi#5O2@z3e;X$Q@d?#V568Dtv@Xj}IaPsfK` z-c)tgaV?52V$fJ(d!6KPW6Hkz-raM!ej7J z#B64AO*3E?2J-lV_O}^07tJ!Z|2e{H#?(d5#nYwPoa@B)VxSv0Ng`S|CWvJFVn10) z5ED?OOs$5QJt>HcH`pBWv3C$AmQGU?7$OgYo_k#NU~KP13^NG(^io>Z4hVbD(_Q`w z9+FL^W|fz|OyT-|$>?<{4&x$Vjkn4O!nHy?|I^InP&&M_e)FtyxzOpAidJ^udacDn z0!-e(B$(xDSuFs|?QVMc+D`!SFTUo@w~ppnrORpub)-+aPD<4#yHb>wR``S}ydb(0dWif6DpQ7tjlM#!yY+Z{=1FppVCQFp0lZ`JHoZ3RKVj8Str z^?aOB?~?MJBb;-z_#48*e1Z7P6{VY+`r13534Pd<=9xp+C0;M*tmRnzHgU%g1ar(y zCoFvN(v6w-*>`wX(D07n;q8U1DZaAu!Qd{3t3gNADx_IYKggA+W5L)_rL(6D!!Tw? zl`iUK)ufI~Qnm>JPsZ}bpyjXHiaHuM^!AZKKFv~2iRpB-ifaio8@g3qz{-V9omi=P zI4dhbT9cM_w?~w>`*taNcczq%QKV2&sU9wY8!Qj29AV+bb0L7Gsxb3X>PGD$JCiHV zw9YKmRUODIase0TF}-NXi#30#OMU8D()?+(dXk}gL2b&4=S6je3uOH7V`k+xIIGKA z;ay~=l-;d&+t^XHv*ajeLbRYthNr8ErS zr#2oN99;@MhdJoVLL`%xo^Ay<8XX-d10w@clweam4wOpTRD7nfb15lhD@5?3D3CDY zyr4=7!?-Q!zr!N8UZ`-1Xrcd;n$xkGj zZpNP%0XAfQU*r;RNfh!wP`#*M6z7Hmeh_!CKd|qn_rKwN2lpukvLx<~P>DmU{r3uI zRL|7xGl)-zhWbed&Vsi3bM_TOt8wP!!T+MZv)rfkmj(4j*p`leXGgO+Qn3tXn))Op zHohrYN0g+@254(@Qp{wcos=EICkLuEuSSYWKP;)jhc>J*l%g8UEvh3ONfp0Uz_cuC zWGN6;f&EUzbu%iDcAJ-GwnJD$k;v0LglQCO>34mQsFN zO?oJlw$U`PoHSc2%BV{u0gQ2LtrpE?GYurp%op*5Czr|Eh+pOnds7aDa_20H#4~Ux zrMlXL4aZOg@(Oh3CBxUzB1^3(W2Np-`Oxh?mhN%*gY<|g@C%td<0daC-!WAL$Hqh4 zJ~pz2(7hpM4w}YiK*xtjD_}M>rqmaC=mCUX<+*3`LV7Y$?$~rE+~(MOqBI_`HHP_v zHqNAlzBGLjZN!&uPgc%|g}%T1MBa$IZqMp0Nd3gK-pOp#&^$#N7BEKSv{P=IWiLdx zQhb+((=0YdmvMh&47>DT?Ky-T6LrQvLg3K`=**W{6l7lIHlOiIqT1a-Um%`r@>S~p zz$Zho`GIZ>V6Z532Sd&*)UdRNXh*&($xP@;(MUG7DA~s5vHh_q&%3oN5uB64Rv`9; zhA2N%roG3Hp^3BR2`%KzLvg1qaRkm8OIws65Kl8K453VXa5sdR^n#Fw;zBLrT%sAq zM#E6e$c|vomV8^fDLYVTLkepaZ$Ej=SEh{IZsO)*Meg%g4q~7)tXRIVw}i;fgf25x zgrN51NLv`uky9r+MeRteHFQg7R(DFIVv-xeo^oZe#Eq}YWNz@qACFf1${UF)6E2hesnGAr#R-KlpkJP63FRf%(SCsK zSEcSkyc<4&)T!~eV&As})$uC-byqS_zGLvka`M0|acpLR!Y{7uLAV-n)_`y=zSAc# z^#Qaq4S6BA{4VwbYje`Kc=Y9nm95;Km4MrZV;>7E``oF&vZ?J%+XPSh%~EsH#&d`< zQ)c}I!e^3%|ARO9!shJ>$=3s$w<~P$ljZsJnLItCmE0?)_?7~ zFt~7)^dgMi+ZtgUusZrx1l&?#s{sNgISAx8PyJiq5L|C5Q00 z|Al%Z*-x?g5r>?zPPy_CU_u#Ip7+QGue4jBIN8Ob=n*H?UnZDa(k-7d8KzR^Ekctr z)GYUrP5`tA=Mm)ymNjH{tRA}veB#7a{1wIr#1HqOd?Niv>Lb!usn=BWBb9FQ4X+CC zh~s3eUO%mV1%c37r4Hz&=@GDDcupcp8&XC9ON37N*7zyCnd_f-jNYrSxhi+^a5QN`KrLpIR0$E;F>G&Ec{Iw1-8&SlY&2PFRUpZoY%=Kxvo*1xUW&x zT>sIx+@R+w+#*)q!HIC{QeJl2R}DmU|OD(#-qm8-Tlpzt_px4Ab0!x`u=P z(G1eoqje97uTc<<($1eyvLvgNCAX3{O$4D@x^ z%pt5iim@D| z%X-Jle8V(#Q`3%Ai+T_BwBSt_v2Lzoz80GS=g6a-=0X^FBjq(KZ^qXMD6zrt~A zyif)FPVvp$kphJW^9m(4XM+l|i2~kA{X3Tsj&Yb){P4Qs46@rJ>4CR~>TiRqzlE@l z1FX%gZv|{E57Js@PqqoZt_8edn^|x@!2P2q@XF%&t*KRfU9>Xkt5t*t6*K|u?wlU& z#vlh5VxfstI{mkknHD!7f~Te*Z{(jZ-Gc;eio_F1h0^Et>wUxFcm#@=xrvnF?bybvAOpMHx#Cx}GU z!UOeCDE4yLSy{TBiWkv>QE$mbIm`fuy!4ZDXZyikLb|gtmQ{NX+`>YA)xq?NM`Eh8 zu{EnEib!`Y#U*sZld1SbS92_@rY@+tkmgFH=|xL|rmOMRv^5vjLREcr!qkeect!Av z8U9M6fiFCGTS~3!BOxY!)tOZwa>Em&_^PW>j#Zl<;v%%R8sF5aJ8a6Y*4cJv%(@41 zakI9Xd1m#O!1ioWwB5e20?{M9XHIY9$eHoD_#Ip4j8MSbo{sp^!rc?e-Q&GyG!{er~jlSKRe_ z%VS8sx7w21y+7$0dwtF<5#X$fuIQRmUM&{y**XP_%UCW7**aHXE)-=u+hcCyz1}Pr z%h@_bi?diR2H83_i^(%t8e?n|&nj6izSy)#-%KqR!EKz2Bv4r`oN>Peiy1Rn3S(9y zSyYnfEsD5BFY6>dY@FL9EI6?-;Gb&HFc(EX#m#PJ^7Meo{tiohQnU4}UCcQ{)wIof zrt#&!oim)PKNI^16E4*~L(l(28=X^*EnsGg{dR=#8T!7^o-H-v5Wzn?c&3RfK-KA2 z%Nb{kt1^q9xZKnG)VYf?ctdV|S?18}=-(=m-{7u$#V{L?D_nF~8oOG{K__J9zzY+1np z&WMXHu$n)Owc(;DG$TV->+-d-x>G9+?0{~5m3?Cqzq8=9#{%+(E`aK zsPke;Twj5Ls<>5Rs>Q+9XvtFMmiYWPT(JzGQ!bAS`}C}p|NP8EZE~8Eb!r(7I#%l^ z67${ELWLe*hf+}|?owRAmTIr=b?)K@*0q!W{@rH1MG4(w-iu;lA*VgyIJ{KfP1}7o z+9-?Oi5p%zxT1P6@k6>*oD|VYgKFdTX-L`a)<_W;BLy`<-&tT5$tg{J&MW6a7dCs% z9}bF&6&f2vpeFcxp)ZHTPxdnXi-}jT2Jd;Oe) zQF9j@KJqS8F>X$YQ~7772FJ2h=Ym~mYNR%_O(s*b=L-&W-zwJI-$l%~pvGyK?H=^! zrJjBwKL?x(#AvJ?Q~|`9W6!46m}7NMgLWathOT#%qOLo}=6TYkP%DeO3K`UxCnnh` zRR^KZfYXu$n|o>|En7$m?8}mt?!BV#ZKhzyDhLJq~3&MgK74iQxZf>HjARSk%_V z#_8W{UUqRqZcqRXVxJjJ%hete{SBO96O#czD=ajuwiUS01p-f%%4YD?(arQQ2zg%+ zeTI}dJ;VPrlf5KU`ty_s2XT+ccF@#g8l-+;{mwmOh|FVNX~!MEYN0Ek%>bYW{<&Ta%gb#otiaCER;D~QF zl4DV-`VrhJ?-}8N`hI~96E_`ms&&ZkUx|*ire34a#NC8Qg{*H3pnry z=5U{WAB3w`Kml76GLUu>C~|V2sg|g`J!Yz$`EI7$0VA*5sj{-|@|NuVbSh1`?Y8Hv z&#db#S3BT{|Hcn^ngOpbl|fY~E7$^Mll+>@zzn)eBAwnq9h#Z!8erfC%}jnxY#K)PHeu33>EnuuC~$6yKVo;oyg1h z3YqX@{ICP~nLM%s{tg}A$@uC&A_D#zKmG#zH7zIXcC;;;ZiIjie{tD=|;YbjIcep4_bDLBqmT5AI=Y$+E=03?--FP%r zzW#tt+jvyP5e{wPoL%;nI{~euKepEX!WH!ip|<%TYx)V=wrlvvmgL5mC0T>NHw5kK zO^CVqKvw4kBhD~k#*{cg3BY8)o-u|?YLJ0R*BLj)ONvTIW6H1|L!37lTcUl!?p}x` zY-A#AP$6XIoH|Cm{Y)GRjP*BvAKn0{rqlXlCniOE@(f=cPG`S zQ^Z0ETq34-fv`Lk;Mw;3m1;%rcg%0ST9zpq9e6<#FriwujhhFW-dD4$`GHa#_0J>+ z%E9>prkGBW@5U)*QuQt#0y}|PB_Roxt!pf`K!$n7>gLU@^uEO_?c3Mo75A&J51MUw z_*yRVzHxN>6dK@7X3))aR%r`*CCjZ0mfz@YIg%$UFH%KO0GBe!DIFS}4|sZc-Jk4A z6Db#>+Af}bQlQIQ?912R8}a-zR}b^nZEM1$yoKVYG(Y;n0!v}ezw;MI(12qtzDV#y zH;uUXA-JRxr#NL_{=U1KidJloR)b%YW+qQ0p-`?-DE?j6x2ZYAom)&b7fP!K1mTg3 z;>U-T%HFLv$C`^TnY!w4s)$Q+vz-HtYk)*FDMG%}!#uEdg1mev@3@FcwRA14%p>o@ z3U%WNHcqqxQ5 zk$;OT$#{VD{`H&9$c1`23tpHm7T_nPk(GvV<+G@?Nsc|O!M-VU=14#%58fd#&I)5OiFqpFLkrRpwEbol#HN{6cnzjbysRx7#@7F#E`Y6C8uD{)x}Fs zQlHMh$D_y#Jb389J2uRny`WEK78vt3IfYz8v#}5zJLkV9Z`EOTC3o^lY1TJLJ*c)< z;z;ZVnCR7fy-c;}5vEMTs7|Xd(x@+JeanS(>4=FkGLHlQih&H|i@SEKQ84bitPxTw z;g+{B&A`pVQL&<79nN4HTbh>{vdA>a{fK%z8Oj6il>o;khc1q*u=QEd|OaI+Uc|naK)o=@Z$PTj_V> zN>o?vroJ}yLeH=I6DeL>n#AlKWxuj)zQ`52@MwXvEd_AqP#3AiqVB2+(6Gv6#rn<_ zR;yQ=rOQ+rNGnXRa~F?8yR=KBvH9W74VUXe+rKNyQ%fgJdq&CHwfLB zRsY$%Xe2fFmo+VjG(M5vFkR73qItlDBnA{`-B2Qg3sGztjvbm%&D>VG7|%#{)C_r- z)ykU$UdS;txC)ub__<30x$r|Zma zk5uu(m6=hM8eAqoQ{{r#gP`U&4`&O_>&KHt=6niyr2Gs_gOm8U6E0e-?R*_pk$)<} zd?>LC1!FkQrbFv;q4=9XijZ{DR5zZQffGtUx&e?M>I!7b4DnhA2wIS;@wY-`5w97N zP${?vg~y8mj2DZPyjI~Oe`9be28m$MJU;?^b?w+n`8+_3TKa_iW_7WYoMrCN(o%_s z6-rn4=c3t6f`bp#gOEgm-mg4EX=nc>>63O-$(h3f{>EG2eC@R*ci8q!rtqjY2m9a% znV5TH*-$Fthpl))N8qi(7`4!-0UmD#*G6VuZh;LW^3@#|ree16sESLcqyA#0eF=pl z2Qxe2E81|4Ijj;)h~m3NY0s0Y1zymPSBSb~hsqLZkx}4=gmrY%f;qH%ric7eEbewj zCcV}I|1vH&t+Y>_g`El~6(IPGS0T68K$g0tTvRP2aRxJSV`*QfCUh$6@>xdv%BI#~ zk-2h{Csq;g+}8eWVp))c-#IL2gFvb5&8P14Y?GRb5HnXI3JU9U3lwI#Ch^U&ab5Li)9o~R@Qz^T}Y2*@VDO7Qw5PWqV9TSu^SDlpL!pX|K=gaWT%%g)8Di|MR zSeU*|@{gg19Zr=rOKLHf;Aym5sErDQR~XG_*9&%c1HEcPsIrAq@CH)%u70dMKJU6qw3}TnMAy@f@?ZRC|xPvmz$%*t4iWieU07wW= zGh}?Iyq_T-*&Q|Z6g|2Z&aFZ&G~3OXl_VNH147MGW0l0#;7s`#jRi2eEQZ9*Ae1E+ zX$K-cF(j8p(4OQPps4wE%L`f1KCA`5!Fdf#;J6MwtLGOWk=Tm15CjGaqpN%B_Z6__ z2HBAN)IGpkV8hLr^$MNT7TfdB>=_%tA@KYoq8vXHXWQOvLZ-A2CVJ}2r#o&U4bTsI&@d z(dl*5(-H;|87sj5oSJB?)SfHC@~^^7qrvsQY%7LNpZ8*7G~#O+;KrD(haa~LqCOMh zAbSHkAne&C+95j=2%8-EIqr!#9z3uj!t~(?LMPn8N{)wwNGOIc@Tpm3#NgSb^F70rv$_+~_vOyQRRH7+`p69|kE>Dq~cFCZb1-15O;+N^#;j6tY@IK`1VnT}*6-K|53q~?T3*NKh^${=vQ!f(IOc;Y5>J+fXqN`;Grjx@1wdZV9 zzn9X!P#wcXia`Z^m`_lVUgnkCiN$GybQNTqaFdRs)QLgmc+M%^BI(~1%{R-DUAa}scMrs?ndj5fI z0G5l4xo=np^CRo^3-@YBYDWy4XWndy3w0WjnuN<110=E0WeFCtT<(hA;A?dSY;Co; ze$%s+Sw`yNRdyNp(9jb57$qJfNww%V`mc}$>=*~x0=S=Ux5dam6Da#K}fm(RcBPij_D3+;da3T0&Pl~Yk zFro0g=1-dCb7|v_ z4=;=nmmnx?PyPf?lQ$GY0lSE)GnV|YeKfx#ueyb27rnoyIT=XWW)*KLT^S6&kNI^22 zP5%0>L_<%6;iql9?AxHiQch?~YfW2{1TaarR z<MMd+O5SEm_DXsv2}tm-3lkWg}W+~(+G$<_%&XS z6%2`sF6F0&9m$U;jLy7Ct%$PTJ*m43E!yJ8&l%KDvDIJ2+RXj*S|6JALv~dCuL=pP zFy9B9lUJYTk$>1HwUd2Td#7#r~kN%dg(jKuXxRpkoT^tZAh9_7QL zZ33>zU!2KQvYWuz8{DvTni!Gg`1%tlVKB;uq)}r8@`_7?tk1$Q^T_R?!M*Ue`Dar%7OA=3M(8o+tThnOk}-jd;C z&<|FBzEQWq_fTHff`52-{{96)9B>GN-h;`SugSV!2?pSu7;kyNkiST}7`JLgX$h-Z zpMgP?h&Ym1OEuoz=?l?RZ^>{{;H9#+0?p}M$fRvDxs>JEUEVwomHH<7`jgxs;X!A4 zf{)MI%jq)Yi;<(#yuI6G@wdLHuSmgWcA z>)+cCtKVA{L|E`!I*)t)M72q9I%empoEPzBPTB+em=W>?eblB-Ho~!3ittW_Vb|x< z#b!FMoX009Ib)m5j8jf|W_cy`dkJe}gp^y-{2)dsP4Nr1v#0z<2)bK<_A7ctRFVBc z6`voE3x)e5*Y1g%zoxZ`*~bUn`#z#Zd1u%%WYv58acQ#qY*6!njr2EN@U zNvGnBbYz;CyX)YUaX)SbT%Eb%UJnC5l^)>Wg{V6hqFZs|nNE-FE+n$p{To^y(2WPW z3(mER_1)#<1|MfT86(*sm7nARFitnat{1z~C~~f@HK-;N80t556~c6?1va1d0LjoR zx*E8eA9)T1HoyJ=`4$lPOO)@2;}0{d-ojXnqiT2x0y-aZ2FsG+PY=kag2;C`tJ-WU z-gGP8e1pi3N)$aG!7GSLI_yEzm8H0Nb(r*5ckd1K5eoE?3iL4r`V)eXEbfDMd2w(R zRfNt)A3dm;6zGBP%(iFTpN>2_4>JKZnymH37{E!J;)JzxsdB~Vc5?uz5)wDIZju)pWh?N2n z-Pp>|I&Acn$WY6sKWzbf^y40V24$27Y-FpAg*a@4M;u)Q5$$1P@Wn+iB#z|^I{MXW zcs<}AT@vij0bJBg$4L-5R!#xu1~&?MKF_H0hayN3|D8uL3m4Ve_9dv`Ll^1v&Aj04 z;+hk>@dMO8Q(n0DBii7A>dzqaa|qQlt&YU1` z+%loRWYnvW<^^zicerg^O4Ab)y?F@k`y9W~<1$J+tl#5Isbvp3{ZZVWu8?0C60-vr}{ye;l52N3Jq`G^@ia6znT0Y`vxSXN19)lM zM2T$HHf|!W`9c5;DmGjxQ6#_GcDD6ovkWCQl&upSw$c9j&!*qg+;_)s>Uy8O8QIOy ztTSvx`MoFKJ<6MMA2=0)ROPhOz*|K! zV$Z6VQpfAzzK%7z~RGPVG+0{7A6%$8SoB4gvP+_8Mp@@C_-cu zWSJ0$G6W6w%EIx&^}r#A^39gy5loDIr1427`Yr^Aljx5M%qy*ioNF?mHDaAfuMyJGH+vSIETV3~3Z z+|v(9hAP96!j)m~8oB3Dy!*G9Va+z)35PntoniACwvR*m3_|N3lKpD(2-#3IT}$_w z?C~4)hh59~#?}TWQqW*tqfk&}ZtIZWC0P?SX(^-6ry#ME?^)O|&`@$+H97TTrL~Yr6pi4Zt0STzIbW-n4#i)6k5vL#;dVXPpl{>PFw+0Rpvjmw7Gfh@<~M{Z#FM28-$?-CWX^%PF=b? z<&@a8Fyr$&y4sR!N+NeBAQ5qeEGr9x#>(2(83Yn|d2s&`w41W)n#j`jM;#{Fap}Nz zZkWa@wmgy_{Yni*u!CDFVY23uJuG#lDx^w9wk~q`P?5Z}yc0!FsYUHZy`{!dmAXPj z`Mke!)fL7D5p2r`Zx(xADI$Im?I6QO@HZ^E_OvX~xvd3idi`qO=w9dSZ}e;HC;E!Q zS;h|N${yX=F?3d$h$$-Fr;37LWUYR5CYVII9FUTkef?|@SuEBs$7noeVXbQ0s?_tc zmEe|KsR}{~#mHJD%t(nvrg04h7Xc;-#oT3l z3B?qPOA?#uc;r-BmDyOuD5xvV_&NGD{eu ziF~=aVysq{amX2xVcFx;chO|uRF$~}WJS6OLp7Gp^W#+c;}HsV*%_?!Rpid3Du2qc zQ6WjYixXKSFCA%ZHH?I02zU#PTulvc%iOYQQ1vTcScw!e{tn5w3A8fbD?FvAqkPVQ zBB?GD^rX#pSYAs;NU7S37j3GA%toNC9afYpt{=Xir>ddEO+l06%~$*?y=)$~Ked*O zu)df?F>++$ib}jl+Lwu?vS{G*1d!=YRd1ByVoPniuc*MSJO|5wD@20$GKZtzgWEKV z&$y01`&n)}yW!qcnR<;_*Ja$OI(?Zm^_aJ=!GBwK&RmR;Y|CPP8b2<9$APw#G1j#1 zqg7f?jZpEJ?_0a7;4W)xyTj06nKHU#;dFEb49wJTW3@lNN-laDRsMBLMBNuZ(;NgV zMdbs*#Zim~BpaZY>a$WWL{q;zAZ{u|xh@lji=Ouz@ZPnjm2Ts>Hl)TeSrDnm6O~CnZcWlddzX)FjcUdU0|4ge`9?a zHQiHFjz3UYNSthM#UDws^)R@QB9mWDT*;_7E!L4PyzAD;Ov_+KX>u~Gy}H;ziP1ud z{4~LxE&ivY<=Hk-Ezz0FL;g6y*MP1kt{7IbGrYTRS_)U@f>zkm2uc~w(FuyaQvKyd znzC%iUqgoGcc4Wf0rN1^r3bq_{Z)m7O71;^l?8VQqQZO2G5&*o4pksUv^}mdQ&mk*a;la}G6~EU_Got2Byk z*@=FRgiWLD5sU87L2KCcF?Wu1$DD3Qa#5GeC$pRIBw@EX^_KjCFY%V*?(TN~(8FeX z#0gpZAFAY}qit&sn!lmjonX4BNROi1*u$)LJm-b>`dCY(Z>zgWIH(u+4}|Rl&)RCZ4=~@Ta3{CD1|e$;%d}AYNl;0`}~S$ z;X3+-wY8FY^&7k%en8P3jt!Szsnm-AF*Bv1cP5p>h__$OXf$(5O?b4@U@E8Kw7%sF ztj3Rt*MQHne-V8{gkM5%P61hInOVSZ>l}9gp|c}z81z}{SCK2YYl5l2O4r54mN4R` z9$5KJKd|Q^!s3%lxMv>46$7vKulcwM%!{{uWG;eV3w3R25A4>nw!Y-wx~<#ZZntbc zp_1=I_}TUBqdE07sb&L7xW`Q3A4m9L9#%T>RjD)<}yuMV4Z4g4k&)2NITrcXeVp=ne)X9k@yfUzqsBWT0h6nwgOV0yMXR};Ib zWE#MvJ!lQm1U?OuL1&~kd#yb!6*{+HJNqbE#u)S~8hIK|aOr%^k=qpc2NdB8RqiY5 z^2@Gr!;b+z&j`?)Unl#!5Q6>OYDt!MnC{Q!^KaUc5jLm*fTBN!VmO<@5SL;wy+If) z9uzF}cOh1#JqfuK1Ch=;vD8Cyx<79r+~=?e9$J!pdao+XPabGgqWW3t7ukWq{o0b* z59Gfhpd%0i(fWUm%7*__3itn3JECl4ZenBb?+7SI1KK-z1pP1K22B?s85oMDkUnrq z{RUD*q$@C0DA9s|zde+ONs>1-P1a65Boa~yfj}H(eQiYc zX*{UY^wu9r)BKo<6U;Uh_4$JUhk%5V_rG0Z`9Ix^kAb`5r2$2Ozk{)GC_GLqHssa~dB)F!KSU8_+NR~kJN#&t>CqfVYs5?a;z-%Q3z0=r}+=+idPK~^WuJT-TSzG~6z zQ}$~y>|;0%wHWWUG}~vHE~K&eakv^(`*}fb(eLv^e;>*LyTQ5(4A})AdwfX@**#>3 zV%|50s@wO4syB!ZS-DFLU7gS0htBszBz2n;=*Xs^_GJW#e zuH6Is((1Q`B08jn_8qao|Bbi#iTJzjX{fJWy`L24Cx#Fvzx3|_J^UU~ z?E!;tDHT##vOnK@NU3AHdIn{pepu#nn*n zfpq9D@R*k|3lF>tFAMej>q5?wF!-7n<>`k}e{j5!eL4wVCbF-Dnb*v&5;Tak>cp9$ zBAK8si+am#d(;PKo}#b092Jl zHXSgNt%MgBE@obL<&;2I^Q$OgMzxL<>MZ!w9GNUdl!!ynTEWGMrKhT;$He#Y@xuOK znYbBcppnV(lH>lI>%AK9tX6qRdr{n_ruR^B)nW4ZFj2$ubyXL4XuAXY@;Mn=cs?p2yy3Ka7SG)t=8WjihC!gOFB5ObR zcvuD9a&ZF|wbu#Ur=~Nlhw;}7zLm}VP#|`Ezlct6{cI|gO?8A|9YUC}fjX%bos?p2 zxIubgkEcn|E+98jfz2pO{R;7t8P3+Jc!(O!n65fHm2C!769rxCu;yi@WkuDgJdE_9 zi&03N^QAzeT`}(xGuFc?^;|^`4GtXihK$c)7R8UOBAKSTE#w6%i-PfwBioe!ls+N0 zFnUJ$P+WnmmbZezbaVj=M$G3&{}`_)#}%t!X$cbcWhrgMJ3vBKngMOK z%G)rcv~C%<;-Wg8;FQ40i3me;@To);({pelKwuM`SN@5sZv4aeQOCyiA$@K zDb4Xc<=~$b*4b=rNusoO!o_kam;O2B&kJPMbBc2n<0>n2(077oCfUj=(>Y z=1e4TiiOI{tYyMhR1P-7It0&7(qiD+)iC1*>XC9`QB6rPVL!Aqf&)0> z;$oa4{-P7eJmSg2B(P2IL>ytX6OML^3xr6){ux$O8|~wmEeX$^yg9ZqhGgs;rIQpR zcho*L2gF6jvUH3H+yahC1GGb)`O@J5XP9<^dveFWDC_S=%0WlAJyu<#(V{7p=hHmf zkJ53%y85#fM9(GIO4}Qad^73UgJu^+#WRi=68b=sI#nJeLGkiO;))1!^(AtLC2Cb1 z@y7IHhKQB;gP5`Eov@}u@bdN%P~Xn}PhoEv6;~E@i{kDO+}+(ZxEAj2E`__hyL)hl z;O_1k+=B&ok^nDx_kOq2eP8!m_2--)Yt|XN&Zxcik~#4(f0A4E!}O>QgLQ5ItdjSs zudHrtw%mF``I78;^Z5Xu;A=AQMl+iu9UuKL0JA(M&~W?|WF* zzb!4!3$$2M+gf8oQDZ?>pmOIONS}Eqcjc})ftqFVq@{n2k2JX*BH2*XO=BaE<6)Z! z%fK8}y5eY`%q`^}aW!QjPZS|yJ{AfAC%Q>}W+AH1pEJvMWs5`ZDDGp8z`;Y8wrvO<-%unPkuwGK*^`AIyhc>jo$ zuVkvGMb5&V1Go#aCU3UPtL;~Z74d%S?36=TB37y|W{sm^Z*pRrqUgk~MH?Ty2p5t& zE{$!cF3o7W3e#Q&0A??B=U@us)+d zRi&a$Emgb4ru|Ub)2*fI)1Dobx2-C7sLe2C4F#u0Ag{>A619_R?Z%b*7O>GM23)Bn zMAa1=)9}VzX!J@t%efKHXI2#$Wyb(ewrQc3(bTWC52GyZzm9DLvlzb@%O5tlw|7yN zRaA#i0@(0(*_YEZT1?7^W^4@Q0Q(ra{T+Mv3!rc5ysb(pY(IVpYMynf$cpBH1LlO|xUz>Ko z-3%tG%171(_tlz1UJc8rR6DqRh)F%YP`pM|)2pnSki%m$J48{ z6_#sFN2s`Qozj7x@Ay;KPJ`05l~(O$QnLg$mh0MLjh#mcuKj(!Uk^GC^ z5l#IFOSi>_rq|Gl2dnT|yVCj(y077eH1#uz`pKqTjbB|teKX1vxL5fj$)DB<2N24p zVWgCtCb;jPvaW``%Gw9Z5n}JZD&z1#NJM5Nbvlo`R|lD9vAGk;UxL?dSqZ^7P<>aK z0M-qjCNw$nw)M`O0@t7R_&FZ8R$}?LwY1otV>6KpDqpH`hSQbpW$ba7ldzX67Y%eQ+s^;FE$axw-`qgKjU5?D+lY%$^2DPxl*xY%8y^3z-Di-<_)@s z;CwPaJkv^)e___{jWb7>5^EL?Z1|J;T zZ5ie{{Ub^Q7)2mdphVyzggjOTBeSSs>Bp)*`amAASqzV~KA%8%2u2J|`g&Ev%)UlQ zM$|UBU8lfQFbx=8ntB<-*giF|FL3;l1+?`_hRJ=Hf%<`hfqsFYfx>|VV68Cx2D^}8 zOc)-CeHUPLs2(+a-yrmvfMtDn5Zlz(=7Fs+n`GCVfj=NFu{_fIz`>B=x^#90z^*>5 zQd|oLBE#{M*XtO@_l-gjet32c>_vGl?kj^7B)e{do#oi`Lbqq$^Q7Fg6bXm@CU?TV zH%!?z6q23=42NZ6+xtq{H4vhabxn(|Zx_aK4bQg30%Wx`-3_9euE)${9c<_I;00Y7 zWJ8E!;hx#mg6cKBrh}Sg@yP3kN6$TPue1wH}CKTM{&dK&Y1m1_RAoRTh z{q$p)xD9tig}e{o39&J=cnux+p2Iu~AsgqXh-3RPpe!_jP?(xP%3yw|M(inb=J0-9 zs5@W}WHfdPv*ttrZZbm~E&#K7cn@Jj3y4Um2_(4M8xVnCIgo;iM37)fU|0=sgXc6! zg51J(5JrY?HLir*$gm!~!L+qt$mO02%FbAyGNcQ(<6a5kx=HI?3(|3nfqdFGhTonc z-0FwBG(GdnvF(QjHbD{%aKj763?&v;vf%dzod2M)@e6YI zhGW3w4>Lgl0iA}SF?VigZB}!I%Id0-=`I?t=}7T`>@P620g?z?u}Wu^wetLYldqd? zPI$)2{0d`XYO2oNou_Nu!rsi;)`j@C8AsW=F<)?$#tjx{&B07$G$CDFsY*q|AF9~g zsPw=sMI9NEML-az+gj92(#pF_HvW>uQni+GySPe~Sz>&GO%!c_QC+9u%Z`_?q_01d zU2L9$oJ6~IsJg)oyz^wFux2dIq#7B@np-j(%8INhran-e$xV9-PSIH5P+32=$O~rX zBO56dt<)*CXH@!j{Jv_2N_XqO^chz&SBIdyR7=SE#yXs@e4)~cMFX)uYD;y4+=4H3 z6V^46r>U)XE0+)WSw7~qU}z??pNS`ydPLium#Tn%q~o1BmO_*k4k~MN6J%*oiH(!Y zypNlGWmTo&pzYozR6*^_cP)J^Z>*`3S%mLX#XZSf!PJBFVJ-~;L%u5Xz>aL~Y_RBZ z8+sV@=wQEPNxMbObJq%(LD{Goy4t1<*=U;yx(gUscE$=?13m3fLsBnkGuK%8HTd-O zv!$qkGwTI;Re+I_ctm?;R(f%2ih9aQ3ay!1>SSub88UUVtW1^62WXC~;U;e_BTsQF zSafagvZ+*0UqVS@g9?(puh~@^>eTQe*$fNRcJR5TIxbOMi!?2vn<5=xtdkc*wMr8g z9I4;8aiRuGmuNC4&IY2?N|xF6ZB;PoGGUOBNo-VdIET$tRI`;GRB&x&iU3xhX{I?# zhD+P*qDD)b)-mb;R*f_loWm9>KRGE3Rf0H621{MmQvg(#kN)o_-jeF<&6SS@W%3VJ zfQXrTeGI_%<_A?&&`S8_y+YhH6Fr)7QT8AYr2Mq_4@)lGL}95EDLRuA1!o)hXD%>g zl{8hu98qesbarUdyC9@;&m<}p)wDD<%26w{NFB{p-NN}Q3^vAhh#c#J!F;qA=rlq2 zEm(#A%TY-pv{V2MS1UvcXOhvO3bOP#Rf6Sj<-(G&yw(d!jd9v8hbxJak~O@yXXOCN z3*Ot)vAUvwxFFuMIi<2VPL`-zx8iRxc{ao~WeJ%pHWcz0NhYvJ=~c`hPb-SR<=LAH za-cXAFHRI6ip>-ks7CV?&$LC3WpEZD&PAdeyLi8n z3!P%blZQl7T}i{Jgrh|;W|2IEOcN-7^f%2lEjp>p-u~bZuno)ACdNd210DnXR=IfWLKrM{Fp%*1dEwagYrnsmf)dE%0<#9Cix6gA4o8o^3?CgMlH_xR{G>mkI`Lt^>X7SJ zc;K?uLP(#zE?*bL#;U^1);^Uf6pJ~Ou9USPrWZVeK+iDW%1>!GmuJ zSVFcOH?Ke;m-a(6R^pkA!U{I|v0!65sW_f6EC$1&TckB@zZ|(-FjVbYLVB;_L@)qD zzsQe?l9~1qif4aZ?i)_^qt{Y|2kBkWkFt@~o0Rz#rYLQ>Ku40|jNzc&Wh&@JA-RO7G2Of6CML$P3 zlEb{kLn)cRy+?!{hXD0qW)b(+Nch=8EJA?b>l|Hp$2QMpBwg$wvLbENA;(r87#Fsp zY{W+kCmMzzZ&v9xUc|&2j~~NxcEJ5`ybF?r744ehG77V zteOOf4dM?igJNwMm;*?fu<0KWbwO=*X}Pg#17m>%ZH83r1F^ed*#(IF#SDD=D-dtc z@O#|fj4TOPY4uIKhy&jfG0<|6(O$t~PQz-nx`Dn#x-us%_N!!g-g}RM!>rhW6w_!A z{L(=fKlN2ou$5h)LX$pZj+hw{>2lW3P1({A?o$-C7^+$Z#$p2R(MQc5g727{iXZwF zjH~gX$VwpWO7OK`a3G^&0=u_>FNCWMMZWl2y+mk-?A};IlO`7}2Z|z$mY&(=UoX;{ zM`BZ=_1iw?iQ!Bfg$$#Q84rl{jf5yE=rf&P1bGUH^!M>sBx z;raLi0d`zXPyMa}Ku0V&P0SYVIQkZ4)EEuTw#=Ax6FL@}EZ6Ct=^|3))=Pj8$o%sk-`Hb+-p&>7WPJscHXT*p7Tg=Fp zq1q=wm~GJ7d!Ux#TZfA#+@$*|_^(L{+K9~~+Fu?LHf!CHjL4(DLW{JcBdrqa|| z7s%Ez(3$-ui4$fd(JD(4q|0G)%`92gWRcb;?8_2x%OjeVv8oxQvB9SyY$fbtX_N!j zR%q;K_KB3Do$Mla(s3?qTV(}DDnC#J&6G+E8jjz$YFwP?k~cpm>1Z9MrEX48wmOa1 z;4lNSq#36{vf1IyrJN0G*wb~hz-t!xVWbwvYPNyn$=;4j9Uw^egIUeth0^j@p?B#I zgY7n*d;9j4t%Gywi4+$11BKeWu^VX{hc2ETOq(^a=WFb^sJIsvK(!hpMo&Q|x=j4F zOrmt$FYui##rn5Bi@nlahbzf|5R3Aw3RiuM~?@sZ}K)36O>tF3bB~%P2R9Gm=`Tmm0&X z+;S`T|1`#Redv#~1&7`kt1i(MenO3hHmi`}d_K}|lQLk)2jo;Jf&*V@>V=C+t)xg}5TxDiIFh!NulOnuNw4^$F@6$W*E zprWp^S8ue+M+`(51d;KsAuY-9`>>4IdJy=oKeT}wgz{Z;0l9jh!mnLDaCZWB_OEuq ze+v90c#jyoX26g1A7S@_ZoJm>dguzb*#FUnBGhO6Gr^Wugeg}6i1P+nbus|x>lq_x zh`ogA(K<%p_Qmd+Jds$Hh|Th;txw8#Kf8Cbsm7ftmcVu63D08e+qm}(*S_jpa@#5T zGX8@ZRIWi*Ps|ojguj61}q0$t3yWc3=^;k<>XTKn?uwq*>Ou_OL;tH+D&C7?Y*74~qadIZ zg+)NFI2t7}6PxH$wNcRi(sUwm64--gM6uLEm=xUd0f|$d?m!D#GqDI_93~=`I5PaF z@X-0eg@`mW)Cs~ub+Pg@m}Xj$niU$PX_-qNu9LvzDi4&XPZAU8)G=}wRlj&bf@^MZ zsKm4hF|3ek0!^x#Ggo8X3IsjprN1LPq^06wEkm{<#h68}NViUK?gIpb&Cd$*MnzFC zPk5+M2`gZeIl6(+lSg?XER&}B1AYB~lv?H6E0PZ8NVu(n)^AgtUVEsxL)MKFYZ>^L zJkNr*lo|5*a@LLN){R;~sC8oS##7HMSvxnV=C#Zva|@O1T<~u|M(!KA>s|hnzQ$eB zsc^be_#H;wk9KY(E{Q9mZP?#WClA=}hwsGcgSZ;71qaZcp=4Q*CV>zf%PegWALvKr ztZO}&bdOCSeG{9|%h1x%^@V$njX8LMAH0AZ_Wc}f1Yh3Q@EuM{(|C~jCKE-@)$$I31SvrS7gj#dq}w*vPnmat9r zIZ2%vb+h98UOWk75D9w9iy+epb2Wl=N^GBWIa;$o)&@B1hc6xsiQf%faO~3bB4kR` zA0=2Ix`sO?>r(!XP?yv*R8``?%W6eejv!Wgrl8^3i|?K$FRL2 zPq=R(PdMA7KM?cxgTv}>NXYgFM^K;3l}21lSP5B64dWfbZuzdH4%gx4num9YEq-3F zf1(q)x>5IY9qwu<74bS8`MjVZ8NhUM$JNo$^9k>p4L0)5TzZIu^d)@K&p3U<_YmFZ z9Qu=YB}`Mt(EL0|sg5}qGUm7*?cqTW_P`*pQZ%95$ElC-hYJG`KK#bQ$Xn??u66J` z=_)2d`p-C$Do?js2UvKyG#B0G@B(JgcgBLsbcM9W=cL#b0*#=_C-oGIMzV!m)7Y#) z`a>hyoX7)1(713`xnMJHP+I90II8*nY=mRWcMxhS1~m1bc)zn#w6y$2(!c7-V3hxr zonrit?9~5eRT1|vF$20-IsEVbI=#4Qc`z1?P=!n(Y+DB43b5mY+2d*({fn zb8>K^HY2xp+O<-;&mjC5dv3;|hktTn!r!6$^s*?rsVIvI_&lJyZyoj+74XvFhapwNalo zueLg@vnTLS781E!w#^Sl$V2g|B$s>PJ!O!jc z4=O4vXD6Av=dbVnJ5T<--NypGZ`Zfk7pD;r2hrLYdmG z#j$!02jCff)8ZC)t`UH>z@4Di13rE%-w>Ds zmIV+H()Y=eoZl9px{tRQw19ucO<2CouBgbM220_dcripVhYz59M?`tB71D<&oV_c^5mD%) z+^;gb$e<@E7Q|2N{N-zOADKaM{-V7k>S;seL3RYhx&so>c+0k!LEPHi>ahH%GL7Mp zS;($fDh%tcjc_RFE;z!g-MN;&1ov;C_PX1N1c8Mh8mT=Td>_=muiS^ncw8xwd2(k( zgv3f!y0hX&Ly~!2wG`msuT00n<$l@gkQM3y5M_s3akJNs2Af)wE*9>Z)^lOXiFrXw zBA=2p9(t_GDTBtRAI7Ul8h86jkP2UxClZ_8*iIzm$&60rRMmKjtjCOafk(RNDnK!i zgmp2&BhyyjUeHtB(tcJlZ+5WII@j@aW<@+DA;a0`ZpDliHVUp|$8wIoK#Uu?$3gBO z2)&ThSu`>6N;;pGKSyXAKF;v4%50ZPS-+CYs$)3%#-MbOFWOx6)Hz|sIC@+9IhE(T z)6P4Sew=&F%o(ywNu>{@*i05+UTkS|z&9Ut#ghplF7zIDPo8G!NfQ10rHhZMf;pZ{ z;k*^G&e3J@h?Yrfygs(mYSRXD_o(Y4UaZ2`D|KGAH0AVwBNIN3g?6p$;-9#dM>a9vMW^pfKaH+hVs z4Y|XQAIeTc-}!c~A5QY+w99P*$0Y0nZHjUsXLWoj86Uby*sqbilEisylMWo*S*@cL z^!aH)va~o^o~=y;%0aIM?W}{()yJQ?`9$zZVl%6WkV~OriV3N)%VSF~xh;nzi;A}m z>-g#nkJWc`l`Y5i1YR3Ie>wMHI(D74##jGY@5f&q?})mHj_Ryi$XH0k#;}8-PQK8P z!PS2RH)1@ZBydi^mZUFA`WR;1?;?|*EUC7|C#Y220KE_K(MzM|~p9?XU#0~<*`s6`b67QhTL z9R@ct>p&u_89wWDNdSGXKw1VcaYoOern*jiZ9D1V4=O!?V!5Kger8dSANM4wYvZ7iBRdRFH-DDh!&n`oQDcBTsKDEVY}*T1BwRdtOJNvuC~b(P^6?MmAn}M#p%a8; zC1I8JyOXL*LbeqF)D#(N=Vd-8%6bdeiY zMz{=LvAHF%_xaSyKEq^L6(F9Xfm7v*S~83d6d{r1&^7=k)3i)| zheKjl-0t|UVJ8fyWn(g2ly)?EVu#xg;t)8sy0&<>?eQGM<61i-T8{}Z^Qk9w@|&}_ zTw|ul?cx1x3f@4{y^yDe7|!CaD}@H@-hv*^>P}Sr2NS;YiJbZ7sJ{2Hoq>0KdW)nx zGiWRI%LmjRN+|zG6|vr^SyJazUqh?XhBYuXb8^);2RO&}k_O9rqt2{Zj|x%B+Ve=3 z2d#dsvz;|e)dNMM?MYc5ORB5s{AA+^A`=bJB*xh>jB}R41H?|g2g_&Bp zKF7ZQ=%4bTPqUHA70>lby1T(xZ_}?JY<&14PEMfgc30Js(k_`y0(5wfE9LH_gd(CdH40z zI;j`IL7FC2E(TJ3ersBK`sS#u(cUZCnk2o}JPFoaO{z{cF_$W!u(_F*=qT)L#oGLB zC6O&vM&4OzVNNn_M)Ncz`&ww}fy8EN?nAzLC0JFPL6vTsc)KjWhfCVi2gs`pg0A;g zz$2nxr`_kZ$Eu{q#t%*Xl-@Yu1f}N(kEXqn&usXuntU|+iQV998+vRXW&p3+zbG^E zx*`?QuUoNv!JU5|kG+E_)M8$yt<~DrE8Hx9GWU!=NM~~@G8-bxr&YJC*zqIK3&|!j z9%lNA`YTnQS+}!#`|#*GvXx8pG)-~PlX|c{styC(Y{i1SWs3K}2~ zhER0Zd#3melZ>D&wui)QE{v;OE7Yp&s&&qiei2&$piH!D^rUQ_d>d<8)`uC_J6s&U z`EPr(^??M=hB?gmK5aYCyv};<{J1^Lju#BWXRj3Jak$Ax&cE4XA?i6AeDZw`L>9W= zlN9{98lL}rtwQ8?ImqPmT!P$tyGKLhccB8`{al3Qf4e`c&wn*+(Dht}Rp)urf#u+F zQ;Ox_`Jqe3GxbP~NB`?h<)+V+I5W{*s4A=$eK9VHCuv0hO)(P@{9JxKE5aC~bZo*f z%&`el3)~uKG&yN!u*j!RJnA4qVQ`AkBx?c&Qo0%FBGq8@BFas+SZVO4cHs%RB8(|y zOcSFbT?U$rm}|&mBNVsy!om+EBt&U!f}~UFa4+TOesW`G13v9ze!62HSi6-uYm>^V zAa|%jbjfV;F`Q~_b*T=$65I4w1S#mbVA_>7U5ZxSifnzfM-QHJ5@zAo=nnlz3{XBB zj5^#D#FaVb$5{Zw6LwCX2_9iQrADtDtKyEu`iZWK<9hctc;DhST)G0hUAiK+c;5m$ zU4Nn@fAfHKhilguootgGr6YZ0Qo1%cUsNMR`Zf`+CnDj(5#32^5HaJdMq-q3{zWbE z9P_}tBP%xBMU9lcM67PYu`}W7r1DoGR~nXw`Vg-pE85}Iny{z5>cwdU%tu2bm0wp4 zwqhhgB(Jxo%DLNsq|APeH+0x_ay_`AzZBm1#%|BjpL!+Va-A?vEXJ?Vs>TRstJL+A z$OUTKw%w(!%_CrN$qdTmW{zEMs~^;Pj#vdJvjuk}wlo6u!r`oC|M2j3^WJIyr<95; zJuXMEIydH5`Qg=EMz+KlO{8l_ysb2&%wTg>PRm!y?7W)#V*4$*ocd=eTO9DoP#X-N zyp{G_F(}PTfJt`nAzAYRv*duRvlr@FL;{KT<1&GC6_MiMIQmOQ){6)8hOnF79&q~c_XKHW9H9hx_ zI;8JhI)?IVhr}K_XjB!zJl9#_r!=vU1x-q)*c$e8aOk}G+;Z>zEved^cY2=XY@e3a z%#tSe@Xc%B>Z$TE;=*c{;UK3kF0L-D?M~0NHvltfnq#We^6~b|v<)X7 z{X(TuotThQWb?jT797}@B9_VC;rr~AZ8TUas42x*5x;M71w7=suzB(jZ>KsxmgsTH zuv){5%q5+G(KtI}ixG`3p9uvW&Iai6KeeX=^nJ{dx!j(Xia*4$+kJ}rm87ZWa(fbzWeTuF=FWOUp7#UD!TI+}D- zu!f1L@ZHLA^A!EQh(K!_1Fz2I9!xsKwW;v6L$abuogEl8U;0_&js2Q~V1Z9$VSfD!j$%^r#KH(zJ z)H?plrE0YactQR3nEO{$?3^6l0b#dXbEI3T9|e*Uoe?6aRG4IGQbg+bO;r>sK$n)& zw(iW?4s`#;mVm{E{<0&%G*N9Odjs-J>|NtttLEZYy5t9QQemL#l=P9Np!;1x!Pk2P zsA0yM8NtxA`{;qJ+_W|Pcs#z*9u{Wsg&!8m3ulqxTIZ)~ortlKp4gn2pJ+ zId6R2pGDdQp<tAxLJLk)fnI7aSTJmCQ{^2R3Rd z#6{wM9R2ZfO2`x4l zfx+k*$Q(hv^&GYskwjY@NgOoRgMv1|7emPCI_&eMT~}zhX{X;wr9>ESD^VN>J$Vg)8S% ziiTGTkG^KSH68RE3cUlBiFVhR1&QOf=9QaN`q<$OC+Ci~sK<@#UWDp>(y@i3%OOVD zMmXI@^JRnsor@z*bSXn*mfYUU15S!Y1edZ{@}xAJs*mSzpgjcW#FNLW!hI*OUtJ;0 z}Ofm4hC+3ac`vLi2pV8^5-RZc~ z%ERduoqFf%ie#Z4(T{mg?8Zo6$E)+7M)XAu;fnJ*vlEpDvjKJJNO7a=F@zfMM5mMV zHyWBk@1JnqN0gqKst!4-`@V6bI+zN3Lb)fvrC7&-ai)7|oD(>J@}&+mwvC(VU25x) zqbRTLiWy@_B(4^#_f>6!>JiK?AG5w%XVd+V=RKPuOx%=Xz>{RA`iQj&k^5~6`bNIT z)5(GCX$EhPG(6ZNeIWS2PK{tpEq6=^uqz@?ORj-F0k$F=ycnqN`jfwn;W__~Nx{~&&0`!_)f%v3WGnhZ$Ao{%tglodW zV4D!txZ2m!=+@Lrw!#)BD*3iJwr2V;YtmNUl}zqv;Asd$Ea$#1o1L! zU>6EK@IH+EWKjYX@zSo0-SLR$6S!hNqj(N)5IZ{>3g*_j`NYFS>^+bd(R%!!Bm8%$Zmn)J>t$fwADJa+be77j|whJL9ZI9S`@^8`BWym?!Y|4jqsa6!$wMT03O>oUp*;UpY0ER`{$Om!PoEF8Jxu4dl>P6a3 z+RGILsmneFM}WOUT|S+h@Qpv*sqMt5>!azF#5aKlkBkXk`XRN9V4!XU4=5$FBck0hTrM6f-mbU#BkLWF?0MMU2sREVvTA zEpx6?BylcRkz;itA7>WfEi}?Z=ro+^Pc-*&y7V z9U);)3c1Sm5HQ(w`#y!qau*gxsNk+N6!(jzfg+5HG~llhgxMDARyS4qPP)*yTB(0y z*oLVE4A}%SZJJ}`S0gRI;ZVI+g-WLym+Uo9Q{}x!sJDDM&-gLmSWU_0aJS9S!__kl z(>a^4jZzC!%O=Gokj*BC82M12VN^=0>sPi>>+HW#$(QmaoOSCX_Yu;=;R|`~N>}a9 zt`2FnBK-y&AUN@Ua6_)0w zpz8QjjXsZzOYqq?V%@j(-05H_e|(r(vkZdXnO>1WI-JlqZbCh!PK^U3J4)9fl{ktl z1lYaVJeab8E{qp;GSj=J^-o$w6(6^`Lu~73=VI!lcKF07U4(=e*FYvlrS|aGAom0H z7B<6zpfg5Al{ZSxJr9kiKdjRU8*m(Iz;b<;?7}sJV6{{-g)B1}iNFysFGaf|rrHrr z62xyfEnf_<1*I~n#dluY)G02GO_)I}@RO|cPk*SOM$FAKrEie3*~Mvo&*{Rt?`27YxVU5Tz|Kgx-1plu$korFk{wij+ zW+pBwE_VMzsIS;BKkyO7SBG{uDhejAUxi}cFJL5EhMdH*nq{voMY?g-O)yja)AF0_TmzC?kq&h58ASRemo^dI!PKK zE~xdUFjMVr+bm^?_Cm+(NYoOH>|dSHW9#+u#mC}udS;zLnihM(jF)S!)yZ0IidMAf zaKCcJsF@B5fv>&gbkKq8XLK4xu!EL&%DSruA6E&0+lM;gj{TlhjYI2fIYxT)N`7V` z;YXI~P%iCHXga#>G5$2qLKr#zA8On0j?zV5ozrrWe=qpMERYTFSBjry1I{JQ)3@0umOuT=pnomPKfV7)4E3KVf677pX1@Q+6#fI{4=L1t z0{%&||GQH6mudW3*MEDg|65bypSXXLz5o3|m46Kb{2T5cg#G^n{F4La_wMkQk>dP2 z;Gc}~|3v(ArugrOx~%_>_$QOfKN0_&clbM^KHtA1{yCZaPsBfK)W7E>{xS^7e@Fbs z)WknQ|E$~oE^q&3pSAuS^#8lq{ZH&av#Ni8pYF8(9sB>T1OF5E&#>WlPWCTrF!^`f zzm=B%3Hj$f@b_Pf+VS5Z|BOEViTLLq-S7S5FROI^cf^10CW^90) { - System.out.println("** Looking in " + interfaces.length + " interfaces"); - } + // if (debug && interfaces.length>0) { + // System.out.println("** Looking in " + interfaces.length + " interfaces"); + // } SEARCHININTERFACE: for (int ix=0; ix0) { - if (debug) System.out.println("** " + nmbMethods + " methods named: '" + functionName + "' + found, add to class cache"); + // if (debug) System.out.println("** " + nmbMethods + " methods named: '" + functionName + "' + found, add to class cache"); methods = new Method[nmbMethods]; methodVector.copyInto(methods); if (publicMethods==null) { @@ -435,7 +435,7 @@ public class ClassInfo { } publicMethods.put(functionName, methods); } else { - if (debug) System.out.println("** No method named '" + functionName + "' found"); + // if (debug) System.out.println("** No method named '" + functionName + "' found"); } return methods; } @@ -449,7 +449,7 @@ public class ClassInfo { * @param cls The class of the method being looked up * @return The method array or null if none found or in case of error */ - synchronized public static Method[] lookupBeanMethod(String functionName, Class cls) { + public static Method[] lookupBeanMethod(String functionName, Class cls) { ClassInfo classInfo = ClassInfo.ensureClassInfo(cls); return classInfo.cachedBeanMethodLookup(functionName, cls); } @@ -469,27 +469,27 @@ public class ClassInfo { * @return The list of methods or null in case of error or if none found. */ private Method [] cachedBeanMethodLookup(String functionName, Class cls) { - boolean debug = ESLoader.isDebugJavaAccess(); + // boolean debug = ESLoader.isDebugJavaAccess(); if (beanMethods != null) { - if (debug) System.out.println("** Method descriptor for bean '" + - cls + "' found in cache"); + // if (debug) System.out.println("** Method descriptor for bean '" + + // cls + "' found in cache"); Method [] methods = (Method []) beanMethods.get(functionName); if (methods!= null) { - if (debug) System.out.println("** " + methods.length + - " method(s) named '" + functionName + "' found in cache"); + // if (debug) System.out.println("** " + methods.length + + // " method(s) named '" + functionName + "' found in cache"); return methods; } - } + } // Not in cache, find if any matching the same name can be found - if (debug) System.out.println("** No method named '" + - functionName + "' found in bean cache, lookup started"); + // if (debug) System.out.println("** No method named '" + + // functionName + "' found in bean cache, lookup started"); // Do we have a cached BeanInfo ? create it if no if (beanInfo == null) { try { beanInfo = Introspector.getBeanInfo(cls); } catch (IntrospectionException e) { - if (debug) System.out.println(" ** Error getting beaninfo: " + e); + // if (debug) System.out.println(" ** Error getting beaninfo: " + e); return null; } } @@ -498,7 +498,7 @@ public class ClassInfo { Vector methodVector = new Vector(allDescriptors.length); for (int i=0; i0) { - if (debug) System.out.println("** " + nmbMethods + " methods named: '" - + functionName + "' + found, add to bean cache"); + // if (debug) System.out.println("** " + nmbMethods + " methods named: '" + // + functionName + "' + found, add to bean cache"); methods = new Method[nmbMethods]; methodVector.copyInto(methods); if (beanMethods==null) { @@ -556,8 +556,8 @@ public class ClassInfo { } beanMethods.put(functionName, methods); } else { - if (debug) System.out.println("** No bean method named: '" + - functionName + "' + found"); + // if (debug) System.out.println("** No bean method named: '" + + // functionName + "' + found"); } return methods; } diff --git a/src/helma/extensions/HelmaExtension.java b/src/helma/extensions/HelmaExtension.java index 5750e65c..9c02c7f5 100644 --- a/src/helma/extensions/HelmaExtension.java +++ b/src/helma/extensions/HelmaExtension.java @@ -32,6 +32,12 @@ public abstract class HelmaExtension { */ public abstract void applicationStopped (Application app); + /** + * called when an Application's properties are have been updated. + * note that this will be called at startup once *before* applicationStarted(). + */ + public abstract void applicationUpdated (Application app); + /** * called by the ScriptingEngine when it is initizalized. Throws a ConfigurationException * when this type of ScriptingEngine is not supported. New methods and prototypes can be diff --git a/src/helma/extensions/demo/DemoExtension.java b/src/helma/extensions/demo/DemoExtension.java index 0d1d9455..2a946e06 100644 --- a/src/helma/extensions/demo/DemoExtension.java +++ b/src/helma/extensions/demo/DemoExtension.java @@ -43,6 +43,10 @@ public class DemoExtension extends HelmaExtension { app.logEvent ("DemoExtension stopped on app " + app.getName () ); } + public void applicationUpdated (Application app) { + app.logEvent ("DemoExtension updated on app " + app.getName () ); + } + public HashMap initScripting (Application app, ScriptingEngine engine) throws ConfigurationException { if (!(engine instanceof FesiEngine)) throw new ConfigurationException ("scripting engine " + engine.toString () + " not supported in DemoExtension"); diff --git a/src/helma/framework/ResponseBean.java b/src/helma/framework/ResponseBean.java index 063f9f6f..b3803a9c 100644 --- a/src/helma/framework/ResponseBean.java +++ b/src/helma/framework/ResponseBean.java @@ -62,7 +62,7 @@ public class ResponseBean implements Serializable { res.writeBinary (what); } - public void debug (String message) { + public void debug (Object message) { res.debug (message); } diff --git a/src/helma/framework/ResponseTrans.java b/src/helma/framework/ResponseTrans.java index 898eb493..ea69a7cb 100644 --- a/src/helma/framework/ResponseTrans.java +++ b/src/helma/framework/ResponseTrans.java @@ -185,19 +185,15 @@ public final class ResponseTrans implements Externalizable { } /** - * Returns the number of characters written to the response buffer so far. + * Get the response buffer, creating it if it doesn't exist */ - public int getBufferLength() { + public StringBuffer getBuffer () { if (buffer == null) - return 0; - return buffer.length (); - } - - public void setBufferLength(int l) { - if (buffer != null) - buffer.setLength (l); + buffer = new StringBuffer (INITIAL_BUFFER_SIZE); + return buffer; } + /** * Append a string to the response unchanged. This is often called * at the end of a request to write out the whole page, so if buffer @@ -237,10 +233,13 @@ public final class ResponseTrans implements Externalizable { * that buffer exists and its length is larger than offset. str may be null, in which * case nothing happens. */ - public void debug (String str) { + public void debug (Object message) { if (debugBuffer == null) debugBuffer = new StringBuffer (); - debugBuffer.append ("

"+str+"

"); + String str = message == null ? "null" : message.toString (); + debugBuffer.append ("

"); + debugBuffer.append (str); + debugBuffer.append ("

"); } /** @@ -335,17 +334,25 @@ public final class ResponseTrans implements Externalizable { if (charset == null) charset = "ISO-8859-1"; boolean encodingError = false; + // only close if the response hasn't been closed yet if (response == null) { - if (buffer != null) { - if (debugBuffer != null) + // if debug buffer exists, append it to main buffer + if (debugBuffer != null) { + if (buffer == null) + buffer = debugBuffer; + else buffer.append (debugBuffer); + } + // get the buffer's bytes in the specified encoding + if (buffer != null) { try { response = buffer.toString ().getBytes (charset); } catch (UnsupportedEncodingException uee) { encodingError = true; response = buffer.toString ().getBytes (); } - buffer = null; // make sure this is done only once, even with more requsts attached + // make sure this is done only once, even with more requsts attached + buffer = null; } else { response = new byte[0]; } diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index 63d52d95..9edde03b 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -228,6 +228,11 @@ public final class Application implements IPathElement, Runnable { } } + // read the sessions if wanted + if ("true".equalsIgnoreCase (getProperty("persistentSessions"))) { + loadSessionData (null); + } + typemgr = new TypeManager (this); typemgr.createPrototypes (); // logEvent ("Started type manager for "+name); @@ -319,6 +324,11 @@ public final class Application implements IPathElement, Runnable { ext.applicationStopped (this); } + // store the sessions if wanted + if ("true".equalsIgnoreCase (getProperty("persistentSessions"))) { + storeSessionData (null); + } + // stop logs if they exist if (eventLog != null) { eventLog.close (); @@ -840,34 +850,31 @@ public final class Application implements IPathElement, Runnable { */ public String getNodeHref (Object elem, String actionName) { // Object root = getDataRoot (); - // check optional root prototype from app.properties String rootProto = props.getProperty ("rootPrototype"); - String divider = "/"; - StringBuffer b = new StringBuffer (); - Object parent = null; - int loopWatch = 0; + StringBuffer b = new StringBuffer (baseURI); - while (elem != null && (parent = getParentElement (elem)) != null && elem != rootObject) { - - if (rootProto != null && rootProto.equals (getPrototypeName (elem))) - break; - b.insert (0, divider); - b.insert (0, UrlEncoded.encode (getElementName (elem))); - // move down to the element's parent - elem = parent; - - if (loopWatch++ > 20) - break; - } + composeHref (elem, b, rootProto, 0); if (actionName != null) b.append (UrlEncoded.encode (actionName)); - return baseURI + b.toString (); + return b.toString (); } + private final void composeHref (Object elem, StringBuffer b, String rootProto, int pathCount) { + if (elem == null || pathCount > 20) + return; + if (rootProto != null && rootProto.equals (getPrototypeName (elem))) + return; + Object parent = getParentElement (elem); + if (parent == null) + return; + composeHref (getParentElement (elem), b, rootProto, pathCount++); + b.append (UrlEncoded.encode (getElementName (elem))); + b.append ("/"); + } /** * This method sets the base URL of this application which will be prepended to @@ -1112,9 +1119,8 @@ public final class Application implements IPathElement, Runnable { NodeHandle userhandle = session.userHandle; if (userhandle != null) { try { - String[] str = new String [1]; - str[0] = session.getSessionID (); - eval.invokeFunction (userhandle, "onLogout", str); + Object[] param = { session.getSessionID() }; + eval.invokeFunction (userhandle, "onLogout", param); } catch (Exception ignore) {} } destroySession(session); @@ -1143,7 +1149,7 @@ public final class Application implements IPathElement, Runnable { } // sleep until we have work to do - try { + try { worker.sleep (Math.min (cleanupSleep, scheduleSleep)); } catch (InterruptedException x) { logEvent ("Scheduler for "+name+" interrupted"); @@ -1256,6 +1262,14 @@ public final class Application implements IPathElement, Runnable { // if node manager exists, update it if (nmgr != null) nmgr.updateProperties (props); + // update extensions + Vector extensions = Server.getServer ().getExtensions (); + for (int i=0; i 50) throw new RuntimeException ("Recursive skin invocation suspected"); - if (parts == null) { + if (macros == null) { reval.res.writeCharArray (source, 0, sourceLength); reval.skinDepth--; return; @@ -113,14 +113,14 @@ public final class Skin { try { int written = 0; Map handlerCache = null; - if (parts.length > 3) { + if (macros.length > 3) { handlerCache = new HashMap(); } - for (int i=0; i written) - reval.res.writeCharArray (source, written, parts[i].start-written); - parts[i].render (reval, thisObject, paramObject, handlerCache); - written = parts[i].end; + for (int i=0; i written) + reval.res.writeCharArray (source, written, macros[i].start-written); + macros[i].render (reval, thisObject, paramObject, handlerCache); + written = macros[i].end; } if (written < sourceLength) reval.res.writeCharArray (source, written, sourceLength-written); @@ -133,9 +133,9 @@ public final class Skin { * Check if a certain macro is present in this skin. The macro name is in handler.name notation */ public boolean containsMacro (String macroname) { - for (int i=0; i=0; i--) { - Object pathelem = reval.requestPath.get (i); - if (handler.equals (app.getPrototypeName (pathelem))) { - handlerObject = pathelem; - break; - } - } */ + // eiter because thisObject == null or the right object wasn't found + // in the object's parent path. Check if a matching macro handler + // is registered with the response object (res.handlers). handlerObject = reval.res.getMacroHandlers().get (handler); } @@ -380,12 +389,14 @@ public final class Skin { String funcName = name+"_macro"; if (reval.scriptingEngine.hasFunction (handlerObject, funcName)) { + + StringBuffer buffer = reval.res.getBuffer(); // remember length of response buffer before calling macro - int bufLength = reval.res.getBufferLength (); + int bufLength = buffer.length(); // remember length of buffer with prefix written out int preLength = 0; if (prefix != null) { - reval.res.write (prefix); + buffer.append (prefix); preLength = prefix.length(); } @@ -396,24 +407,36 @@ public final class Skin { // parameters = new HashMap (); Object[] arguments = { parameters == null ? new HashMap () : - new HashMap (parameters) }; + new HashMap (parameters) + }; + + Object value = reval.scriptingEngine.invoke (handlerObject, funcName, arguments, false); - Object v = reval.scriptingEngine.invoke (handlerObject, funcName, arguments, false); // check if macro wrote out to response buffer - if (reval.res.getBufferLength () == bufLength + preLength) { - // function didn't write out anything itself + if (buffer.length () == bufLength + preLength) { + // function didn't write out anything itself. + // erase previously written prefix if (preLength > 0) - reval.res.setBufferLength (bufLength); - writeToResponse (v, reval.res, true); + buffer.setLength (bufLength); + // write out macro's return value + writeResponse (value, buffer, true); } else { - if (suffix != null) - reval.res.write (suffix); - writeToResponse (v, reval.res, false); + if (encoding != ENCODE_NONE) { + // if an encoding is specified, re-encode the macro's output + String output = buffer.substring (bufLength + preLength); + buffer.setLength (bufLength); + writeResponse (output, buffer, false); + } else { + // no re-encoding needed, just append suffix + if (suffix != null) + buffer.append (suffix); + } + writeResponse (value, buffer, false); } } else { // System.err.println ("Getting macro from property"); - Object v = reval.scriptingEngine.get (handlerObject, name); - writeToResponse (v, reval.res, true); + Object value = reval.scriptingEngine.get (handlerObject, name); + writeResponse (value, reval.res.getBuffer(), true); } } else { String msg = "[HopMacro unhandled: "+fullName+"]"; @@ -445,21 +468,21 @@ public final class Skin { value = reval.res.error; if (value == null) value = reval.res.get (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } private void renderFromRequest (RequestEvaluator reval) { if (reval.req == null) return; Object value = reval.req.get (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } private void renderFromSession (RequestEvaluator reval) { if (reval.session == null) return; Object value = reval.session.getCacheNode().getString (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } private void renderFromParam (RequestEvaluator reval, Map paramObject) { @@ -467,14 +490,14 @@ public final class Skin { reval.res.write ("[HopMacro error: Skin requires a parameter object]"); else { Object value = paramObject.get (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } } /** * Utility method for writing text out to the response object. */ - void writeToResponse (Object value, ResponseTrans res, boolean useDefault) { + void writeResponse (Object value, StringBuffer buffer, boolean useDefault) { String text; if (value == null) { if (useDefault) @@ -484,36 +507,39 @@ public final class Skin { } else { text = value.toString (); } - if (text == null || text.length() == 0) - return; - if (encoding != null) - text = encode (text, encoding); - res.write (prefix); - res.write (text); - res.write (suffix); - } - - /** - * Utility method for performing different kind of character - * encodings on the macro output. - */ - String encode (String text, String encoding) { - if ("html".equalsIgnoreCase (encoding)) - return HtmlEncoder.encode (text); - if ("xml".equalsIgnoreCase (encoding)) - return HtmlEncoder.encodeXml (text); - if ("form".equalsIgnoreCase (encoding)) - return HtmlEncoder.encodeFormValue (text); - if ("url".equalsIgnoreCase (encoding)) - return URLEncoder.encode (text); - return text; + if (text != null && text.length() > 0) { + if (prefix != null) + buffer.append (prefix); + switch (encoding) { + case ENCODE_NONE: + buffer.append (text); + break; + case ENCODE_HTML: + HtmlEncoder.encode (text, buffer); + break; + case ENCODE_XML: + HtmlEncoder.encodeXml (text, buffer); + break; + case ENCODE_FORM: + HtmlEncoder.encodeFormValue (text, buffer); + break; + case ENCODE_URL: + buffer.append (URLEncoder.encode (text)); + break; + case ENCODE_ALL: + HtmlEncoder.encodeAll (text, buffer); + break; + } + if (suffix != null) + buffer.append (suffix); + } } public String toString () { return "[HopMacro: "+fullName+"]"; } - + /** * Return the full name of the macro in handler.name notation diff --git a/src/helma/image/ImageGenerator.java b/src/helma/image/ImageGenerator.java index debf059a..3602c822 100644 --- a/src/helma/image/ImageGenerator.java +++ b/src/helma/image/ImageGenerator.java @@ -9,10 +9,10 @@ import java.net.URL; import java.net.MalformedURLException; /** - * This creates an invisible frame in order to be able to create images - * from Java. (Java needs a window context in order to user the Image class). + * Factory class for generating Image objects from various sources. + * */ - + public class ImageGenerator { public ImageGenerator () { @@ -20,10 +20,9 @@ public class ImageGenerator { } public ImageWrapper createPaintableImage (int w, int h) { - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + BufferedImage img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); Graphics g = img.getGraphics (); - ImageWrapper rimg = null; - rimg = new SunImageWrapper (img, g, w, h, this); + ImageWrapper rimg = new SunImageWrapper (img, g, w, h, this); return rimg; } @@ -31,13 +30,17 @@ public class ImageGenerator { ImageWrapper rimg = null; Image img1 = Toolkit.getDefaultToolkit ().createImage (src); ImageLoader loader = new ImageLoader (img1); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); - Graphics g = img.getGraphics (); - g.drawImage (img1, 0, 0, null); - rimg = new SunImageWrapper (img, g, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + Graphics g = img.getGraphics (); + g.drawImage (img1, 0, 0, null); + rimg = new SunImageWrapper (img, g, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -45,10 +48,14 @@ public class ImageGenerator { ImageWrapper rimg = null; Image img = Toolkit.getDefaultToolkit ().createImage (src); ImageLoader loader = new ImageLoader (img); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - rimg = new SunImageWrapper (img, null, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + rimg = new SunImageWrapper (img, null, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -58,13 +65,17 @@ public class ImageGenerator { URL url = new URL (urlstring); Image img1 = Toolkit.getDefaultToolkit ().createImage (url); ImageLoader loader = new ImageLoader (img1); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); - Graphics g = img.getGraphics (); - g.drawImage (img1, 0, 0, null); - rimg = new SunImageWrapper (img, g, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + Graphics g = img.getGraphics (); + g.drawImage (img1, 0, 0, null); + rimg = new SunImageWrapper (img, g, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -73,13 +84,17 @@ public class ImageGenerator { FilteredImageSource fis = new FilteredImageSource (iw.getSource(), filter); Image img1 = Toolkit.getDefaultToolkit().createImage (fis); ImageLoader loader = new ImageLoader (img1); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); - Graphics g = img.getGraphics (); - g.drawImage (img1, 0, 0, null); - rimg = new SunImageWrapper (img, g, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + Graphics g = img.getGraphics (); + g.drawImage (img1, 0, 0, null); + rimg = new SunImageWrapper (img, g, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -88,7 +103,8 @@ public class ImageGenerator { Image img = null; img = Toolkit.getDefaultToolkit ().createImage (filename); ImageLoader loader = new ImageLoader (img); - loader.load (); + loader.getDimensions (); + loader.done(); return img; } @@ -96,7 +112,8 @@ public class ImageGenerator { Image img = null; img = Toolkit.getDefaultToolkit ().createImage (producer); ImageLoader loader = new ImageLoader (img); - loader.load (); + loader.getDimensions (); + loader.done(); return img; } @@ -104,26 +121,27 @@ public class ImageGenerator { Image img; int w, h; + boolean waiting; + boolean firstFrameLoaded; ImageLoader (Image img) { this.img = img; + waiting = true; + firstFrameLoaded = false; } - int getWidth () { - return w; - } - - int getHeight () { - return h; - } - - synchronized void load () { + synchronized void getDimensions () { w = img.getWidth(this); h = img.getHeight (this); - if (w == -1 || h == -1) try { - wait (30000); - } catch (InterruptedException x) { - return; + if (w == -1 || h == -1) { + try { + wait (45000); + } catch (InterruptedException x) { + waiting = false; + return; + } finally { + waiting = false; + } } // if width and height haven't been set, throw tantrum if (w == -1 || h == -1) { @@ -131,6 +149,18 @@ public class ImageGenerator { } } + synchronized void done () { + waiting = false; + notifyAll (); + } + + int getWidth () { + return w; + } + + int getHeight () { + return h; + } public synchronized boolean imageUpdate(Image img, int infoflags, @@ -138,24 +168,24 @@ public class ImageGenerator { int y, int width, int height) { - if (w == -1 && (infoflags & WIDTH) > 0) - w = width; - if (h == -1 && (infoflags & HEIGHT) > 0) - h = height; - if (h > -1 && w > -1 && (infoflags & ALLBITS) > 0) { - // we know all we want to know. notify waiting thread that - // the image is loaded and ready to be used. - notifyAll (); - return false; - } // check if there was an error - if ((infoflags & ERROR) > 0) { + if (!waiting || (infoflags & ERROR) > 0 || (infoflags & ABORT) > 0) { + // we either timed out or there was an error. notifyAll (); return false; } - // TODO: If image production was aborted, but no error was reported, - // we might want to start production again. For now, we just give up. - if ((infoflags & ABORT) > 0) { + if ((infoflags & WIDTH) > 0 || (infoflags & HEIGHT) > 0) { + if ((infoflags & WIDTH) > 0) + w = width; + if ((infoflags & HEIGHT) > 0) + h = height; + if (w > -1 && h > -1 && firstFrameLoaded) { + notifyAll (); + return false; + } + } + if ((infoflags & ALLBITS) > 0 || (infoflags & FRAMEBITS) > 0) { + firstFrameLoaded = true; notifyAll (); return false; } diff --git a/src/helma/image/ImageWrapper.java b/src/helma/image/ImageWrapper.java index beccd67d..b5531fa8 100644 --- a/src/helma/image/ImageWrapper.java +++ b/src/helma/image/ImageWrapper.java @@ -94,12 +94,16 @@ public abstract class ImageWrapper { } public void resize (int w, int h) { - // ImageFilter filter = new ReplicateScaleFilter (w, h); - // img = Toolkit.getDefaultToolkit ().createImage(new FilteredImageSource(img.getSource(), filter)); img = img.getScaledInstance (w, h, Image.SCALE_SMOOTH); width = w; height = h; } + + public void resizeFast (int w, int h) { + img = img.getScaledInstance (w, h, Image.SCALE_FAST); + width = w; + height = h; + } public abstract void reduceColors (int colors); diff --git a/src/helma/main/Server.java b/src/helma/main/Server.java index 1d8669e5..ff908b4e 100644 --- a/src/helma/main/Server.java +++ b/src/helma/main/Server.java @@ -25,7 +25,7 @@ import org.apache.xmlrpc.*; public class Server implements IPathElement, Runnable { - public static final String version = "1.2 RC2 2002/12/05"; + public static final String version = "1.2.2 (2003/02/04)"; public final long starttime; // if true we only accept RMI and XML-RPC connections from @@ -90,6 +90,9 @@ import org.apache.xmlrpc.*; // create new server instance server = new Server (args); + + // start the server main thread + server.start (); } /** @@ -137,8 +140,13 @@ import org.apache.xmlrpc.*; } catch (Exception portx) { usageError = true; } - } else + } else if (args[i].equals ("-i") && i+1 -1) { + read += r; + if (read == buffer.length) { + // grow input buffer + char[] newBuffer = new char[buffer.length*2]; + System.arraycopy (buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + } + } + newprop.setStringValue (new String(buffer, 0, read)); + } + break; + case Types.CHAR: case Types.VARCHAR: case Types.OTHER: @@ -435,9 +454,9 @@ public final class Node implements INode, Serializable { Relation rel = getDbMapping ().getSubnodeRelation (); if (rel != null) { if (rel.usesPrimaryKey()) { - nmgr.evictKey (new DbKey (getDbMapping().getSubnodeMapping(), key)); + nmgr.evictNodeByKey (new DbKey (getDbMapping().getSubnodeMapping(), key)); } else { - nmgr.evictKey (new SyntheticKey (getKey(), key)); + nmgr.evictNodeByKey (new SyntheticKey (getKey(), key)); } } } @@ -493,7 +512,7 @@ public final class Node implements INode, Serializable { } else { anonymous = true; } - } else if (p.contains (this) > -1) { + } else if (!anonymous && p.contains (this) > -1) { anonymous = true; } } catch (Exception ignore) { @@ -873,13 +892,13 @@ public final class Node implements INode, Serializable { public INode createNode (String nm, int where) { checkWriteLock (); - boolean anon = false; - if (nm == null || "".equals (nm.trim ())) + boolean anon = false; + if (nm == null || "".equals (nm.trim ())) anon = true; Node n = new Node (nm, null, nmgr); if (anon) addNode (n, where); - else + else setNode (nm, n); return n; } @@ -917,7 +936,7 @@ public final class Node implements INode, Serializable { if (rel.otherType != null && rel.otherType.isRelational ()) return (IPathElement) nmgr.getNode (this, name, rel); else - return (IPathElement) getNode (name); + return (IPathElement) getNode (name); } return (IPathElement) getSubnode (name); } else { @@ -1748,11 +1767,20 @@ public final class Node implements INode, Serializable { if (propMap == null) return; try { - Property p = (Property) propMap.remove (propname.toLowerCase ()); + // if node is relational, leave a null property so that it is + // updated in the DB. Otherwise, remove the property. + Property p; + boolean relational = dbmap != null && dbmap.isRelational(); + if (relational) + p = (Property) propMap.get (propname.toLowerCase ()); + else + p = (Property) propMap.remove (propname.toLowerCase ()); if (p != null) { checkWriteLock (); if (p.getType() == Property.NODE) p.unregisterNode (); + if (relational) + p.setStringValue (null); // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); lastmodified = System.currentTimeMillis (); if (state == CLEAN) diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index bab0a103..c2740d5d 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -9,7 +9,6 @@ import helma.framework.core.Application; import java.sql.*; import java.io.*; import java.util.*; -import com.workingdogs.village.*; /** * The NodeManager is responsible for fetching Nodes from the internal or @@ -360,8 +359,8 @@ public final class NodeManager { } /** - * Remove a node from the node cache. If at a later time it is accessed again, it will be - * refetched from the database. + * Remove a node from the node cache. If at a later time it is accessed again, + * it will be refetched from the database. */ public void evictNode (Node node) { node.setState (INode.INVALID); @@ -369,7 +368,21 @@ public final class NodeManager { } /** - * Used when a key stops being valid for a node. + * Remove a node from the node cache. If at a later time it is accessed again, + * it will be refetched from the database. + */ + public void evictNodeByKey (Key key) { + Node n = (Node) cache.remove (key); + if (n != null) { + n.setState (INode.INVALID); + if (!(key instanceof DbKey)) + cache.remove (n.getKey ()); + } + } + + /** + * Used when a key stops being valid for a node. The cached node itself + * remains valid, if it is present in the cache by other keys. */ public void evictKey (Key key) { cache.remove (key); @@ -396,62 +409,129 @@ public final class NodeManager { db.saveNode (txn, node.getID (), node); } else { // app.logEvent ("inserting relational node: "+node.getID ()); - TableDataSet tds = null; + + DbColumn[] columns = dbm.getColumns (); + + StringBuffer b1 = dbm.getInsert (); + StringBuffer b2 = new StringBuffer (" ) VALUES ( ?"); + + String nameField = dbm.getNameField (); + String prototypeField = dbm.getPrototypeField (); + + for (int i=0; i 0) { - // mark the key value as clean so no try is made to update it - rec.markValueClean (dbm.getIDField ()); - rec.markForUpdate (); - tds.save (); - } + + stmt.executeUpdate (); + + } catch (Exception x) { + x.printStackTrace (); + throw x; } finally { - if (tds != null) try { - tds.close (); + if (stmt != null) try { + stmt.close (); } catch (Exception ignore) {} } + if (markMappingAsUpdated) dbm.notifyDataChange (); } @@ -579,14 +723,17 @@ public final class NodeManager { Statement st = null; try { Connection con = dbm.getConnection (); - st = con.createStatement (); - st.executeUpdate (new StringBuffer ("DELETE FROM ") + String str = new StringBuffer ("DELETE FROM ") .append(dbm.getTableName ()) .append(" WHERE ") .append(dbm.getIDField()) .append(" = ") .append(node.getID()) - .toString()); + .toString(); + st = con.createStatement (); + st.executeUpdate (str); + if (logSql) + app.logEvent ("### deleteNode: "+str); } finally { if (st != null) try { st.close (); @@ -1084,7 +1231,13 @@ public final class NodeManager { q.append ("WHERE "); q.append (idfield); q.append (" = "); - q.append (kstr); + if (dbm.needsQuotes (idfield)) { + q.append ("'"); + q.append (escape(kstr)); + q.append ("'"); + } else { + q.append (kstr); + } if (logSql) app.logEvent ("### getNodeByKey: "+q.toString()); diff --git a/src/helma/objectmodel/db/Property.java b/src/helma/objectmodel/db/Property.java index 01ff127e..d933c5b2 100644 --- a/src/helma/objectmodel/db/Property.java +++ b/src/helma/objectmodel/db/Property.java @@ -8,6 +8,7 @@ import java.util.*; import java.io.*; import java.text.*; import helma.objectmodel.*; +import java.sql.Timestamp; /** * A property implementation for Nodes stored inside a database. Basically @@ -19,13 +20,7 @@ public final class Property implements IProperty, Serializable, Cloneable { private String propname; private Node node; - private String svalue; - private boolean bvalue; - private long lvalue; - private double dvalue; - // protected String nvalueID; - private NodeHandle nhandle; - private Object jvalue; + private Object value; private int type; @@ -42,29 +37,31 @@ public final class Property implements IProperty, Serializable, Cloneable { case STRING: // try to convert from old format if (node.version < 7) - svalue = in.readUTF (); + value = in.readUTF (); else - svalue = (String) in.readObject (); + value = in.readObject (); break; case BOOLEAN: - bvalue = in.readBoolean (); + value = in.readBoolean () ? Boolean.TRUE : Boolean.FALSE; break; case INTEGER: + value = new Long (in.readLong ()); + break; case DATE: - lvalue = in.readLong (); + value = new Date (in.readLong ()); break; case FLOAT: - dvalue = in.readDouble (); + value = new Double (in.readDouble ()); break; case NODE: // try to convert from old format if (node.version > 4) - nhandle = (NodeHandle) in.readObject (); + value = (NodeHandle) in.readObject (); else - nhandle = new NodeHandle (new DbKey (null, in.readUTF ())); + value = new NodeHandle (new DbKey (null, in.readUTF ())); break; case JAVAOBJECT: - jvalue = in.readObject (); + value = in.readObject (); break; } } catch (ClassNotFoundException x) { @@ -78,26 +75,28 @@ public final class Property implements IProperty, Serializable, Cloneable { out.writeInt (type); switch (type) { case STRING: - out.writeObject (svalue); + out.writeObject (value); break; case BOOLEAN: - out.writeBoolean (bvalue); + out.writeBoolean (((Boolean) value).booleanValue()); break; case INTEGER: + out.writeLong (((Long) value).longValue()); + break; case DATE: - out.writeLong (lvalue); + out.writeLong (((Date) value).getTime()); break; case FLOAT: - out.writeDouble (dvalue); + out.writeDouble (((Double) value).doubleValue()); break; case NODE: - out.writeObject (nhandle); + out.writeObject (value); break; case JAVAOBJECT: - if (jvalue != null && !(jvalue instanceof Serializable)) + if (value != null && !(value instanceof Serializable)) out.writeObject (null); else - out.writeObject (jvalue); + out.writeObject (value); break; } } @@ -114,10 +113,10 @@ public final class Property implements IProperty, Serializable, Cloneable { dirty = true; } - public Property (String propname, Node node, Node value) { + public Property (String propname, Node node, Node valueNode) { this (propname, node); type = NODE; - nhandle = value == null ? null : value.getHandle (); + value = valueNode == null ? null : valueNode.getHandle (); dirty = true; } @@ -126,141 +125,115 @@ public final class Property implements IProperty, Serializable, Cloneable { } public Object getValue () { - switch (type) { - case STRING: - return svalue; - case BOOLEAN: - return new Boolean (bvalue); - case INTEGER: - return new Long (lvalue); - case FLOAT: - return new Double (dvalue); - case DATE: - return new Date (lvalue); - case NODE: - return null; - case JAVAOBJECT: - return jvalue; - } - return null; + return value; } - public void setStringValue (String value) { + public int getType () { + return type; + } + + public void setStringValue (String str) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = STRING; - this.svalue = value; + value = str; dirty = true; } - public void setIntegerValue (long value) { + public void setIntegerValue (long l) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = INTEGER; - this.lvalue = value; + value = new Long(l); dirty = true; } - public void setFloatValue (double value) { + public void setFloatValue (double d) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = FLOAT; - this.dvalue = value; + value = new Double(d); dirty = true; } - public void setDateValue (Date value) { + public void setDateValue (Date date) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = DATE; - this.lvalue = value == null ? 0 : value.getTime(); + value = date; dirty = true; } - public void setBooleanValue (boolean value) { + public void setBooleanValue (boolean bool) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = BOOLEAN; - this.bvalue = value; + value = bool ? Boolean.TRUE : Boolean.FALSE; dirty = true; } - public void setNodeValue (Node value) { + public void setNodeValue (Node node) { // value.checkWriteLock (); if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; // registerNode (value); type = NODE; - nhandle = value.getHandle (); + value = node == null ? null : node.getHandle (); dirty = true; } - public void setNodeHandle (NodeHandle value) { + public void setNodeHandle (NodeHandle handle) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; // registerNode (value); type = NODE; - nhandle = value; + value = handle; dirty = true; } public NodeHandle getNodeHandle () { - return nhandle; + if (type == NODE) + return (NodeHandle) value; + return null; } - + public void convertToNodeReference (DbMapping dbm) { - String id = getStringValue (); - if (id == null) - nhandle = null; - else - nhandle = new NodeHandle (new DbKey (dbm, id)); + if (value != null && !(value instanceof NodeHandle)) + value = new NodeHandle (new DbKey (dbm, value.toString ())); type = NODE; } - public void setJavaObjectValue (Object value) { + public void setJavaObjectValue (Object obj) { if (type == NODE) unregisterNode (); type = JAVAOBJECT; - this.jvalue = value; + value = obj; } /** - * tell a the value node that it is no longer used as a property. + * tell a the value node that it is no longer used as a property. * If this was the "main" property for the node, also remove all other references. */ protected void unregisterNode () { - Node nvalue = null; - if (nhandle != null) - nvalue = nhandle.getNode (node.nmgr); - + if (value == null || !(value instanceof NodeHandle)) + return; + NodeHandle nhandle = (NodeHandle) value; + Node nvalue = nhandle.getNode (node.nmgr); + DbMapping nvmap = null; Relation nvrel = null; if (node.dbmap != null) { nvmap = node.dbmap.getPropertyMapping (propname); nvrel = node.dbmap.getPropertyRelation (propname); } - + if (nvalue == null) return; - + nvalue.checkWriteLock (); // check if the property node is also a subnode // BUG: this doesn't work because properties for subnode/properties are never stored and therefore @@ -281,22 +254,20 @@ public final class Property implements IProperty, Serializable, Cloneable { public String getStringValue () { + if (value == null) + return null; switch (type) { case STRING: - return svalue; case BOOLEAN: - return bvalue ? "true" : "false"; - case DATE: - SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy HH:mm"); - return format.format (new Date (lvalue)); case INTEGER: - return Long.toString (lvalue); case FLOAT: - return Double.toString (dvalue); - case NODE: - return nhandle == null ? null : nhandle.getID (); case JAVAOBJECT: - return jvalue == null ? null : jvalue.toString (); + return value.toString (); + case DATE: + SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm:ss"); + return format.format ((Date) value); + case NODE: + return ((NodeHandle) value).getID (); } return ""; } @@ -306,51 +277,66 @@ public final class Property implements IProperty, Serializable, Cloneable { } public long getIntegerValue () { - if (type == INTEGER) - return lvalue; - return 0; + if (type == INTEGER) + return ((Long) value).longValue (); + if (type == FLOAT) + return ((Double) value).longValue (); + if (type == BOOLEAN) + return ((Boolean) value).booleanValue() ? 1 : 0; + try { + return Long.parseLong (getStringValue()); + } catch (Exception x) { + return 0; + } } public double getFloatValue () { - if (type == FLOAT) - return dvalue; - return 0.0; + if (type == FLOAT) + return ((Double) value).doubleValue(); + if (type == INTEGER) + return ((Long) value).doubleValue (); + try { + return Double.parseDouble (getStringValue()); + } catch (Exception x) { + return 0.0; + } } public Date getDateValue () { - if (type == DATE) - return new Date (lvalue); + if (type == DATE) + return (Date) value; + return null; + } + + public Timestamp getTimestampValue () { + if (type == DATE && value != null) + return new Timestamp (((Date) value).getTime()); return null; } public boolean getBooleanValue () { - if (type == BOOLEAN) - return bvalue; + if (type == BOOLEAN) + return ((Boolean) value).booleanValue(); + if (type == INTEGER) + return !(0 == getIntegerValue()); return false; } public INode getNodeValue () { - - if (nhandle != null) { - Node n = nhandle.getNode (node.nmgr); - if (n != null) return n; + if (type == NODE && value != null) { + NodeHandle nhandle = (NodeHandle) value; + return nhandle.getNode (node.nmgr); } return null; } public Object getJavaObjectValue () { if (type == JAVAOBJECT) - return jvalue; + return value; return null; } - - public int getType () { - return type; - } - } - diff --git a/src/helma/objectmodel/db/WrappedNodeManager.java b/src/helma/objectmodel/db/WrappedNodeManager.java index 5a425bac..0499b821 100644 --- a/src/helma/objectmodel/db/WrappedNodeManager.java +++ b/src/helma/objectmodel/db/WrappedNodeManager.java @@ -110,6 +110,10 @@ import java.util.Vector; nmgr.evictNode (node); } + public void evictNodeByKey (Key key) { + nmgr.evictNodeByKey (key); + } + public void evictKey (Key key) { nmgr.evictKey (key); } diff --git a/src/helma/objectmodel/dom/XmlConverter.java b/src/helma/objectmodel/dom/XmlConverter.java index ba735de0..51c6472e 100644 --- a/src/helma/objectmodel/dom/XmlConverter.java +++ b/src/helma/objectmodel/dom/XmlConverter.java @@ -6,6 +6,7 @@ import java.util.*; import javax.xml.parsers.*; import org.w3c.dom.*; +import org.xml.sax.InputSource; import helma.objectmodel.*; import helma.util.SystemProperties; @@ -75,6 +76,15 @@ public class XmlConverter implements XmlConstants { } } + public INode convertFromString( String xml, INode helmaNode ) throws RuntimeException { + Document document = XmlUtil.parse (new InputSource (new StringReader (xml))); + if ( document!=null && document.getDocumentElement()!=null ) { + return convert( document.getDocumentElement(), helmaNode, new HashMap() ); + } else { + return helmaNode; + } + } + public INode convert( Element element, INode helmaNode, Map nodeCache ) { offset++; // previousNode is used to cache previous nodes with the same prototype @@ -82,17 +92,16 @@ public class XmlConverter implements XmlConstants { Object previousNode = null; if (DEBUG) debug("reading " + element.getNodeName() ); - helmaNode.setName( element.getNodeName() ); String prototype = props.getProperty(element.getNodeName()+"._prototype"); if ( prototype == null && !sparse ) prototype = "HopObject"; - // if we have a prototype (either explicit or implicit "hopobject"), + // if we have a prototype (either explicit or implicit "hopobject"), // set it on the Helma node and store it in the node cache. if ( prototype != null ) { + helmaNode.setName( element.getNodeName() ); helmaNode.setPrototype( prototype ); previousNode = nodeCache.put (prototype, helmaNode); } - // check attributes of the current element attributes(element, helmaNode, nodeCache); // check child nodes of the current element @@ -113,7 +122,7 @@ public class XmlConverter implements XmlConstants { private INode children( Element element, helma.objectmodel.INode helmaNode, Map nodeCache ) { NodeList list = element.getChildNodes(); int len = list.getLength(); - boolean nodeHasPrototype = helmaNode.getPrototype() != null; + boolean nodeIsInitialized = !nodeCache.isEmpty(); StringBuffer textcontent = new StringBuffer(); String domKey, helmaKey; for ( int i=0; i -1) { String prototype = helmaKey.substring (0, dot); INode node = (INode) nodeCache.get (prototype); - if (node != null) + if (node != null) { node.setString (helmaKey.substring(dot+1), attr.getNodeValue()); + } } else if (helmaNode.getPrototype() != null) { helmaNode.setString( helmaKey, attr.getNodeValue() ); } diff --git a/src/helma/objectmodel/dom/XmlUtil.java b/src/helma/objectmodel/dom/XmlUtil.java index e4c519ea..bd7bb63d 100644 --- a/src/helma/objectmodel/dom/XmlUtil.java +++ b/src/helma/objectmodel/dom/XmlUtil.java @@ -97,7 +97,8 @@ public class XmlUtil { int ct = childlist.getLength(); for ( int j=0; j1 ) { + converter = new XmlConverter (arguments[1].toString()); + } else { + converter = new XmlConverter (); + } + INode node = new helma.objectmodel.db.Node ( (String)null, (String)null, this.evaluator.engine.getApplication().getWrappedNodeManager() ); + INode result = converter.convertFromString (arguments[0].toString(),node); + return this.evaluator.engine.getNodeWrapper(result); + } catch ( NoClassDefFoundError e ) { + throw new EcmaScriptException("Can't load dom-capable xml parser."); + } catch ( RuntimeException f ) { + throw new EcmaScriptException(f.toString()); + } + } + } + } diff --git a/src/helma/servlet/AbstractServletClient.java b/src/helma/servlet/AbstractServletClient.java index c0dbc599..5ec01caf 100644 --- a/src/helma/servlet/AbstractServletClient.java +++ b/src/helma/servlet/AbstractServletClient.java @@ -49,6 +49,8 @@ public abstract class AbstractServletClient extends HttpServlet { uploadLimit = upstr == null ? 1024 : Integer.parseInt (upstr); // get cookie domain cookieDomain = init.getInitParameter ("cookieDomain"); + if (cookieDomain != null) + cookieDomain = cookieDomain.toLowerCase(); // get default encoding defaultEncoding = init.getInitParameter ("charset"); debug = ("true".equalsIgnoreCase (init.getInitParameter ("debug"))); @@ -73,7 +75,6 @@ public abstract class AbstractServletClient extends HttpServlet { protected void execute (HttpServletRequest request, HttpServletResponse response, byte method) { - Cookie[] cookies = request.getCookies(); RequestTrans reqtrans = new RequestTrans (method); // get app and path from original request path @@ -120,11 +121,12 @@ public abstract class AbstractServletClient extends HttpServlet { } // read cookies - if (cookies != null) { - for (int i=0; i < cookies.length;i++) try { + Cookie[] reqCookies = request.getCookies(); + if (reqCookies != null) { + for (int i=0; i < reqCookies.length;i++) try { // get Cookies - String nextKey = cookies[i].getName (); - String nextPart = cookies[i].getValue (); + String nextKey = reqCookies[i].getName (); + String nextPart = reqCookies[i].getValue (); if ("HopSession".equals (nextKey)) reqtrans.session = nextPart; else @@ -157,6 +159,14 @@ public abstract class AbstractServletClient extends HttpServlet { if (remotehost != null) reqtrans.set ("http_remotehost", remotehost); + // get the cookie domain to use for this response, if any. + String resCookieDomain = cookieDomain; + if (resCookieDomain != null) { + // check if cookieDomain is valid for this response. + // (note: cookieDomain is guaranteed to be lower case) + if (host != null && host.toLowerCase().indexOf (cookieDomain) == -1) + resCookieDomain = null; + } // check if we need to create a session id. also handle the // case that the session id doesn't match the remote host address if (reqtrans.session == null || !reqtrans.session.startsWith (remotehost)) { @@ -165,8 +175,8 @@ public abstract class AbstractServletClient extends HttpServlet { System.currentTimeMillis (), 36); Cookie c = new Cookie("HopSession", reqtrans.session); c.setPath ("/"); - if (cookieDomain != null) - c.setDomain (cookieDomain); + if (resCookieDomain != null) + c.setDomain (resCookieDomain); response.addCookie(c); } @@ -183,6 +193,16 @@ public abstract class AbstractServletClient extends HttpServlet { reqtrans.path = getPathInfo (request); ResponseTrans restrans = execute (reqtrans); + // set cookies + int ncookies = restrans.countCookies(); + if (restrans.countCookies() > 0) { + CookieTrans[] resCookies = restrans.getCookies (); + for (int i = 0; i < resCookies.length; i++) try { + Cookie c = resCookies[i].getCookie ("/", resCookieDomain); + response.addCookie(c); + } catch (Exception ignore) {} + } + // write response writeResponse (request, response, restrans); } catch (Exception x) { @@ -210,15 +230,6 @@ public abstract class AbstractServletClient extends HttpServlet { HttpServletResponse res, ResponseTrans hopres) { - int ncookies = hopres.countCookies(); - if (hopres.countCookies() > 0) { - CookieTrans[] cookies = hopres.getCookies (); - for (int i = 0; i < cookies.length; i++) try { - Cookie c = cookies[i].getCookie ("/", cookieDomain); - res.addCookie(c); - } catch (Exception ignore) {} - } - if (hopres.getETag() != null) { res.setHeader ("ETag", hopres.getETag()); } diff --git a/src/helma/util/HtmlEncoder.java b/src/helma/util/HtmlEncoder.java index 14916d41..29c587ad 100644 --- a/src/helma/util/HtmlEncoder.java +++ b/src/helma/util/HtmlEncoder.java @@ -363,11 +363,14 @@ public final class HtmlEncoder { if (i < l-2) { if (!insideMacroTag && '%' == str.charAt(i+1)) { // this is the beginning of a Helma macro tag - insideMacroTag = insideTag = true; - macroQuoteChar = '\u0000'; + if (!insideCodeTag) { + insideMacroTag = insideTag = true; + macroQuoteChar = '\u0000'; + } } else if ('!' == str.charAt(i+1) && '-' == str.charAt(i+2)) { // the beginning of an HTML comment? - insideComment = insideTag = (i