Codename One projects generated with the Initializr can now use official experimental Java 17 support, with most modern targets expected to work and more JDK support planned next.'
+---
+
+
+
+We now have official experimental support for Java 17 in Codename One projects.
+
+This is available through the [Initializr](/initializr/). To use it you should generate a new project and select **Java 17** during project creation.
+
+Support for Java 17 required work across the toolchain, the generated projects and several targets. This wasn't just a matter of changing a version number and hoping for the best.
+
+## How This Works
+
+You can use these new Java 17 projects with practically any JDK for the build itself. We tested this with JDK 21 and even JDK 25. In other words, you select Java 17 for the generated project, but your local machine does not need to run on JDK 17 just to build it.
+
+## Caveats
+
+There are a few caveats you should know before jumping in:
+
+- The desktop target is not currently supported for Java 17 projects. If there is demand we can add that. You can still use the `jar` target to build desktop applications with Codename One, and that works fine with Java 17.
+- UWP will not be supported. That target was already deprecated, so there is no point in doing the extra work there.
+- Other targets should just work.
+
+## What Java 17 Syntax Works?
+
+This support includes modern language syntax that makes day to day code much nicer to write.
+
+For example, `var` lets us remove obvious type boilerplate from local variables:
+
+```java
+var greeting = "Hello";
+var target = "Codename One";
+```
+
+Switch expressions are more concise and make it easier to return a value directly from the switch:
+
+```java
+var message = switch (greeting.length()) {
+ case 5 -> greeting + " " + target;
+ default -> "unexpected";
+};
+```
+
+Text blocks are also supported, so multi-line strings are much more readable:
+
+```java
+var textBlock = """
+ Java 17 language features
+ should compile in tests.
+ """;
+```
+
+That means you can start using language improvements that make code cleaner and easier to read.
+
+This still does not include support for newer JDK APIs such as streams. As mentioned in the previous post, if you need streams today there is already a cn1lib solution for that, and we hope to improve the built-in story over time.
+
+## What About Java 21 and 25?
+
+Since we mentioned JDK 21 and 25 above, it is worth clarifying the roadmap.
+
+We do plan to add support for newer language levels in a coming update. That is a different challenge, because Android only supports up to Java 17 today.
+
+We would still like to introduce that support, but at the moment Java 17 is the more important milestone.
+
+## Other Updates Worth Mentioning
+
+A few other useful things landed over the past week.
+
+The [Initializr](/initializr/) now includes more advanced CSS theme editing with live preview, and there were several follow-up fixes to make theming behave more reliably. This is relevant if you are starting a fresh project anyway, because the [Initializr](/initializr/) has become a much stronger starting point than it was even a couple of weeks ago.
+
+## Try Java 17 on a Real Project
+
+If you have been waiting for a more modern Java syntax level in Codename One, this is the time to give it a try.
+
+Create a new project with the [Initializr](/initializr/), select Java 17 and see how it feels in a real application. If you run into issues on Android, iOS, JavaScript or any other target, let us know. That feedback is what will help us polish this support and move it forward.
+
+---
+
+## Discussion
+
+_Join the conversation via GitHub Discussions._
+
+{{< giscus >}}
diff --git a/docs/website/static/blog/official-experimental-java-17-support.jpg b/docs/website/static/blog/official-experimental-java-17-support.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6fac66db1cda4ba35b828e19b16a4b866cdc2dad
GIT binary patch
literal 45873
zcmeFZbyQSc^gn!QL{bJpDHTu#1Vkxm1CVZzR*;qwkWd&<5gkgpq!~Iyhm;Z-8tD*F
zDGBNBnfDCp^L)Q+{eEk`@B8<2U6;9Y&z^nG+4scWpZ&SwhH(=#SUFEyD+p3mg?Jzc
zB8Nx`PC-QAiU9nB2+l$z$JY>~N5K5gwJ8Dj9~nXjBBcLUKF}5-{v!``9v^r9eEK3clJusbw~03u`x^5Am5AN_m^?d zD*F++=0w;Y;U^qKh>tSa{%-{qIOs~s_RgHyVeKB9Q{E|e-U#o#!o!8`YVMAatBzPk zXKBBF(b_Zb`yCC};R#Z+vnQWj6_fJT`|=QNB1!o87R9(Vh+BEc z(Y8@evv`UvqO_^r*Q%P0n67lQ=m4gT-luhg*^CpXUGlDmRhd7;5}Q}mSP4D(b`4?M zg?*;JL5S)VQuev@B;yR{Q^IB<|6u~-*jJ@4{28?9dva-C6mITe<1i9O7WCdmMw>Jf zTIkRZjg~KiT*@n;qZ4O}d0)p{DyUbVxwHQq<2_I~5L0em|L8scSNf{i@?AaE>=#RX zMS2rIy>7C$WfHW^zxCGoG 3>3HY#fuM`gh>x_ zdI=k4xkhmENcoLZq uVLu`saJXxsQ-y sJpQ zNX4#W1IZ4n_mb*@Wu{?uTLZkEBv06KvoLuhJ~8s#SDCI( {~rDo99i_ zCS~@B)Nv327*PRCIA}@>`|%{rs!Cu34q7J*JDLUe^S^9){Y=!TjFE5WsL-B+ja&3F z72jShKlGmhD+1^?&xVNtR))WBAv0y4E`u<3&<*SPezajKQs?GF z@&vS|1=KQBhix~N#NWA`ZyO!-{gT=EkZv8ooL*UA2@(5SPnullSqti&Pr?u771oPx zsnz(1uS=lM412NVd0wc|>}@uBH=1(wi~_McG2#M`i$})mkjH53o4JF{q-)K&HESj# zyVrK=qz275Prhs(5 O!3{Sy;=&_cwxJOgCHbrLgJ(AGAE4E zs%on^W(+7Th95*8@v7hLUU66UTX5retwW-kxiPjXXW?nSEhfyIf1TV+YPk5ItvRe6 zWpAIrwX`@n)AT6NM(<`vM(7KRL!Nw57TvMJtOPH5wJ#xsuJ$UW$!(o{MV1@!SBWfG z9<%Q2%S>e6T`6ViTP}!W-|5q(pGdmlz!hUub~W!Ook`i+aD<&Ysq1z+Vi;S>|5lgh zeQ K73|{JADkdYW=sJyP zvA_;zZ`dt7 3HV^}74vYV zCd2sjmT-&b9rNXlxw?INeXdIfc|Qa?z9U#=7Si-Fu|tZ!F(oanXPXKGxV$*`eRzwy zvtwrOlqFbHwypcPV4Sgh>}#Sps0@BM@==Bw(Q<0^sPC}T0r^7jRHE{J2`pvhfpc!Y zS#43|nZ)>KOI_{dkH;*sm6o1rhAkQ>E^1&^c)y$nYr+e})~d{=rMC=HW|~T104xbc zlW9zY8EtiilsRSd9Un7BPnz(PN1ho5)3(}6*e{uV@bsc7^Q!R`i_aGG!BebNdPV+E zShr)(TF<_ITF$yVCG)E2F@ky_U(o1D#M-t6@AEJhV|4M^$l%QM!yfeF#OdfS&l}e` z6E0%{_iGBIQ<3X=b6ZI~2P(*mF+XQR)tE0kGxre#*Sf~IHX(plUSEO#f@-_2MPK#B z`ng|Zt1VMoMVA^i(cYb3Rwj8q&*>ln>^${#LVF*7c)O(dibq}co8T^TUbvIMUF$>R z |d68%fFuGU`Ta>eUJ1yz!eLRcwY_TplJaQurkY} z7G7D?!t_^N$S7qR-rUU;6<2D%WN@zaQ19JS=7+Ddg$Jd*jLvMELWPL}djuF9WD3^X z8wE6^- hvSv01 GjclhRgQZR%#{; zqXg2GX9{%~?lkbHuXiV|m-Va3Nzd(5xDz3H=fWi6ws2cKtWJ&(M2OR4{>cMCAmTWV zL+}tgIj%25j*DTt`6UdGwWFiZP?S~=$mHNJ{!OR&2Y~z$2^T&CvuPzy{UV?Gg|os> zm2^L_VU-C#bYOfmwSa>L3wAuhgIX}dI~I{D=~-8TeHreT?fXpvSIhR7gk&AMy*x|w zY?_=XZ0Dxh=&8&;g%o~ym93FR>-DVa2&Eg*Ut6YTIB}4;CCmWYosQs$OL~KY-YelC z9s{lf;}Z|8n^im1WW+;}k7c>cg0%bZtMl{5Gx!kco`oyHm;EIks?K)JoT+a9?#Wj% zyOGr@)H{A>aolaJ5$Fs>Ue`FGEC;KPzL+3bcAb?hy@Y5pOG2V-o~dNZB4t)JJ?I=; z=vq=w$*o`6>}|;#T^E)j6z @6s+ThBwfB+{;>+pInuUfUDOxWhoU$6W_%^W z%T(U
?klkgpnq1aL1A$Y9yw-y#_5E`|gy!1-93Q;ettA$!@ zR?f52&W`2l2(FE9UdLz<^~S_} G2ZdVjc#99OJ>B0(|7M?6yL0-Q$gh^ z8pVvzyE^ZN83^wAdV)GvR*!fx#lPe_CZ4abG>+_1zf`PYFCOvXuD7C5v=8Qqq6eX} zplknItdzYK4ehV(IXdg_WUAw$yLuvUL49%O1oWGXi)BK~TS-cLDjxK#fq6L;b*Nj` zIi|yorz7Haro^Dl`F>p ;eKEHhOW +56j4GC(4jKu%5bC}I z@<^#*;T`=ke(SLh9v&_}0!eNMKL}j?#MqTe9SZFpzbUM###pEYm3kiWUX4gZkyaY& zkz6cQ@&0)EnTGGZQU9GU_BUq@`iswt?9e-fl!TWmsHYaRDLKA1QZ4isLEqlq5j`Do zqA*jiV!3XXp+Zu>fMm4l+R6tURBXJ&r81|T{2s|-HT8k-+mG*NwFi}yFWgw6eFB}i zbab$eZu;woypDa1;)Mz15gK#iCrB54Xh=)oJl1O0^deXu3^vQjAl<#9(yMOM>@cQu z%v8=?UNm{lxvsdNsH~L4t-Z?-2*2)VXJ9Y>GKD7G{@m>^iw -hPJg+!6|N` gQ^hQcFF*9E`DrgVq>)Db8xRM0fzpHYuIcqLM3scFzmr6iCbC?_6a*9lg8qc<=B zO5tIvfPb?2{{!9PMgfih$m|o!;QG^)Fx%P9Xs%riI^z__+AMH+R?zk(e0dJuVnp zi)jSjuW=q Dm+uEESx zkSdza%yJSibI^Zdtx3P*>)+;8A?;CCMnaZ+z!~7;`fVY@dt6L-m5Sg|K_h}{z?kMp zz`Ib<($3|EGEwPqClrC%h% $MCj6%{ew^X&10x6djcHASfe3YInP*}ya#v7dF7l{oyQq+w$0Q*IWhue%69iu( zVTYPXC=}@n2j6;o&0~PEP``aU2Qby}o|_7B=I^Ni%&NqVw=<{(4ek>;wW%pEP1Q}@ zIl(HnwEKJh9l`znG2iig)jX!T{2gk?L?JMK@Rm9D0NHUvY08Sqzv&sr1S9-t#&e3G zKX~>r6XaA<1f!G15gd*u6=~-j!`mc$44l4(6}I%=VH01*uooilk+8RJAf7+&YNQ1I z-LsFjrF>`5vd{W56K@+dv7h-?;o4W@5`r$_`KWOJ?PN)s?w6gAkV~*aE0I{Tck^;A zq0yC~VsZZya(ur>zp*9~KB4TEN$jROyqM_= 0{qQZg9oj=}&zL~?XJ)#i6u&VGkPAjjf8q_H0G3nX#S zMrR$5+2WHhyaIuO&lQiPn@Hgh(0O4B>s9_!@hDv8aKouB>~|3xH^ve1pzBy=#sSao zYVQU*Px}O`Xd*|jZ7KL4DQp}6RxDjYJ<)Q&1Sa4^9Au?>)O47G?MOTb+0)uyU1kYx z!Y~!qEw m58WBQ$-rQ;sl-t6G&Z{1%b5uisoc=6(rXwd57?T4SS2IW7M zk5UA7nAcHD=`01UKo41|<#@2J>j^`Y5XPKE6B!AMS|)Olhxb>clCR10sA!q{ok6N- zuPGw$ft3`60rM*A7Z` Wv~UhOrdxg17HW}Xwxt10RYNsS6}3>OqC#@E#EhigqHx1* b>3F7wKc1^7f&rC0DDE&@uG{Tw4ILlmw1LJDG}hh!8#JX zo&8e$cggXL0ITWoYH>y>BD)l{UrYPXavJjnED8L0o#}5#xP_vkph-26?FFQRcod{Y z!?sCj$tOSvHjOAvq%(|MNl;n&4%jd-OsQneY#Ye#$M5^j85Q0VLu|QR`tj7;n|s;L z;p#WeD6h8{o^|*z$g4W*>-XB9eQwfP|7L>meN&ygLbq>etCpkIwu?wMgQJx@8Rl#z z!0(?mThh+Ib96eG50rV6BU4%$*;<#l!9HQ|3xcSjY`vZaZ4=^O?P;zD`UdRlI7ntz zVY>{tFESfgcNL_AOuiOYN5Gc 04(HAm{VBc~6NWF9vE`rf8*oqA1FSWm zSPeTch?vD9IuHTY&(%;{c4ln>OI%}hN4W(ldkDOQ!glg+4FFt<<`c-*#_F*3nzYxA z7 wrL=b4 q)aFur4;};^rlnaArFd=T|fIUN2=f; z1_z;!dy=?`gD7Oc^**`k|5=AGUPs3NK}Ukdf_DeIuY0B0az`pQgQ?I+Ig!u$N@I%E zN6i$@l<2@b_IYEB{I6#FCJg7TD9c}&-a5)utP>eL!rtl@j(3rQ#1;%@3}qM37A2m} z_T!ql?l)iK*)?Twxk5-Q*F8r~vLFn<@d_H!UW3JDPX=pOP-ei6&`Zy4uFv-M2KiHS zV-(9m(8jmLP#ER1NZ2y5=kLvh@UoUU3cqX6fggcM>j8Eh*ddsb#}oE=GCSfYby7Wu zo4GN9R2i^EP5hn$c7_W4<@U8Ogv87gPiLmQt*PQ0^jpj8xlpH^U#y;}|5^Nh+z6 zJX~yrdv9YnPCgH<-Uu_MQOYero`Lps{736b4TChRUw&8CiEg+9HK{WJ$^(yer;~He zXs$Af|IWOkY9TSn2US!?8T^$4A0K{41vr9s9Je{`&nGTO2fXtGoB<_nz;DI7d-#N? zR`1{K1x=|0D1 8`1>TXsy9{vOs3 >lK;Q!&9B^3*cXkfR}gu-nJy5{k1=lc1LCQ`SyJPxR=fP%!g!+Mf<% z4&NJhXqmkdPM?czR!ibx(I6Gq@zefFZUuKyAC(uBtG9y7&LkmKG)U8vsw^i=Yj3b8 zlXwBBtZ`pILtfOtSfD_4U;pjM(+@eD=DZ0BrRobU15Yo`WoLh9U=?$g^N35_*3%o( zWQ -(nVq9Ja!dTznNjTPw_HtlfZ>$CqQC}7U8KyVs=Aqr1n`>FBKVQ z&-IazNUi>J1l 2~EfbY6V9?XD<1Xh!hxhI22u|)QqGjC7RdT?xsBm zlK7~a{_Av*hwA4;`1Zcf)`(EdJ0WDvo#ws6d?&-9$^Ip+5hmo*PjO`ukC`%ip2{Xz zelYB$b8ofe4Lu6APaM(PHXf_m7M;%SvY&I+(TQxI?c^LjSrc^6?Tj#$I|n`DGhd>E z*3dygg#Xsmz9!E^9y{d~#-TMtm$I|MAu-@9{ObSVUBQ7; $4O6rSK^O_E8KX%M *UHl>80tqLlZI6 z-FNQ2u{ekov}OLl*2B!3CjX6(-@X<&b;HVci?RMEfi`@7DNbwpM+C7OW?23(C;o+% z8(WX+hK+h77pdm;0zii)<}yGTPtsA^q|-iO*ugwvH_!LGDPH6I6d;(mO+T+MpEhZ_ ze-4mWL7m8KgpFqq)-Ppu9|wi7-;Is^9
cWuD-Z>qlU;|KRL{m~#0^bLO_$pG`f#q`WY${g!^`oSE(&@$hKR=&51k zzJCAAMVB8=?_}ES_@g&HS5Wmr+3gSzex=b(Mx-1&QE`|%jDwMcL4F;YNOzGqz`N#6 z4{F#Cj+uOzUUG&mxo({2k{<2uH)JqNEzC7a`@87t4lTI6DMfPs$b0!-?OZ^))-F*5 z@kr=3B>a&x+5Nr|5R<{Qj|tR(gMyD)09XDG+1j5S&pUyYZa2RG-a8S+Z>BZySnxsS z?`Ru@f@nb2k4OSaN15=kqM#ZqYT^z_Ho;*oiTInwOc0n%!U;~pU9|j_%X_2i!$4>= z>H-`-5;1;xAFx~G5>#YOF>rD3q>r1Gn6%CR3YJmY&}HVYjX;QRu?i!+p_S%&Q_CwS ze8)FW3H-#|OR2SI4e{pfBYT04AUIk%+EHVp?Uk7?KH5>(PCA7xyw>sF=9_6E4kCXU zj)PPYD^+H@w>A;`_TU-!0|>c?Ca-1V!kBryX$Fva9QKb4`nOCkwl7^cApR%1^q1M{ z%efz$L)Zfp4tgOf4{-guop0 &uOhMc>}J^R?ah>9D<+uj z$_b??%B*})*p8;%&pZre|6EvZ0;amuxo|}6u3q)R%PbSyRa>@8eS@PHuEVyjZ?RA4 zIYv?|pp jNOIH+AiAWr4NMyKbnc?txqleLhKKOJXIB^oy}Q9 zW~+9tW0)*$Kia>?e)@ntz@|(n9CB JLsg2igYmMwa?Jg`tf z{ {N4VohkFIXSU= zCRa`4al~0w1$7LUTZSMWdY~@ _vd-x}K47-hZ_Pq7Eu zjpw@>4l-*;ahmZ}u}67a0xQ9dyU`2x=D)zAtRCq_JgQdZJ+V1_ufk}UvZ(!4i`b#e z*Zo0*CeLy05RoblSfZ7~{Jr#;hypxR2cSQ9;Cmj!YT&ITJYEMvIXp0=M2x`o{o`T+ zBoi+U5?=VXUI4~|e-(%X{K@=H+>65FX$%<_DAQm3r^#E?0=g( H8%0oZ}Gpsq?{xe#=qKLVX&SB7zzJz@spS{dp zW#<+3E{Bt@_P);K2e;RfOFX@1D>nl-3k6Myqm#nqNCBc%(02MQ9Nv%53ZA0?`NI2Z znE;>q2Q2yni)jP&6#!1@0C@+mHfr|qD=-_e@9qyI_8%?y*p-L*8!;Qi=Cvtc#nd AN^bqWbGL2 nR9x>RBLozoyG;uwWd&%vP|Ztrn#7!T$Q)R8E#+^J-*IdcxxYk}8(QTE@~yl|a=~ zRj=+=I8rMvG%L;VIrR39@ZMe5<08K{1es@}@}d=mt(1^PCrqDA%>F303!7udLAN;e z_0xtDJT2=c@8`+vlq2RGjJi#yYvtWd3KYFs3l8-{y7%&8c)w<^Cfi>CNR310W`=d4 zt;=1%i`(Q9 (YK%mh7gaF7`FX }7dcpDgdK-}&jf1jkUe@`!RYD`TR{w}eT3<9-_1?76xLlNXd-6cX+{C$hDu zC1Uj{=N)zW_1$FsTlWxzyw5e8rR=+l$(DzX){LQp=3n*;o>oo~E}KW{Cj|{LAhk|9 z8IMeXacg|`P}&blzHbCGNAo{@9wty+s&(P5r5hE9Ny{X(Te1dTIdW&4M-j+UX68QD zav%)y2f&(iNVDUYuU$%zEu;kyMu0Hm;rl-bCJ7!21tI1Y5EmlBtnSZHl;Z>WiPr$k z1;7q~$?*tj48Vc#lbSG!0-q;WmOGCOM~9X)UG2E(C#9}b`Xu|CZEu075<}dBd(##; zs8F{jo=KeL>YE#^C3;Q*j>XfbzDciV*i138D|K$T@;(%{hvldBNy>RWXlMQG_0H|w zmgdstW&x$vWVDiV!p{Wq*5 Y_pv>;fgIE`)MpY)ROnR^T*tVK&m*qOhOi&qJf#Mg>;aG!s2czh)#|DMePK zA`>0qD7&Kb+hbxy${5~qBa5S-*t@{XUKlq^s6;Sk3E%lKAd^0bcv!)fIN|YQ;(&gh z*6d&w2~xkcls;Ita`rJ>#&rvmyTzxf&|l|oJ>UcHdKM_Lz->wR&If?^K+Su=8u9CY zzx;>C_Wh0*T{yT9`@y2{@ixs-Yvk4*qVQIBc}k{{BDS7A+Rn3tXpZqZmNjJWN`V=T z$k7?bgBQrn9GS8GYeBR%69-r9)jGWxbc%&hRw-hIJni2Ek^QrLMxAS8KQ=o==a4dm zk2X9%ni^#4R*p*$tQX5VOX$m`izZI-?tLT>nA1g|yfTt{1-Hx**VLm$t;+7qeYJ5* zS&j32j`c;Xfze6fycYXPW_BF$<0O`$1Ll6l(vt0$oJjuHTkol@+!3n@o-YvXZaApU zJQhoDMhl`d;76_u7bzj7q9o4ESQ|?^P0|6F4g`gDh$C;aT{X#Dz{dh?o;tIm=tnTk z#k8+v65==r6v*M7uiph?p0kZIKb~Wc*s(kn``p_ai-BLhv}5TD5rZ@b+aRt2indnv z@8clfE*!*QYzeCgmN=P|l(RNdIZt-93M4$&mfwqKvhTElXE`yKF|57F(y<&EwgsT= z%lBmb!CjBT%dAFNpK;)W
U$L4 zWe;DU_NZHZS8BGm{d~htbM2(|iapJ7*}(iZwHMoS9CVUK6D**25G%mtr7+vQ3xd3j zS(>BmABU=5OE4ePl=VQ#uv_eO0(G-(0tdU`w&c^>RVx CQ(?ggNt^C$8)r zy`k&-OK4f%F#i?C?5(SA!;rCMTB|uLy0`S6AI}9Jo=B+7Y%?eD;AT}`eV_R^mzl>m z_7i>-znP@S)9q)x?fvzs18K{5oAl9{D<;iE(xn{>E=O6|?~qG)SeEKTJH3nh1ZT?z zwZrE(3%Ix;Nm`^euNQs) ze&aHnXVa3jQonnPH@>ddnwemH_7Yn|Rm;@fjJuHmvd}(ww*~z%j6tlG*J=9JV_z^F zJP{nmgp>&@0@QTs_kMH_(J&R~P9&Wdo!?IpjoUOEx-pzJnROvf&gZIi+)dlKTo?HU zyX6a~;Ax8Ude!gX zWM-K%1>v?XN+b{wY*7oiFPS93W6@MUIj`yB$8Zitpq5hsfxuBVn?UOdgPjQ3^TB+7 zb;9&pa`ikKr3Nga)dP)|cC#@rUlI0|^{3g6+Dn@k=n3+=GZ-u(D);naMpV)LBaHCa zvsXns1dWG+Bk4zFa) lO#8@ARtk{U9-QbqC{FqEaiQ^)q3W5ro_9yQ z9o4kc1wCwY*?anFF9)N-+fD333u6=d-kZhg%B$Jm?01;$jaFEWzhUPrecEB9woqnA z@77++j|o#($Eh8)F`73i*`zfz4Ptb5JJR8ZHTmj6nXNlAU)kHLQkn%~r){@22TT`g zUCs8c-f4GzHKj*P5 O%FZH-=uH*fKP73tf*;&rDrFs^fD{+lEmL@IamuoY6 z zyVQX^m#4u 9evP(Th!Xxm`Cf zDxVWnYXTft>HJAYbYH(+)a<-FOlEkU-6ISIR~-FD9IJgpGKl=~N6IbfnF!_s+lp`K z&`!()v|Jtsv{}qL9X0bdud`CRZj~An67?`0t)M)EIr@1x|Fos9T1Ncm(=a=Aj*<5u z90bduj0_>jR+z0PnZ+~i1kS2nZLE%Ac{0R(e>;mQ3?;MW$iA_$O0&dxh^kRKa+Gl) z8)H>3?tS0GgW&F8pkY{ 6Y@FS((p zb|GF508mKyD ?;S!MGcKocMUG>l9FfJUMH zG~mNo0Jy#~OeNa3nR^2^p){`diB*ozkns6gcDw7(FfM9@Z_V%bB{-5VfGj&mo)W(0 zkyX)}d`lu>%PJ2yCML{;`Dv*2XkAlkYn$0aHe{R}2!r%~c{+scxBBh#+0WBmHgr3W zc GwO5Zya=sPsq?z@E?GEJ+@e3Q~i_bsh2JBG#;C@4pFp&jB4n86ZLp7*% zTu XU=Wh+H7(oa&&l_oY# 6NeQ3)#bh)+q;`E5v9L9cx_p`v8TZLwy=GJ|)J+_LK`IS^Zy{#yIIk(6ssqQ4? z&!4e=1AFcf*^s} #* #nt&!5|~Q`{75 zX5i{S^(kq6S>joE PL04}7j+*+U29BxRPNyxxF%ijMeMooYP5+-DSezQ zeR4QsY@8}M&QA_4ZO>OKpT9BtnsC;Fk-B1bBtS9KCxmZT#`0 CEYRd0k-hg5 z)T$v=;(U#^jaZ=ef4y(ZG|24{Q#W_b>j_;a {GsnYL1cS|5NMr@Zlqd1|E++TNs21Ls(h#GHQh=SI70A?z_J|=8TCH*%)8U$ zV(8S=rv oATvg41+vh4pZTFwZ34UBQ?zSu|5w4q*IN@U~A^WY|F zZ;8}(ZTMVjdp6X@C;3nN;C6a?&^C->w!Xv@Dqgff{Dy>$ iY- zv~kNoBA(R^h}SOc6T{=!snoy9ntx_^LnJ~(T#kBJJeLzfn8zVfAchx1e1(qPR8x6> za-X#P=1`pbVkv%o(^R~QwK{Ps|1_la<9ymX`;$uzp!qcK{fU$Q3}3GsFIRx8j@l|# zbFk2G#(=*h)q@Mxx^P{)zTE&jphwx9Rh>@p6u9y~^S-w%PIw(wE)nT Opgd z=59V&(b^-j-`w{CE@b48-*l@VK9c|S#3H54%uZ`sVKp98-uPW=nwffp=pMhsuU@mP zFaqg7jqg5MceUxCiu!2F6&I7n3q>){bY+p1gF y{Vt86@&OFt*k7J}t?3mO@ zNamp6(ijEb|M^oWhpo1_hTM~UetW1u{7PF2G8GZ8PyJZWl3Ae&vr|+XX2Y$yKwvK# zNY2Ksq4}5#==CH37B=L&|F$)N?-DSjz?6yqO#tQ dLu zmswnTo-?LQN#u9->rS>S?Vm`~00c1^d%oxs0;HPa_f?V8>!PZ3_)xU`@X8V2q*_+j zs(#|&xy_l)72@aEknVsPBRo#7Smmlfzh}J6k=^da4kIV;lh1)^R!!o?VF$8x9upXw zaSTOHD8aPXtI0vwq_g2_Q(?gra2{>!3(uJT24zgyZCgFG(egOvDhyLRkju8#Ik2wm zkth|Bb9IxXpLD=+8*Fqh=wS$zdZ8vIq8n7-5M>dQ 52t1w-E%Tn$w6f(u|f!5!RdvRyI~ZLwjc>zO_S{k!>GH zxEuJV$@FrJL?v%2uViTCN0%Bg5^c=!PM5a83BlOmrte>BQ?P3K`)5Rg0}-Sjycf?X zSFFD)G;^gk6x`uF(K52?0PZ|Kc&5VQ6Uj`PxI`OMz0^b(-E*Mb{SPPOqu|X61t1rk z79IS3!5ZD(y?mY%hAn~Ab9fY(hUo?l6m9~Xm|uY$EgPp8;#Fk(hV!QGqL4!Kc0BPQ z*|EF1*W}?9%`0gA{=Glg>j>z 2S9l-+aKit42S= zlBUa{LtlR~Ub-$Kb57RjmmDl3dAF1I{Itm9lxHTr;@keJ@PZS*xcCj&T>r5V*Q%um zc;%GW;0ZIN@4c#U3oKJjv4YRH&uV3ZOVFLk%KEIO=rq !G8iZ;;IH>KRjn9b*FeGYT;iibkd=X9Jgux8bBY+wNA}}i+u;%rJwmw zQYr)JRF) B_<{ V*eyX6*<9qx=)l@v;!U67E}15)A3d6iwQvHJR5!x<}6NM?;P?W^tsNzMvc3v@~t% zU}YORCo9x)r^pP=kjqdepxFJT5SQ!)**m0Fk^x$An8(h+dLR7Ss;h4u6i^E9A2E`t z4gQH>)a@x3D4qE;tNI(<8n4_>&({in240F+W=T sjipy%{*R+}2}&aQDBn3<&TMD#?%mK0-i!21@a;3>|q=0GqQs4$!=p zp#yYLw$?neKvmM9q&$2L41xY@)@79PH` ;YB)FNrnY36!S^pLxCKPyPeZys2RL0vD`D1lt44MS_Rt} MP@4g hO$Qv K&md2ntr7R=m%q|GS$*tRqTJF}pC|Z8J1y zJb<5UW8RV{dXKU|RDpy!dzF? O~89`>e)yr>|-hp?}d`C*^)c zea{=8uh@o80x!=|R8Zm{QQKbrz#3Tm4BHK4%ptVL*mUXMw14T%M%RXl&mZ%%66d8K z+#Mph{T#MaLJGV}8$ir{E+mKMUHyv>wM%pY_w`n+qFNityW~cy7O{~}W^Ybcn?)Ae z&9fB_PXtk+2`3xg{mr2#zF*tJ?~jQK#x$b mkwCUK3g8;(;R;CVRgT>W_WA zequs6ywVbE3e^%5nipwCp_`fOKtmm{1N2LeL;L>Wgm`M{MxR`UI*z?_YTl2W|A*uA z-_4-?NE1LT{=Z9LCzshLdbHl2S+i3HjeQXz<}c{1t|55*Ta>LY`0DeFn>5W2%U|{T z=z%eX zi@uelKD2m|fAk4>Bi0)(65{WMyHfA0o-D|5C7 zy8$XFGC7=$E&%;T&;EEpd{uoyGI{7rlT;zrj5CQoOJM{QX@_!wa>aw!hhoeq$t`nG zf(3BeJyP6fv%67~HF|QOCevFKt?%^c)NFFS4{v|UX1qehUBqB|**A?t6<^JGEY}9| zjo?p~Ro34Qy-yhL=CQyw`<3N1tHYM!N2qzr*ROe|eF7t_GEKYm`6!C5Je` p4dZHl z{#F(LJo0CDx*gNk_5ckP@Hce9uYsri*{ZdiPp{R$JHew7|0HY6pPxMP+w;BVWE%3z z?nJOq@RH!GeDR>%i2cqL%Ft^$Oe>huG9Ru`I|N}$P&XKk4u2w_ z03QI8G$78a9h|s t_ `)dvzY%7SP#>%fhycu5Dy=Zg> z7>jUmoh|y0U?Fv(&5Cz0ze4fDK!$swHP0UIJisApi }1j)v) zA0-j OcPLr(6{!FFFR!`MK2l4yoxXe zRaSPNk;7oWSizX(LSvwPrFlWTUoe378|k~jKi4F5F4qsNoHVP_TD;CX`X)@PKm>$j z&k9-!x!53Xk~9LDD*W=&Y*j}pMN(7>s`(?h02dV;0H}P5???fAE})nLeqPo*e1PT& znC|@w7Xfws77;5$$8=Y-L-O@93W95vj{FEMRW2b`OyYKajULvxL*UNB=H#n^D ^$k%MeQFJE6_;tZQ;+9yrD$qz|#~8)U1i1Zl|mstK!*H?HMh z@ui0*h&Tz?F0$O!i*iZ^e#hX5I2-;sAXr`vz_R25MLGN*&Uyd{ux;tr>O3nqN7xgs zYp{*|j&xaV+M>JOz>YVH3q Sy10O zw|lRB%N0VW)1fF(&D+a}!i#FqzI>uJ%DH ^HW~PLMJh7}W%FlsSnc4# zdGVX&0kz&UbsyGEEWcEvqMm%g)wX7wyV1?aa0!1sWDV#T7R4Ov{5=+jn91CXM4zf1 zmeZGYNliRjR%^10 DsMQdWH`lt+bXk=(! z13mu>Jiexoz#3_)%c4w7MW0UW`*V}fJ(e6Kq*tVy1HZ@EX34F6NMWb1Tm6?+>A8R2Rq{PJR`M76eMUz?T!z zLU8ca!GZnkkvFCI!u=`d81So=BylxOV@ta_t%9|=P%Un;N|^lVEL1&MqvlHchlc}L zkkD-zWn3oxD>|Ly4R)pfGQhUH{P|NAsobCqPt7lUnqszkCY(C;l2e+O0dN&jy~Yu; z+-fjc_e+=cAL2!7&$6No>hPMzuSXFfW)m1qWIAghN7y0CtABEjILuw5FY^rOj2Z*a z{X_D?n-#!t3cWn}Nc8#=rgx*Cd6<3m_DrM^syF(VW4Y#4t@*(7in8(AzmN1Y=jLjz zKHnf(>nRT^8;a1c$h)#bo0 zvv+%~pjhI-mA;hnWqfN9I(Yo_~nd8202 zB9U+3Cmklqy~p#oA0^RUChmM 5J_)z#I9kHSLh35Fu!n=#m)fF9k zRqXEPXy(Qhc>2EWLewpQk#{C@AgLUdJalXUpiy5k0_eII;X8? z6%X|r(h5dXO*r^iO(QMk55>ODxdsNpFqF6Hd?4ggrT1Sx^FQ2yq9S1RV&;K;O+$P` zBr3=XerRgY#2foy=CPULW_)oS4;rsJ=KqHiZh_Fe-sbI`kv0WoIj#WsMOY*csOw^a z=O2!T$%Iqp0iv8y8ApiB`0#LAEnvn-=nW|O6t7Wt^hDH;Wprk6&_Z%7bqE1u0BEo% z_rd`Oj?|Yq1y@DlI7MxyMn=YMX&12JEo1hEx;_uaorBALAo3-7uFEtquqMa+_2gyz z#R%MsamJH2s4+| hZ5oSfr@ZA*h$3;-0PbZxgK_c;D9# z?|&~T$M Rk03clX9Yq&AB zcPbqB%wn|}gy~L-RNq$Crx6lo6{u*x5{V2EDUD6Bl(+fxb2#j`y+~ZioA)Vjqx{rw zg o_!hKT?)s{*;A~4eDu$)xePlb$$Gk@t|Ie~>YdHoQLAOmH4ZMFCV4YAh zf!|V}fn4ov p$IH1xU3#z;BIs|nNAwmQ2n`#kTyTFkJdITmIEUAG=8{BUA(Y0 z%M3?`T}ZPKCn)V6l?=y&LW)H-(qSFC2n8P5E^D* WoFT6kX8(oXw>ri6oMKAHiH)$&yW^yJJm_Y5LJwz5WFYvp`PLggG= z63;G`;HrEY0aHq!6V$A-Qs^%ye38y!izPr8LgUdh$|8Bna;rLtqX$Em+U%AJ7>*WT z^;NkpKp!wYn>@5Lk5*(;VfK5meovadi!nm0ny}d;K*mqrp$>7nta%XxnLDl%n-v(p zb>jgoZnA~5ue`EzmF1hp6N_E5i#OuUo3r>U+{UVAjqRSM9s-+y*~V-AT@QY*AvBy} z)|?)@sB<0Z$&RU0ap2?`aHCn~p9oIOkARfB9nmW( Dka({30xGFiX4RpKuo`I^ zX!cqBfoPF?c@0_H{54ka#dRWHJvw{+XkdawyzM0Rv%eqyf>6PC<$kggwzqF-xpqeM z78Q<3H9>oVLn9>Og+ws53anN&DL$919}Fl0CM<*Y5A`IN)p#WvL9<~sH%EiVr6X@I z6a3vn0P9Wo#Bp)nE>j14^h4;D-1pc^?PB*i`}nKFz>9B46p7=Hm$PbX5M5Q336BS& zeZXtPK)hdGkXp^yt29@Mxqjn#yv?*j9|JJDdX0UJ2HPJB+H%VQ3D5=5`4I@qQVT!W z;GE6<;Tz>3aKkEVIa{&pk|?p|gOq-~%%A&)+ 9A<4)p@z=@t$R9K^F@ z9HKK)K1^7S2L?MbEttyrH=|A9rA!)9Yr cM!;I5cTsbM7?#clke{C?xGzt zqmmISCL#d(N1L+5DDH 7UGh&;M^#6>YkrQ%J-qU8XAzv>E z$2lX3+#$|4lg+7!lj7J3epfy&8>h@!sjMc^X7n&36O%|Ffr(&vGg}1RuGG~Xvm7hy zZ_fl1C-GsofI1Isi^fZShEaO02O2k+ _5{nw^Qs z5*D}Zl;5?Jb=FRfRFsh#tAeF6@INh#l$C);{J|}VRFwtuK(r(cM`h6rISf^A5~s5H zEeiSi6N@YoD;0jA>4*}3*CX2v*hJjWysQN;1|7gdp>g>7A!$SjBkJD+F;%dq^k#5| z7A?#0{Ba}$X&MG5qX(p)nQ?AsIs69=cz9bifiHZ3>hu3EsQ+_60Z
hP`x>|w4M}ezPLxeh|(oRl`E8x$EB1*h}8D67@WuZ@UD*yb6H5Y&K1WNfcC8U+x z@k@M0rYclYPmySDOE$)KmppHbBhNbgwOIb|eP1?pC{EX0@vO S*FJP$mPB89aBq^r19k#zVyt? #E7zLPM;W;7-@ z@i-tZT89h1Lt@TwtlcA?;0Fc4VZ|J3fV&IO@(taWGZK1jk87w~%g^x9GuPnx({{@m zDoZGrLvBT<|6gP1<*1NH=n{v#yCr#prqdU~V7S08nnG+HQz}qoY$}+9t7SpMo75$a zk48P&BHv_ah_PTZ53Grt?Udz>5*G*8Oxd?DsX &g#tvO=SQ9ci`eL?N zCGRl*dZYZIwlzaeH56USy9;~%-dd8-{K5 2RQ)UvuiW~oXCj2WiTKcRiQwp@?|UmqsV4Ons&I-Hs;@urE2#acwH3!C^**Q0 z+YTQDT60&*sb2KZ_$)`xZe?VP=Y5Upxzkne-D-!k`zS!TWf}E;SS#1m5F)K$bcsp< zVq0GSaAfZn=nRJHdNjUkKA!YPsvV;_L&9ksBS0d*U*;)XRsyohk!@~jvf7nw &aLprTCP(Ogh>;e#FDJa|APM;&-=*qFKJ>uLaHsEr505+ z5qocNMzV}zaxPg5i$A$jSsBvBQJ71QNnV4=zgIg{a<1xFmyD)N)-&=~)stfh{T}Ps zKO4Hi^q!CRGg}v{*!8&OPOfK4nk*z>8NTOZtfC`-BNTUR;uyL^D4cAq&DLsol4!+= z`M&Zg!zS+fjOj3q50oc7u1^YExI(Tc-<+2_d|rzjlz tQjx1mP#CjcmsOB$f4&wWeB z`9H%ZF!c!NeFIkhs@g2zBw&_= pg_h7 zE|MnVWVBEZm7xvFmQMad=1V0>dW^PXsQZj~_DA$yG9{;f?r~Y%i{<3IF+y(Q4_35B z(LxT$&uV5c)m-82w?C`TPTk`Tag!F+eKYq $n_B-Rv=tCb4b}mJ#BA zfW|WxmDAuDQ%d+f79y-qVWGwT!qGf N31rC4z(p=L}!?Cm-M^shq zAiDZY&VW2fUh5_C?^ ZP6^z<{}n2bftYcK8Bu#`BI z*~#ALx9Vpx64@i@p_vbNFBVPZtFBD8oNH+s8RAqMul0Fv<7zOJO$l2GPgJO@uN>91 zE|sS-LWJx@C!el^c3z%9F?Ed+J%9If?k>2SZ#1u?d{B803E`DxW3XzuEVA|G?Zd8E z8VVoJ4;{Z1*&warPfN88$37LgP-0k HB~ ?eJRIly1LVPQ+ z?(^II_3gSQJGG!Vf(7#}lf$w;frK1WXk=219TnW2|E-ExH~G(ahK`m6!VJi_A^zS6 zCOd4;6KOHKycIo=nW(lAOv8SiLp7Xi+4(Q#Tk$=i!wTa)bncv2(}Sp9)`(gx%Z2X# z-?#W3Q~5i)VZ I8b2XL=yO~YXt^v-o zCvW*}d5&bc1V9PcT(WuUCTE*|BX8=#XDL-~0&e1Rnqv-gOD1tsT(V;zj^!b%CP+!S zcy#CeS8|V2m>@0clP%ZxA=MSl!LVwBV#H4`x)Tji%E9Nd&L3ue$iUL{tM?4^a^h Tv7@KPDmwsR&TR zfM$8iExM}ZHV^~^Ejs{T695Y$0C;itZn1O!#!#yH8WVuSX-nUp!v^O-DNyBp23VF> zKNBvNJC#R9S6E9%P_e~U{XrdOP>`EOz*lYE0u4o)21$-&q9iQH>403q&0Nlx)k`2O zPr93tP%mFt`w;2=Qqd{z4z8M7;#gR>29db)Q~=v#AzuDib+NtFFkJ`*&95k5^NO+$ znxtA;I;5MjY2IcwY;~fQTk@Nl#VG* <&o{y z*4RFO$z4>D&HjvSkDUz%oLR~&6rE@jM^|XBe)YX`>=l#%iBz^s40DKdJv%tJSSstt zqxyQi5-WAcr7K2=rKzJS79f45SQ#JC_2RhL##*0!^X2vT_fz8cg|_EWu4D1~Yz8Yf zs+7j =gcTG*0-S!D>0bcT>JGiWS{2U=e9nC>8s6M;scKYO!kQx!o?H z6S37H=V{8P3n5WjOQzshPCKOzj_>r*jI=&-Ir6{b^sVu;v{dcmT8hE`h(i(QjLZ;y zN}3URM;^L5$XWD0(8iI)DT3tB^L#q`*37+{8reVuxakx#AO={*E7>_cztc|d#}^Bu z4Q~+X_ZpDo ZJM-p6 z{VmmyB+Os!aRC{^{)!K6WrF?z8XU3oIpJ>UYYv>qW*4Xzl;n2jMqcn zV#WE{0Bjy2#L{5FQrb;@oJ!>6;Mn7alwc7L2Om@aSXN~!Df{J>1~wFgseR`~Hi6%( z%Gx;*|LP*Nh7?adSohrz8;SQ9I@Q6BuRWx*qrH1JAX4$YT85mGu%r(7SQWR!%E!~Z zg72#L^ImyM5QHV~p?3-jB=X{o`o1yvEhAT}D2#hmVQ2a#bArILd8bNmt>Jg&TEU+N zOY6xDrLu0Qh)0UPk(Bt%ZqE%ku+*N6*X{4kDAt|-Sp ss%3wd_6X-faJeL%8B9BY?bQ?0+t_Y22J^6ONjCkdWlrCcIlu zvEZW{P5l&kazk*vMQNeQ)mFYYJyZUIY;DbO202Y5WZ+6gMmYHGIbyF6ic-+x7yM%i zm #&iOF`R zw;+*~E0lM^BA7Kvvua$uV=`N_sD#?wuNHSW^^K5VO=W=fc$_~H$;&S W(05er&z4N-=t?+I}P8Nzx;Dx=BnzSZs5na?$8WqTG>AN%D zYkIUjW3`r2`17*I`br7nUL|SzY )&z@Dg6p(a+?*alDN zs Ae6&7_jRMk5bahEKuQ7xX=>T3 z)eUWS7N4Co`L?-K)zt8mWMlB;HjN??TS65sPnrvOzy%y8RQ$9C{s}3iKu}xay(=$# zr=}1k?F7$;wQB@fy>PdB-0H3SfWqVS(nl9aH1?Al4_$n|nTvpCmusZ-2`cu+^?yP6 z@Ns{rryj}ketJ(qhk~(k85F|oqdky$uY1~=!clt0@3Cu*1f^r66$|1I;sCU0oG4~U zdr2W;x2AsGTBgZd(O7M|-rD;&BDznfF)0$^7ZkpVKAR6bC(8ekE|2P)`*_$Go1CUA zo$uwpcJyp!?SUWm;F4%Q((Tee*wQ?l!W$F4WZH5`G&WT7=5TCPx9d3HQH}Vz!$3*M ze(8w;-5Zmmi}9cWSfC;f-W}wjCCiy8sRqDaRE!HsvF_(ev3X#glFRnrQu-r^KFu>+ z)9E{6Zvb(Ohb607c!~}604!#s#69<{Y`+Rv0Arm3LBD^9J!vwI @=!Mk0XX4yh|4ZEZMV%f6 zP%K(}vm-);gw?%vV=>7 gDSq=f$Z7|A7EB>$)aY8L6Eo)YEDdS zYkaOp;l3#inGMWHZ8}?5H;aJ%4q&tH-~gg+w{Q%F|G=_93!1L%YVi_S%x7!z -4 zhL3$-ww>IZ0Na8}iW~jPcEsaEwIw+!pFe(5YL`0*;EE9U=lB9=rCX(!DXleOR=)mZ zQ(3b)5%JZ3_#0~}yhTpQ@*8<4wcXcov{FN7Y3_MS5rDf}RhRP|lZxU~-lpb^@+Zm_ zELAbxuRiu=M0gd*?TxG<(5C0(THFd^#e|HmbuDC5Q(Kg>pK+=b#5P5`&0pMWY82Xl zWVqP8B{MI|Pq9oJo@@ESb?-(ol(Os4`hh+DbB{vlmT1$RGWI02M;!FtErFZ|hX5 zH|C&jL#e5t!0X9B=oKn2Qd)E&MDoTZc|&rmx><$4P1WI &WRxF7^9L2WI##X>Aa-S~HO8f48kv&^FJS#A+A{~K;u5D=*yAc?H5XY@KNX*W zR7=`aOPVw*QT&&q8Tn-}Ju_#QqPXf ^ (MmaO<6E+2YV+QBhcVxR15~J9qLu>#}vg83e5TBFzsHF@l<`*u!Nos z@T(LyFp#tWJiWJ*kZ*A^w*#&LM&ZQ&2~@y|Mg!Lxh&TQRDFkY}{%aQSYp+`)Qzt94 zsvMcfz;Zc14qB@!pf)ahyF(AhqohxBH1P^emKth0GlFA*r(nZkY2~m&Sr%VIRhL)o z&O>%u@)G2pBDv+`B)p2=OH4H@BU!gUyw!E z_#@?XnV>@52p^_nFY!+c2%Mfp?6W;QHp=rvE9wCdtxH@kM6nr*go8uFN3MR$Z5n@g zNVZYEe#W*s(+fH?M4nV|x|t&lcv$QR<6Fu!wly=}Lr6%Ye+czE!U=UcHK6s#Wylzc ztgcf; uzf8 zk(_re>sycs@f(ed92O=xgg!Ta>CUqCmT~gRmp0)OgT!C{-omDk8~)RInLYU%h;3Ww z`(`u0mw=0u#?){(uww^*7(i)39NKR(-v&ep%)Z=OzNAdvz8^xv;;TJK0?H}~);en= zAx3HSdsY6SQFHk)3=3AxX_3e%4Tfn9n|`?)+$jG*92@|TRYOUKD|}%?X`SRl)`Qgw zCM~UgNNddvVybPx%di|nSVD|_u@VUgUL7bSC>j*`sR(?G#=z9of%k^`9gK#CChlV? zJ@x)fl+>+9{Y8l87nJ;3p<-t>lKwlMz7zXfaRkcoTytfAG#WeFb=dEc(rd +XAVW2xVJOd_9tz=-BIbpPp&fkvbQ@R{5Ru< zi`(#q0@bs2Z_<9zX-$E FLbJmKbF0W0f(imX{u-Sli z`#)?uF#Q_{VBbdPZ&BrU2#%w17w!M998W>%RJ Y8%6b2w|XBPviR1hnJPZGlQW~x14v|#a~t8S zzss1ydL8UW0t;M~QWn?vDurSb-)b#z{pxbwJU%UCf0wjn@gu-;W>M~92mB4v-756! zy#o%78m3XYZHjKh{Ae9#Mri#e@Mic1{!%lMsPiN;&qJqDzYD{U2OTd>k(4aEuigfu zj2W|v)hS|CPThU}w2UF9OjPrJsZYARV*#MX XhmWIhlEyX#$SPh<-aoXk)S|eM!J&Z*YYR1>!M@ z$gGv6;_C7HjG*wcgw)BNZ`~0il=91WEhq=PnGg*KqOr&DLeH09tC_njT8bi+s57kd zN^?nnJV{8XN|d6yRoHLq7r}r2O`xCw*|PsUoW%czQ2$>W8Iaelfootgta-RfGC~@- zY^_MALJG|gY8XHbB(Mi=SxSER$3>!Q5e7=KbVR9CRl=^ zC$?dSMkYO7eMB?9QINKdm;J6$-^N#xDoZA~Tf1-vW}^OVUQXCA78*{8*PnzOM5{Rt z)PB`oY>1?`w}_Rb4P|J2Hlc(ZKU8XWz3RJe{N!ES%4WRb<@VRjc7nc0D(9qo+2eLg z;rjD;;48&QpNTP{Qb-4u9zX_b^}T`~seHQmHdE8hw`g8%uwNXuAgEb;zZp zPbxN99Yi<&{fp(df1;5g-WxlrtvnvcuWC8vNtBqjJofzgki`czs;GhHKApVC)h)~B zdLB@BVM)Lw-K9=`;h~Y@@>O>8!92Fw)T{|3zI6SedoF8?pwZ3KpzUP=sDLazWq N?s^gkQ~^ommLTuKZOB*c~gd`{*K{M4`Yi&O5lCb2~{sl=lLz)+I(6#I<8 z@tS^)9(1?|#n(Tb!K}be>P~?x2ptK$0uihI(ZliAaOk=0hu6^6R<(ZUdC{3an-Kb> z$3t=_b`c0roCjkA53x4?a5^+VMBB?{nSxm18_LEtB*N&61#J)Q1GmRc?v<&BprH{& zC4cnAVJh~#{6kK3S#L#SoCL!DrIzszR{+`g0~?fs9UR3scnhF{Mu3mK-y;R>lZi>= z?u*5ezoZ|=kBG0{1H;{67tcXQooZK$&8 cF96fk0KP)xoz>>rLZf?n;vUriUA4AnL1)9~I$488&U zupxfWSAeHaYy^8j>X$K(^?rGC2yM-rczziAO~U WqSw;1q)6V$WH0fIwW%MJ-xkE@%&&5TE zHlK9a^`ABASFq>M=A(>l{?HU#4 s7+3eZH<|@-yO zKo}dxon0U)hKO`g8Lfdknv<#smX%0~=K~NqXu!Hd5JQ6uf^`>emehxmeu^r7jhOoh z^r>;Liwny6zc u5BS3{kJn)GT4$Lq7D@HOo{xM+6 z=|!Ho&Np~>1%;L%Ow#$`r)69Nv>gbpJR!bB<;@(z@jRB?llZmognm^1gZ9SnDV-)9 z#*$YhB2TrxD;w0czkfWP3vKnr+uS_Y#M3#W33uTR#uRh0dTmbS^>by+7Ss-#QaSaD zacjm-6&BkG?`p37q?fQzdb4w1kW>0n%j)jC<2;?|WAQ?&Xf!;Vl`J?G`FkbCIbX*;OHB7J;_nqT1cZwl?VAw7ndu)w3m?~93(O{ofLTAJAS33mx z7g4skWzw5)5ngRGg+O$$`ui_@dK_vgU^*LD#Wv=CRy!?WR6m5UGFBGeWaEiheH6bI z+3r>lj0e L>m%5+f&0>}Gdy~K8~ z()Lg2%9*s_o%$@sIk~8Qb8|@>UIjoHnv`yPsyD|XG_>c}8r>fPU#t9oMO}M5)8GGJ zw~x<9gqlQHa-Ahzs0ec#SuVL}NV(*egyfb@DHSqzMXbzi2$OQjEpkaAV`4&*%b5Fe z+pO 9^=pPuN_*ixEZL)w$A9-a>0WkA$m3d;pIezPesPLn=xDVZ|xov8eVC?740 z_)kjbL*vW+Rf+m>hi@eC7Z3qga<<=}FTgU=VLHz&Jy5UanPNLxL1iY^IwRP%Djs#< z2S+#My^n|aKAEId^9rmgg=+7S{rdhDQ@_phuybL44&~GoGQ4rtT(oJiwohh~sEXcR z^$T)hQoB{PT_@?zXKQD>`lU{`UH_>=L$wPfVhDQ p{C7 OHbj22omLfR@x1VF+;Wz|cO%~trC}A3{S00);n)W3$B1{2g<3N!9~(-`JyPm< z(0(etn|64^>?~E~)*Bbi!b#=%xFYb(0GTqPcPTQ`0s9&%I9!@|XZKNi2?5m#=}!z* z>D}I;7Em0Tppw#1rsMx&&sj@i5)4ka>_wAi;EE14?}H>vZD2Q<9BaXE&d~$ cm;<2hgSw(M<|micYi!3s&v31 zjNVGCo90&%=ZUI&PzMXj_EN0ErbkhC$LUc?a!&$Gn}i+~h}J1u2;}Mm3gR#_#X@L@ zwUOcw7z#Hz))xA=!+SSu$g N2T?OIWtRcN3WavmTlbqV$K zO&igurAr1?K2NPIdg0!2t>g3nW1>niPsVx{eS#_`&MqsspxOR`lhS?tWjA!FLX4CF zlbJaHyKxLe0tbt2#wyh%`wT1>vae+Tm>w6~KY2@L_ 9J$2@`{gA*~_pBY7$A*qc&)6nW_6u_P7v%WGiMQ#Ss^oVPkJh-NX1zn=yv0r$ zyyixI)6o;M|76aAT$kecRc$oChAT7gsN!X>@jXAt9eKICjsM9^@6p+76l=rD3~`e@ zWUbKII@u<_NO$N(^GJAg5q1v`X9Kht-^tju_ue&x#IQ ^~herTx4XpdP**07WdUmukl1upg zEkFMgBnBYaZ*$Ys#MJoRrcfF%xuf*L`+zaXsc3Bti(ZtHRE#OK0}Q!$q#agPRs*}G z?%P*ZrcS(Rs&4y%P_ce;RIXC(eusMrLak+J7;aDU3JdWexDt89LoKwm zZo${dEqKM*dLOH)j~d9iYUSAW+AZcOq}B7WG;&0(*jmo%jHIb|=9jsc*0gg->f)## zNBA??t#wmuJ{0R*b9lqCy?U}bw_S@$D)fBpI^ue}DHQjLfy;HV#ROr*4(swR8o(=f zq~nN`v~BfM6R1WXS=m!5t?F;2)hLFn6a7lwL$_%T &Qcamw_8 EO*lwc zgdJvBaJniubT%FLYK~_S``SNRZQ`q%t#j#s53UN1ZSe+6?iRc3w3GAVMPw&N>gY-* zPSL4+2-mI3F7(2vJz+e=cJr`ToI3DwLhRieyy=NCW#!V# AlmS%&!X`13A)mI< zo_K=FeNO+H*Q@k0s@?3l_>M%ekAz%LBfBJKNsULV;mQ2R9K>r$JmCYTE+oq84*inR zb2jIw*{UAlgZCG&lOhXkpKDJyxih~}CQC0%W+#ChkndBcCg4|Nsl$)9dW^`(8ih%n z4i~43Lte+ivnKrObi@V2bxP1qJZOPU)s7Tq;1qM5S!ysq7=~Um({Y571M+i3pB1#) zXI;H5g`;^iU>?=VlJGT)g!lNZ)H=hDW@s}dO@!~9dLGXD&IDoc(aZ=KaHH;kl%Gf7 z^#Z;AHVzBiSZ}IEPR?>-5z90`KHNq=8Q9}172|FxvjBDfr3nyJx?F@UcOrR&XGYLr zy|?fEROxrOY llX9z@4m>E;NZ zyB2R*c-&M}gSTg&^93)I%rk`#1z2lV+aff|K-y!&@OfcXvE32UTAUfEL5~ewmD+Ts z6{|?j$9*NQ&t <@At0Os50zZ-Tu}dagOnQd3^TkW_pZCVwZjR8$I4S z!`rO>cua?7b4M2Li+}T|z#+q;GFke-a6^Mh!-LSM zbU}(|Mcz}J2;eYN$Za_#H==>ED+0J_{3ZW@3U-;>bNYYz0+;Fe7uNi7YX@N!prU2R z@5u$RUfcRAXc!1Ec7#TRLunXoq{^6aF8BARIh|7YTt3KDG+q^d!=YV|14H}WZyaJ- zA^LUBuMYWcOd~R~O9!5-`-S)bkh(uR>lVBQtc&{i+!sp%R4lA4whz`-BbPt-;b@gY z% epgmwkELm3tVvt{yaX@QSST_9Xlq?B7`Z{h_C(T6_&kHe-l2!Y za-GG1QG=$}3B~FT5uCdy=jM~{VKvUpX5;)%X9YU1(b)r+ IagBW zwOXaNyzJ$|BublGTjm(yvF3yiPizanB(RY#Tck4*e6SByrS_#f5^S3w^K}#`?IkBK zfcpP8o`9RE1pW6@-|HBDS*sB(@SFx(xjqZqL%rIyO5+S)_Yv3d`Mh=3 zbY7U6Dw=TX#J)K7yJv0iQA`puLoV(I{)Fv_b;$0<16zoof5TLI`#RAM{cj={Zky#^ z{F-587n9FV_7@rY1) Ucenmhv&dV7LfiJsAKr`y%J=6th4gH3V#-#b2H+1 zGYx(zYU0*Sc?*~R?@7Jz!CITSt$|#Q2cmzr p1to{e9xLkf6F(!+w+f{&4tpu81SSN@qo=Od+7s z$R-5LjZzAa`Rd=l8J8Zdtp~A<4|Pt>;mQz!jwTs2vBBaMxQjpEUfbK&%}H pAoOFNh6n%<-b~_tM!HDNgrRGF>=GZRTU1mEw08OgH=9!9hW6N;+a7 z?Y)2Vm|ly9rS^^mzw-^{i}1V?#@p5!q;%rPz1T$Bi`-4?0N!Sj=l1Gp_; &19obw8hq+0Io>DFSG=|B+x 3MCa_#&uki>UXKO)6x-5$pvjGT#`$7C7%*zN_aQqG zba1P0Pv?Z0o*3xkQq3j_H` NpHns4jl;@rx1hUbfPf#ND`hE8`_VEoCWm5yXw?f zme`|r@`wD)%(E4N_dp1+c||*z-`ate4xivzEO=ZizwXB@f gUVtGt>Nr;pE9wk?i2Z958gJ^HUPLv^2;iHRb!9g7C zgRMp20qkBzVmI8Oc1up3NBOt4VkKa}9EEZktQ+st28=B=hL}<0zz1&u%MBc43Lj9G z4M47Gh2%)k7E7ghyc2rU4$t0Bpn@)rLO@$Jt;a!XiIb{s--TPia}=Lo57C=*bEcN? z+!Ustn1&8zPsyn;RLIi$eHU7nZwnFlgFj^Jz2O`LUs>Nzpelm%gq_QZL4`E1kDOuI z!B`C3eS=ZKjP)ZA#^nKJ#%v}q46D0J);tltyt4Gq!uk FJa9Oy@WFWkT69-Wchikt(G$<3xrbr Y% z0D9VX2M3apb0bM#ojf0EocBszL+l&u;*xY=2JEf?-TD&i{ZWBULtiU!AE)n&>VN1^ zCXECO;@;EPK=c7R%%5p>2DR?mRB4K;PT=YNYyZ7&pOR#UimfW-MhEwM98f>j?)F2- z!S0?Isq-_v+31W6FtxSaRt1s>E^z&Axgoojkiww-^#MFQJNc|T(4IqARCM@sp}cCP zC+ikf*_F!&LtI!IC7spv%Ta35*VWp*ekvl)*(6K!9R06INN?{uUTwlHRbH(K=iKb* z_UC_1y8mp@a3{*UwUw>!B{0Iwd)Q)JQ=NYN^VBmtn5Cjr-+#z2HI{HiGKX c0qH+y$ofN^#CXN?LkVM)vi)aO@4)^iY2W~qIW2uT2$AW=_(Y4*;5|B zq{Ur0lI~-0I*vJWWCtBY%yP>;SNhM5zXkUl%! 74mp~_5UqMV2%Xyq|)zD z7a%Q3wh*iUZB!-Ta>fU;3!eY>3;-(;ZKy@`C53JGb!Ls1(_WjPM7B31ESMZCD{@YQ z2JDJmffeM1$d{=WLUO;yT|Ul!lS4D~q7k>JG&a*wKFOASyjSi@vk#{ rb!`8Y_2b`uWH#E-?YHFqrjWF-`6R$|G(ej z?%gn? Dg5d3M(Wnf?kzTQ%= z`dYn}oE7_u jIB77mJ=+#hU m$<1!j#YTv5GO==0^PI6 zC5eR^^#q-=ZBC2CZf>XW6LK3zn7Z}ni1Vum-;Ey)Df=G=govlnlyOsUxgkcGK_fxz z*%Fp;C40%NBN<~Km!SLuj* 3 z^+{~SR~|}(fB^e&9eb$@srYe*$x^QBrPWX3*|O>bbR3}-xk%!PQ61f2b$=s_U7TuO zlZfs!)UKI(wE~)g7MriN+OgC%b=dm(z%kIg2Ms2)`8Yq06D9?=mtO`r4TuniojzH) z1I7DtJ2tt#$gG^rGv^fWQc`(IfX&2%V4J0UT$9W9Z6nI8H7<|&%ydx0O527dj_hm5 zCL?o|3qhDG(u kNt$ykSfN(xVab@pu-;~TG&rr_}f#(O$yMia1s{g>=94$9JOZe2cg@$z?ud%XH zH3AEQDq>cA7m1vGLp{GBNBsay^Z|*NTKePb-CIo0+rZkT9=AX|KLhG_GInrHQ#*Kd zsxA?`2e+OcAG~S0s>k8rR#hjm92>zC@fh52^EXqRP~bLsentiS2}niED%8N99mJY} zHefE;hr1&<>pB_ =$#R0sTDh4_;sO$Bmvh z0n74+1p?=_rb|5oTo@Qdq;Oq{2XljiGaPtbzZFe4%b1@klInX^E31jjSL7~geW kgMMk;9;8cd=j#dQ)2-E@7ps(HAb9ZB6ayr?@*~Lc_!#-^A z4vKzlw-#2|{@Ngw$iZNf7Z^WatvD%b|Lbm-fxoyfV;%4U&^O8}B^xr&-bsqa4DA0b zFqZ#t+^Oa{7V2Q2V1b?PrsUd(;1|c4FR__$evus-b`48{if*rB17)b`3PJ)2Vt{5( z_MFTx0lQK@T(%PhF#I3@mH|BnmmI~dxPTzVB^TTQ*v|5R1yJk#AIRfcC~|SjwqJw) E0| Date: Sat, 14 Mar 2026 14:12:31 +0200 Subject: [PATCH 3/6] Added record support --- Ports/CLDC11/src/java/lang/Class.java | 4 ++ Ports/CLDC11/src/java/lang/Record.java | 37 +++++++++++++++++++ .../official-experimental-java-17-support.md | 19 ++++++++-- .../hellocodenameone/tests/Java17Tests.java | 32 ++++++++++++++-- .../tests/OrientationLockScreenshotTest.java | 2 +- vm/JavaAPI/src/java/lang/Class.java | 4 ++ vm/JavaAPI/src/java/lang/Record.java | 37 +++++++++++++++++++ 7 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 Ports/CLDC11/src/java/lang/Record.java create mode 100644 vm/JavaAPI/src/java/lang/Record.java diff --git a/Ports/CLDC11/src/java/lang/Class.java b/Ports/CLDC11/src/java/lang/Class.java index d79fe6660e..2e640a47ab 100644 --- a/Ports/CLDC11/src/java/lang/Class.java +++ b/Ports/CLDC11/src/java/lang/Class.java @@ -278,4 +278,8 @@ public Constructor getEnclosingConstructor() { public boolean isLocalClass() { return false; } + + public boolean isRecord() { + return this != java.lang.Record.class && java.lang.Record.class.isAssignableFrom(this); + } } diff --git a/Ports/CLDC11/src/java/lang/Record.java b/Ports/CLDC11/src/java/lang/Record.java new file mode 100644 index 0000000000..d870566c2b --- /dev/null +++ b/Ports/CLDC11/src/java/lang/Record.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang; + +/** + * Base class for Java record types. + */ +public abstract class Record { + protected Record() { + } + + public abstract boolean equals(Object obj); + + public abstract int hashCode(); + + public abstract String toString(); +} diff --git a/docs/website/content/blog/official-experimental-java-17-support.md b/docs/website/content/blog/official-experimental-java-17-support.md index 1f39718e8e..1addcf559a 100644 --- a/docs/website/content/blog/official-experimental-java-17-support.md +++ b/docs/website/content/blog/official-experimental-java-17-support.md @@ -4,8 +4,8 @@ date: '2026-03-14' author: Shai Almog slug: official-experimental-java-17-support url: /blog/official-experimental-java-17-support/ -description: Codename One projects generated with the Initializr can now use official experimental Java 17 support, with most modern targets expected to work and more JDK support planned next. -feed_html: ' Codename One projects generated with the Initializr can now use official experimental Java 17 support, with most modern targets expected to work and more JDK support planned next.' +description: Codename One projects generated with the Initializr can now use official experimental Java 17 support, with record support arriving in Codename One 7.0.229 on March 20, 2026, and more JDK support planned next. +feed_html: '
Codename One projects generated with the Initializr can now use official experimental Java 17 support, with record support arriving in Codename One 7.0.229 on March 20, 2026, and more JDK support planned next.' ---  @@ -32,6 +32,8 @@ There are a few caveats you should know before jumping in: This support includes modern language syntax that makes day to day code much nicer to write. +At the moment this includes features like `var`, switch expressions and text blocks. Record support is planned for Codename One `7.0.229`, slated for release on March 20, 2026. + For example, `var` lets us remove obvious type boilerplate from local variables: ```java @@ -57,7 +59,18 @@ var textBlock = """ """; ``` -That means you can start using language improvements that make code cleaner and easier to read. +Record syntax is also queued for Codename One `7.0.229`, slated for release on March 20, 2026, so Java 17 projects will be able to use compact immutable data carriers too. + +For example, this record will compile once `7.0.229` is available on March 20, 2026: + +```java +record Person(String name, int age) {} + +var person = new Person("Duke", 29); +System.out.println(person.name()); +``` + +That means you can start using language improvements that make code cleaner and easier to read, and records will join that list in `7.0.229` on March 20, 2026. This still does not include support for newer JDK APIs such as streams. As mentioned in the previous post, if you need streams today there is already a cn1lib solution for that, and we hope to improve the built-in story over time. diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java index b14782b6bb..d4d3c52cdd 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java @@ -1,21 +1,40 @@ package com.codenameone.examples.hellocodenameone.tests; public class Java17Tests extends BaseTest { + record MyRecord(int val, String otherVal) { + @Override + public String toString() { + return "MyRecord[val=" + val + ", otherVal=" + otherVal + "]"; + } + + @Override + public int hashCode() { + return 31 * val + otherVal.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MyRecord)) { + return false; + } + MyRecord other = (MyRecord)obj; + return val == other.val && otherVal.equals(other.otherVal); + } + } + @Override public boolean shouldTakeScreenshot() { return false; } - //record MyRecord(int val, String otherVal) {} - @Override public boolean runTest() throws Exception { var greeting = "Hello"; var target = "Codename One"; + var record = new MyRecord(2, "V"); var message = switch (greeting.length()) { case 5 -> greeting + " " + target; - //case 7 -> new MyRecord(2, "V"); default -> "unexpected"; }; @@ -26,6 +45,13 @@ public boolean runTest() throws Exception { assertEqual("Hello Codename One", message); assertEqual("Java 17 language features\nshould compile in tests.\n", textBlock); + assertEqual(2, record.val()); + assertEqual("V", record.otherVal()); + assertTrue(record instanceof java.lang.Record); + assertTrue(record.getClass().isRecord()); + assertEqual("MyRecord[val=2, otherVal=V]", record.toString()); + assertEqual(148, record.hashCode()); + assertTrue(record.equals(new MyRecord(2, "V"))); done(); return true; } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/OrientationLockScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/OrientationLockScreenshotTest.java index 62e75b57c9..91b37b9595 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/OrientationLockScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/OrientationLockScreenshotTest.java @@ -17,6 +17,7 @@ public boolean runTest() { protected void onShowCompleted() { CN.lockOrientation(false); waitForOrientation(this, false, () -> { + waitFor(50); Cn1ssDeviceRunnerHelper.emitCurrentFormScreenshot("landscape"); CN.lockOrientation(true); waitForOrientation(this, true, OrientationLockScreenshotTest.this::done); @@ -33,7 +34,6 @@ private void waitForOrientation(Form form, boolean portrait, Runnable onDone) { } private void waitForOrientation(Form form, boolean portrait, int attemptsLeft, Runnable onDone) { - waitFor(50); if (CN.isPortrait() == portrait || attemptsLeft <= 0) { onDone.run(); return; diff --git a/vm/JavaAPI/src/java/lang/Class.java b/vm/JavaAPI/src/java/lang/Class.java index 8422cc3d90..debb2c8d0e 100644 --- a/vm/JavaAPI/src/java/lang/Class.java +++ b/vm/JavaAPI/src/java/lang/Class.java @@ -341,4 +341,8 @@ public Constructor getEnclosingConstructor() { public boolean isLocalClass() { return false; } + + public boolean isRecord() { + return this != java.lang.Record.class && java.lang.Record.class.isAssignableFrom(this); + } } diff --git a/vm/JavaAPI/src/java/lang/Record.java b/vm/JavaAPI/src/java/lang/Record.java new file mode 100644 index 0000000000..d870566c2b --- /dev/null +++ b/vm/JavaAPI/src/java/lang/Record.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package java.lang; + +/** + * Base class for Java record types. + */ +public abstract class Record { + protected Record() { + } + + public abstract boolean equals(Object obj); + + public abstract int hashCode(); + + public abstract String toString(); +} From 2e008285a8f846cf00d61e51ef89eb62897a88b4 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 14 Mar 2026 14:36:06 +0200 Subject: [PATCH 4/6] Fixed Java 17 build options for Android --- .../codename1/builders/AndroidGradleBuilder.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java index c4cd8ffaee..4377ebab93 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java @@ -3607,9 +3607,15 @@ public void usesClassMethod(String cls, String method) { String compileSdkVersion = "'android-21'"; - String java8P2 = ""; - if(useJava8SourceLevel) { - java8P2 = " compileOptions {\n" + + int projectJavaVersion = parseVersionStringAsInt(request.getArg("java.version", "8")); + String javaCompileOptions = ""; + if (projectJavaVersion >= 17 && useGradle8) { + javaCompileOptions = " compileOptions {\n" + + " sourceCompatibility JavaVersion.toVersion(17)\n" + + " targetCompatibility JavaVersion.toVersion(17)\n" + + " }\n"; + } else if(useJava8SourceLevel) { + javaCompileOptions = " compileOptions {\n" + " sourceCompatibility JavaVersion.VERSION_1_8\n" + " targetCompatibility JavaVersion.VERSION_1_8\n" + " }\n"; @@ -3770,7 +3776,7 @@ public void usesClassMethod(String cls, String method) { + multidex + request.getArg("android.xgradle_default_config", "") + " }\n" - + java8P2 + + javaCompileOptions + " sourceSets {\n" + " main {\n" + " aidl.srcDirs = ['src/main/java']\n" From 57246b717fd85fe97317b7832a0a4933023b697d Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 14 Mar 2026 15:03:07 +0200 Subject: [PATCH 5/6] Cleaned up failure with the Java 17 test --- .../hellocodenameone/tests/Java17Tests.java | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java index d4d3c52cdd..9cdbd144fb 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Java17Tests.java @@ -29,30 +29,34 @@ public boolean shouldTakeScreenshot() { @Override public boolean runTest() throws Exception { - var greeting = "Hello"; - var target = "Codename One"; - var record = new MyRecord(2, "V"); - - var message = switch (greeting.length()) { - case 5 -> greeting + " " + target; - default -> "unexpected"; - }; - - var textBlock = """ - Java 17 language features - should compile in tests. - """; - - assertEqual("Hello Codename One", message); - assertEqual("Java 17 language features\nshould compile in tests.\n", textBlock); - assertEqual(2, record.val()); - assertEqual("V", record.otherVal()); - assertTrue(record instanceof java.lang.Record); - assertTrue(record.getClass().isRecord()); - assertEqual("MyRecord[val=2, otherVal=V]", record.toString()); - assertEqual(148, record.hashCode()); - assertTrue(record.equals(new MyRecord(2, "V"))); - done(); - return true; + try { + var greeting = "Hello"; + var target = "Codename One"; + var record = new MyRecord(2, "V"); + + var message = switch (greeting.length()) { + case 5 -> greeting + " " + target; + default -> "unexpected"; + }; + + var textBlock = """ + Java 17 language features + should compile in tests. + """; + + assertEqual("Hello Codename One", message); + assertEqual("Java 17 language features\nshould compile in tests.\n", textBlock); + assertEqual(2, record.val()); + assertEqual("V", record.otherVal()); + assertEqual("MyRecord[val=2, otherVal=V]", record.toString()); + assertEqual(148, record.hashCode()); + assertTrue(record.equals(new MyRecord(2, "V"))); + return true; + } catch (Throwable t) { + fail(String.valueOf(t)); + return false; + } finally { + done(); + } } } From 33c880e116540b6165d8bb07534583c51d160659 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 14 Mar 2026 17:32:04 +0200 Subject: [PATCH 6/6] Updating about bluetooth support and xcode version --- .../official-experimental-java-17-support.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/website/content/blog/official-experimental-java-17-support.md b/docs/website/content/blog/official-experimental-java-17-support.md index 1addcf559a..5a252e1aee 100644 --- a/docs/website/content/blog/official-experimental-java-17-support.md +++ b/docs/website/content/blog/official-experimental-java-17-support.md @@ -88,6 +88,56 @@ A few other useful things landed over the past week. The [Initializr](/initializr/) now includes more advanced CSS theme editing with live preview, and there were several follow-up fixes to make theming behave more reliably. This is relevant if you are starting a fresh project anyway, because the [Initializr](/initializr/) has become a much stronger starting point than it was even a couple of weeks ago. +We also finished updating all macOS build servers to use Xcode 26. + +Another useful update is a newer version of the Codename One Bluetooth LE cn1lib, available in the [bluetoothle-codenameone repository](https://github.com/codenameone/bluetoothle-codenameone). This library now uses a native Codename One bridge implementation adapted from the original Bluetooth LE plugin lineage. + +If you are using a Maven-based Codename One project, add it with: + +```xml +
+ +``` + +If you are using a classic Codename One project, you can still integrate it as a `.cn1lib` by adding the library to your project's `lib/` directory, running `Refresh Libs`, and rebuilding. The library requires Java 8. + +Here is a small usage sample: + +```java +final Bluetooth bt = new Bluetooth(); +Form main = new Form("Bluetooth Demo"); +main.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); +main.add(new Button(new Command("enable bluetooth") { + @Override + public void actionPerformed(ActionEvent evt) { + try { + if (!bt.isEnabled()) { + bt.enable(); + } + if (!bt.hasPermission()) { + bt.requestPermission(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } +})); +main.add(new Button(new Command("initialize") { + @Override + public void actionPerformed(ActionEvent evt) { + try { + bt.initialize(true, false, "bluetoothleplugin"); + } catch (IOException ex) { + ex.printStackTrace(); + } + } +})); +``` + ## Try Java 17 on a Real Project If you have been waiting for a more modern Java syntax level in Codename One, this is the time to give it a try.com.codenameone +cn1-bluetooth-lib +1.0.0 +pom +