From c46387c2b0e8d5fb56d932aab6c45d54cc12f4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Berg=20Glasius?= Date: Tue, 2 Jan 2024 20:53:19 +0100 Subject: [PATCH 1/3] Ready for release with Grails 5.3.5 --- build.gradle | 41 +++++++------- gradle.properties | 6 +- gradle/wrapper/gradle-wrapper.jar | Bin 55741 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 53 +++++++++++------- gradlew.bat | 43 +++++++------- .../formfields/FieldsGrailsPlugin.groovy | 2 +- 7 files changed, 82 insertions(+), 66 deletions(-) mode change 100644 => 100755 gradlew.bat diff --git a/build.gradle b/build.gradle index 2dfa3a1c..eecb7a6a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,11 @@ buildscript { repositories { mavenLocal() - mavenCentral() + maven { url "https://plugins.gradle.org/m2/" } maven { url "https://repo.grails.org/grails/core" } } dependencies { - classpath "org.grails:grails-gradle-plugin:$grailsVersion" + classpath "org.grails:grails-gradle-plugin:$grailsGradlePluginVersion" classpath 'io.github.groovylang.groovydoc:groovydoc-gradle-plugin:1.0.1' classpath "io.github.gradle-nexus:publish-plugin:1.1.0" } @@ -33,26 +33,23 @@ repositories { } dependencies { - provided 'org.springframework.boot:spring-boot-starter-logging' - provided "org.springframework.boot:spring-boot-starter-actuator" - provided "org.springframework.boot:spring-boot-autoconfigure" - provided "org.springframework.boot:spring-boot-starter-tomcat" + compileOnly 'org.springframework.boot:spring-boot-starter-logging' + compileOnly "org.springframework.boot:spring-boot-starter-actuator" + compileOnly "org.springframework.boot:spring-boot-autoconfigure" + compileOnly "org.springframework.boot:spring-boot-starter-tomcat" - provided "org.grails:grails-web-boot" - provided "org.grails:grails-dependencies" - provided "javax.servlet:javax.servlet-api:$servletApiVersion" + compileOnly "org.grails:grails-web-boot" + compileOnly "org.grails:grails-dependencies" - compile "org.grails:scaffolding-core" + implementation "org.grails:scaffolding-core" - testCompile "org.grails:grails-web-testing-support" - testCompile "org.grails:grails-gorm-testing-support" + testImplementation "org.grails:grails-web-testing-support" + testImplementation "org.grails:grails-gorm-testing-support" - console "org.grails:grails-console" - - testCompile 'org.javassist:javassist:3.29.0-GA' - testCompile "org.codehaus.groovy:groovy-dateutil" - testCompile "cglib:cglib-nodep:2.2.2" - testCompile("org.jodd:jodd-wot:$joddWotVersion") { + testImplementation 'org.javassist:javassist:3.29.0-GA' + testImplementation "org.codehaus.groovy:groovy-dateutil" + testImplementation "cglib:cglib-nodep:2.2.2" + testImplementation("org.jodd:jodd-wot:$joddWotVersion") { exclude module: 'slf4j-api' exclude module: 'asm' } @@ -64,6 +61,10 @@ tasks.withType(GroovyCompile) { } } +tasks.withType(Test) { + useJUnitPlatform() +} + publishing { publications { maven(MavenPublication) { @@ -182,14 +183,14 @@ asciidoctor { outputDir = "${buildDir}/asciidoc" } -task apiDocs(type: Copy) { +tasks.register('apiDocs', Copy) { from groovydoc.outputs.files into file("${buildDir}/asciidoc/api") } asciidoctor.dependsOn(apiDocs) -task snapshotVersion { +tasks.register('snapshotVersion') { doLast { if (isReleaseVersion) { ant.propertyfile(file: "gradle.properties") { diff --git a/gradle.properties b/gradle.properties index da7f52d8..b9f7b09c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,8 @@ #Tue, 02 Jan 2024 08:12:44 +0000 version=4.0.1-SNAPSHOT -grailsVersion=4.1.3 -scaffoldingVersion=2.0.0.RC1 -cglibNodepVersion=3.2.9 +grailsVersion=5.3.5 +grailsGradlePluginVersion=5.3.1 joddWotVersion=3.3.8 -servletApiVersion=4.0.1 asciidoc=true githubSlug=gpc/fields githubBranch=master diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 457aad0d98108420a977756b7145c93c8910b076..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch delta 32671 zcmZ7dV{j%+^zMzuwr$(CZQHnGJNLx4ZQIVowkEc1O_F(^_rK3RXYW(hAG*4$`%AB0 z*RQYY)z<<(JO>V`A`cD$mo$i(golm}2Lb{E0|Ejf3gXZtBq;J7~gnOR#-z;l}qh0Lvare3l&r?ldA)QFy#Tbz1(U z!c#J5i}HC@a}-mh7t)5-Z5LjfcQov)S|%69G-)`zwHiOtXfWL$>r~YeaP|gN$CVaM zz$^3=3m?(7D%TK!mj{*+a{I9RCYnYe+Y6Qy^AvKZPi~sp|SvObH1Rld0@jASg+Aai}GfRt9cxg}b_!68k&aBt61UtmZz#mgdfJOWG1y$61?X2#x9Ll-(HS?AoYb+__h} zM{|+}V^1d2CQxs1|Dvb8y?N;7nRnWC3aM|#raQ}nW9#6RN4$`s9sxY+>XPv$KAnrB zC3B9i-4SbCFxs(<`5cPa>%qn|N#NR^e1^o2iMkHW^ukfg>ioWQIa~4kDgEMJa~x5> ztrd*gY-O3HE5#^N9bZSROQO;cZ8#okk4d%@>kIdm0jLidt`qWWHUlBwYl$d_z&i3z%HRSW?Q@7#*qhFkXRb6!6S+HyrKzllsrO*T<1WxIQaB4`4#|?9N zQT_|K4n#o5tuezmEI%1RoQfDenqx~acBjW^Fpp6#$cwqM$}?D)tmxN4#X1xZIVr*r zpHEMbkLiG^3*CZ4NQFRqY&qklECa#kg=46ddDjU-oT@4*4CaTsjW!3VL0iM{4_3z9 z9uAmdYV(@9D%T*in*ygrE8b`KSE{4a3s&1w18PDV@5@6PAJ|=%`Yy4DYn~SO!}SlE z5E|6sal~jKfFA2Z?i~aL-#z>)zs8KP-;WF?z9%1Epr>Pb7HR<6KDO&1o?P4JN$F9@ zryDNWG%Hi(=dF%Cyj*HIEuKd0u1aaw2K_Q;oKZ~LlW2CasTj+-;?qpgC`i%>dq|&J z0rHcp9V>a(UEixc`N~k}Q@h&GnG)H*eHSDe{I@!&mj{kC_DL)%VkSRYdP z7F;@BD}lSWytUY5Xwo@^yyZja{?op0(zDXOVNcfT`!L^InwqclD0#(Pnd4Qv)mz30 z5WG)ruS}|}^kHR2fYx6#_-h6GsJTgC34BZOj4M)S=4NO|3h@@x_3y}@&C-))5O~Ow zYrmEzC`9%KIh=>4N+-%|Sk}+ZkIfG{Kx{O=XhsxwhcYz7faSwf29ZTrPBDcjp^%)q zhM@R_zD*DsYTpq5SLlV_Z?Jy%4?HN>iQ_NKAfgP=kt~&5O?u9&60R#+$&5zR9q3T# zH}el>tK^5yxA^Bio^HrI$cCghrIei4*ju|jddGawCea5=`aEEMwelbHbG>-{Cr~_9 z^w(=oq565L8?)$61k!U+K_E{oc|3w-gW4Cj3kqd)pV;CaIU?$IDYghBj|;zqXQrS0 z5ep>V0A2jTtaSU_B66|$u_EOZK9EPLH|O|BF}Eylx zG%++%!c(a$6L+_~K;rqPYW2{%_~YEiAH8YY+4~OawTfKxxz79-(f8Fk{>I49Hq@ae zK_>1uzF|qi_iqur7s7xEqV6(G+elO#6`nkeDwlqYWGI2QwGCB^Z0K_ZAI1JCE#dbfB@&&lX;hmRy*uVd!pDFW`xGCXKlmCZymVuMW z^8Y8Z>(Kvc^8aY3KQt_~gr$|KhrN5s3=R>nOmjyaUlZfsjy)IrG?bbQO=|nEc1{Sn zYOU7NI)@+|8=5o~lsdkQV=nui>q9H>Z~V`Fb>q)sWhG<(i&&V|tXD@>OEdWa#- zdOvLcCL6!isWIFG>@a+mMuLNaRzrRu&#bdh7ktoo@O73M46+a9;bv-wp-T~;F#VUT z&{bkN?;U0r`)Gi^(4W);leu~pccd8l-b)C6NvlRzraEz?1`P<_+|9T@MgJnD%4Aak z?P3EV085+r9!pg-8`;x_N6CCo$;5t+r0LlBsaymjx=9d(IOH7MV#)ckLQx}N=ZQ%? z9siU`J#+?YNvKLM*BCYcmzqM`s}Hl60nR90J=oV3v8GMuC7@Ygll`I>%E|9y`+ef2 zSO>;_o)XJJW5aB5}_CIt&(w!9XyE8nc^Yji{j48A=Pjy!1$y=7GP7@Hcg>XsLp_ z+RyzjHpyz9?=XKTV7(zP<$3T?9Qf&&q)DjxeL@ zwp%uW8pLF=l7~n5!<%a77_#M-QD?2Z^pGTD>0Z6{<6TCs|wzKwM5ZD^rA6&e@+6gcDcS@K&Fk&9x@^*#*fCQ-Tp} zyzG;ag_l1J5#eizx%hg+7JCWc6v#`<3>TbwM^%`O8h31BRsEJWv<|oj{8Ke47WD4P z1u1vfmE##UBifNzwJUOG~jr|5SCc(R+AahPnYdBj~YY3PN9SOwj#)Na<;ObMo$kj_g(cqR((l$;Bpr&3a4pDKeKHvFhT_ zZqe$vosEk?#e&#~fv->k^{Ozlq0oeS`04Ne8{-zcm9827L-heD5Dn5y(lVY_B%%g zlwWLP;z~%zMflALo_WoE=e3*}A^ik?L72cWOL{L*7w0n*J!_NyBKZR)fnX*xpoj|g z2HSZu;J|bj*%@OBEk)2dzN!ieG$*??HwcAC_O6BnRRcy;ro z-R}yutaS!3Q?!R}ExuZ$f#&Ctg2Wy(F4NthpOfQQ(HZnO7 zvSil#`Zaws@RWJYI7gUurg2Mxk$kbQ27#E34}K8g^sVs8r7qHCK(J+-Vs;!mqlU{)b>z3`Hsq!3Se%$BIfX-*W8-qqqB9K5 z@j-Tm(!OaR{9cS!^6bcfI6^(z5fPFb@A+O8(Lq!>@c+J46d;pu2agV?Rh$eYwA5k>Q^H57d4CIAmkK}awq+$x0UWvq1THR#rf#gs~vx8AFj69zB zn~A6!8c%y_m)vs4oc|h6QZ7uYpAm7O+@9*iJjSORvgVSp_c`-L578&scbd(QUgLM1&Ci;6&xIe{cb*vVu88jw z8kw=^&hLEsIl=6+K;mhh?mH&Js1&GqzBlsZu=v$uvx_6 z{1Yi7_C1&8C}F9ZA=(_Fw50f1LD6)k?GbpEWK!R_F%8p{*>@GC)m|@g4vT1C?9f=d zKbBk6s4`&8=M+<`8#2nAjge^4;gFY5r_L@b)}_KL7@Ed3DteSdzB$vg57n;f-4v(x ziJJ;cb5irEE8In>c;rJy1U~wvaeJR%^{SruhH=BHg-pfKannBIhUEwn>|>=lp|Ww& z_evh8kjhSyx2b81*QsGjojGibSR?K zK>H!RJrmvVa;|I;!@a)#s=FfkuH8jjJ}O}(-HINQ-pZuopYdK=J9sM_oo$RX6;869HP&xK|&MSY$*;4tB@y=I77FgWB+LqFmoz6B6*O z5?)`zh4ZntFo%xf1MM>Xzw`XwFfC17$7dE*n-VY$%Q-jM02fJ_i)^MBQK6=7?vt`Q z)h62|3HS;`&*2F4xSU4{oKZeMHcV7!Qnw)%Zw`yzE4#xhM!$bSKJ{@IG}}f_u9`}j zSa@i2I6jw-DdVsG3gokb`IloV$W^MZ%IX=($Wu#6X-2U~{3Fp~cuW^M$x;5kwza@fZo4JV{01~^%FdBA5{Gql#@{>liyLuvSu5R*eNwgZ0-@S_0pX@vEgVP zL&H=X6N{e9H!c!!h0NE%7hQ%GHh7N)wWC?p4IM-JX!(=bV4+LH)%Vzt+T0mxWEJ#q`}FM4_1m@#nmls*O@ zM9H!Iz(<@NwweXq><%$9|BPIbiSA4QDDr`ZLPy+SF7+B#g4RKM+1Vi){whSMQYMz? z5hDuqbdaq}fGRAJC_dXEs*AgZ1r*Z2+MLpfjU#(j(4i}Pmh!e;m?}qpVpj7|IE|Kw z1XdK7SiDri0_37V(IWJPXdF>-suZ$%bd>`r5JUfs$(n@1wL^-y_z5o{HeUo{%$^@r z2Id$oN4rz{$f!Ho3=4g71GjuY%=fIIR!p;CNSfpJEO886pz;BQc=cS@SFS|#FUzx4*qH&!Ol7M%Z)lo2R5AxxEhgts?J&C#u38@F4?eV}dX+=5U#M*V@IGy`c5 z{PXU(#wo;@&0=nIA30VcQ0}a8eI@occ{*|Un{?~<0#B}$#_jQZL=zcG) zf~WQDE_V;{6~GD}NtaGp;#?-2rj7$wC>6%H9KCxc(#dZgD#g6487DkLy!fsfh(Lsu zgCt_Xu)qYWcQpP{A~U1AE*Ul}$`i+YKb{*pZ`{MwQA`Kk18zmN9iQ)iulWueS8$v# z3=9GPo8txe!Voqb$S~}#gdt-Gen7q$>~DKBIP>8pK)hbGJ1SEl5J=zG3M|{9qnKDL z*p06aXIts|0k z4!KylLe=(L}O%wXei3V@^1gLD1 zQeGty^uZa?PUMyOIMVj&#rRQ-khOchDuM*UiM3Pi;ZI#Abi|kf6-B!ztK{}u?)^Ot zybq>SM*X}vaB~HDpO`-az#?4J8R*2q17WpvG_TEK`?!B_y5MOyp1_^lJn1%Ap=^iB zCEafGBamP>EYY=Aq||8dMOAz^y@ttxx9(jNI1e(jFz!sv*pL=y?=Gb72)-QL-t*u; z#J;NY_!aQ5l*$Q!3Nqtq6dfC6)tiY%=%~srAXBC z9IpMACuxBTHE(Iq^}^6~^WV+(r+qToPT#RXnu{Ep&$dPZ{x+1%b@4yonCiQPjc8Ne zJp8L~*hCmT4?};&k0mFXgxPZEa*Mi*_veOJ3v=TOF{LNYv3S)zHdBF)ns_5s>>mSa z+5!Y)g&Ri}{$9kx-^a1L@%NwJ2WXbSLX&IE2J>^jQT!}_p=*H$wXh24-h_pS#XyJ> zQ95$${1e1D;P#4uq`z#mj|N+Mp#y1}W6}76-2}_h(X;ADUWWml*#>&GoP{zCeHtAT z4_$sJ+=HdJ_%KEq2ZvGlQuyigG2p(7NnLlCqbmYGr(C8>fevTN`lW$N(jPlwO`p_@ zUUQL4Phsnzm#{V8Q!c07isH!BJ1NLmt0%#&+TxvN^FL2lIiqN0NYgj1eV083QUQRx+t;P>OEK_f-9Y0e_X;@udICoQAbbSw$ ztT}Y^(f&;3sC(=Ja1SkT&Mn`$m0gstHE;U*N;+tc+T3_5f5b;W5~lUD6=av^M08|i z2q=$X>SdI_6qHjkFGAgoajYsYmzy$|avRkEYjo5CjrLSkwP_2|JknySY8j!iS2cSTK)E^pt}IcW*aCb^Pi+;_e4S*Mt&BH3)B-GRH<$eL zd~=rFui)JqHBsRzY`5+)9NunamX(_34R^JkmsU>I%g@{G>Q+a?Xv88LJ~t`7`=fV( z#T}NLeWOC}9}Y&O1z8gBvYU~wBm#jjwB9%dJ2LA+6js8VsxY?vzc_FuI3ig5Fjn|& zaX5k8gcU41lH~4S9ze7huT?j_b(8>w@j8UHXT+w8G(L_TnQv}eNZjDC5n0A%|9 z1c<<#u?xcJ;cSS85_Gv4wYzN$1=nnSa9T*50+1JSYWzP94B1sp7Q*nb)qTk z`8%wM6z_9>*?jR?&@093vw)@vlc;c*5#{S6;-~E=uEjdPJ9_|9GDec-hQGx8r1Ue} zht3fH#nZE!1|0snuKw88Ipn90cU5%GIrcerQeLr4zbq!ZidNMEAk35g88QsmU%3zi z7a&{2Oz*CyGs7e@hp@^#F`9gJ+Kr3TfN=u;K5LQynR2YqB&JUeXP67xzJg)y{PPQP z7J!7#;_ZU%oSpXuCOV<2ZhY^!0l9C*F7*KIst4Z7VA#{WN0l+)V`xE(wu>)-M58GsN=C z4{K}%)7s&a=EG%iyFE+pU;E};z{1W zc|wT?*pEt}yu($L=%#?CxUcvkt)4}x@r5d%syOB}anLGQ$i*z7zi)$~(}T>jje>dM) zK$8OnGiWhKgiEPx?_?GFMV1CoMjAB{P;6jCpL2Mp@*C%CTw6voTmDGTok_2w{wuk|8XA+jwaB3Qu$gY zw&Vrk3v%-Sr2#*N1aP6##^BNE6yD*_RWmHZ_y|DIgn)xETpX6)E%dzolnR2c8*cqi zJHPkdZa$U&eSZ^!5DYfB3q)J%6&~rf%>cZWq%n+FoRy>r<|~IYR&IO4f$+25Mh=RS zx$5$wjFN3Kh}b!-4ejuJd?ptwp0dMc?DmARqxllD(nRsyE*_~5Csy8a#$~vHV9G_+ z<`@|s3iyQdPv2m5;`QneisJ>Wz?*6en}@n&_nIFGNs(hn&s4*$IsX1sIQR8+k@bC&*S#z(s1@aHA zhM+QEpyx!ZgJsiiKf{$2w+xFx?WyCm<`YKt5!G&so-oOv9nuo~Rv{2eT1_q_f6A^{sKb-#J!`z(y@*>tB z78@Or)I&B?*}iwyT){CYC?odrMJ^bfPWVJp>?c}%Vts3Wfw%E9LX`G}$l8bmBc$Mc zJQOCi$g>0R@9hKNIX!n0tIaV>?Du5#xZbdPsOR4jhN3es8)1^*dzx^c_sv1t_timn zRVRPF(+0ZCpA=SA>-w}Pf`-K%SDDHQ|0rV%*nYAZa%nCtk3!)rV;eHpk!1JFY}tZ0 zgoOv1n4nca0*QL@86e4alY@v4rJ(|23vj>T}oe_Yr$(6El&l6*l`awSxYoK9MLQ z!@)Oa8nH1JOu{NOv>EW(|FqsgA{r?$d*7;6ChO$EqWbeq06j8mUlra?ZHtpmQgshc<`mvM zL-^|#4lts!X4bCi&^#Ke9*%lQW%mc#1v^Di`{FXNq9Pj?^Wm*3G#`8vbkaP`YvPcQ z=QIs^E~l5vf0H=>^Nd$5P?~Ea@Z>D+8DClQcj8sJ-0gAwQ{nzMyjDKtBTCbrcKSMP zrn0%8ADHr6+p{<~;q|n%p^uwGd2^|D0x`TioxY!ewJH9h6K<10N?!`@8^#K$yHJSy z7gcGZC^v+!cQi?m5~oP#od$d8Jk8x_;)!o&h3+Fq6?VWpVr0G{G09TPUvQ2-Y#3pz zNXafwmz@MGf&nCfnFK5n!svtLcZK&dluXn?B_M9J^S}QdQB2uUU%0HTV24qd64uCT zDEgxQuTUJ0Id(P|3eHu7ok45reZ8}i>Wg@u2IJ`nYLVM- zoEBcYSY4SDqqtMwf_LPuvjWvo2J+P>2{;<9rLUh`Jl(;^_D*Nu-1jC->Cvl+Q={q+ zct9dJ9PNME(R(BH8nN)29ELqnJ9o8#l^W0W+{f)L>mBO$PMm}{YRk_0ZNfG`1i2FqgZ1U4MM^ny zK>~zjxk;$tcG%p&+B_FZ=#z4h)UR8LYUJE6dk1PgW{(#vkPDFb8at_ESegWh9 z^GK?r-{HgWDTK`;XdP>1<5eChXw?^fLXPl?zjj)p^K2Fip~ox4io;981&B)qe!CRU zFR`>wF)g*9fQJwbd-o;aSS-*~ypL;Z_f^D0c!@kIUhhSLm+jSYstXhrASRoJ3cL(> zyfD>F7o4%9Js(Tk+;oLWL+pyWf3XWDmsr@BZIp``qBRwUee8ik`Er2pyL{diM;o6M?x|Q#gS?o2E9%PQcKXpi>%%4T`8uEn(TakE%P%rx z&#w|2Z8^TT=iRQ`=cS{g=mMM9yax(E&c7W&2KK6;&DD6L8eNaee|c90`)RDmjp-RA z#xSctxb}(-(reDRPd2DM0JiB(?^1^Cj_>s_-;O-sjPAzqzr0J}g1BkDTcX3fW1=Cw zYl1tfbQnNy$HC!5Mfzrw%k%?8TVM2#em)w>^Nn!a3)ABJlz#a=#qcVnhJF)Ugm zkEwylla+HWaF|?tyOdnSmLVhJYN$Q+|sjzP{laet&N7)p67^QZjNq6LMzjQJitT~A60G#Ns zf*Z_nJ2&?zm23biWpo)w9sS;-GlMBEv>vzOM-aX7c2sfJh-;jqGt#A`<${ic>t!{` zM)xE;420yg%wyT)?AuF7~`@rw)ekpDMw2 zBEc?oVpmtAL2d}GFzR?fRJA@;+l3UVhTlvpCg=8ZzOHM(mQ0WWIC3P{lLDLL!t{qsJ~KgS&+d)cyB zh?cQr|KAzI#1NN)nH|kqW%f#?zcZcdMS&v)5UpPV#X#BHIn1LIrJ*0)L1T_DOyKgp z7w$H2KRXz)9l?-HwH>0K!c;ASm3+@q z=H?N7Uh!7CR;0es8Jb{3Gu8lzrRlxLtNSQwz$%)!)#NK=dsglhCc%cYV5FTnM*hGN za@VP>0jWvXJ{DZ26+(;cWyhaWQLBw}tBq9?BX-7>a8&it)g{|7v`Zpwvnw(Gb9Zgc z_?BupxP|t!GlT4+GpnL>&A8arATU{_(cLIr@@J66AR7E=%4R+oqK?=e4N6YBDK? zkKXnq4y?S`87rM?<`&E}UVM)hbr1GgK3lnm17dmN5}uR1+64#jfq&WE@1_}(N)7+zu>D7CbVi;c2r{&Xu`aTmi~>{^VE zKM~ZJhRidclMZFtfUzp2IuFH)MXqTM;FV;~ML!5uJZ*uQOkHL`eO0FQu+Bw4Q^Xa~ zhmp&jr2l1VVSEFc)9cHc&POE1q#>mnwql33G#3We6AXN&1D|AHq`DEm^%;MS*8{3}Xu%9jWg;AcULO81g}93PN5ms^1^46H>f#wX{oYe6AP= z{bM4-EO^q5sX{Z!>8D9!gldYTzZ@#Xq0<-qZC4bq;jq$vJ$bCK0_D<}`)dcY9nE!s7#~D&UmsG^CZF2PN-Ix#PLWjk~SRK$v zNJAk?^RAI{NL$#it{H-SRUGQ1`>xV_3v?vQ*6qa>$Nq}g^*JqS)kzCp5x=TS1~^%9 zZ|pG8vKGw$%z1t9{_TJJt2p<4RulnRJ0yH+3eMn|yxp6KpOb^Z9S@AfDy1YyLLG_> zEn%}L^>K}r?ufW2yEUGnXFfPB+a@?WMZ$&cRgmH~nw!mga>h0qEa|cAMQboi)}Ho8 zyoEhzQ~2ygF`94QB{_=oMsMitB6vGwRCw3Q@2?|J?d>CA?G4FUM7cdW@tX3+-eojT ztdrRn-0syL`YYxQgaH!69E6bi%SfRM&EON8+EP>aYmD)2GcUsmQymX_=NuHr1Da9@N|H5MK<`bxx|YlWWSLr!H;l~?3)?O^Go=4^bLY%Vgk{NXCkRG5u= z_Uu4kwp74sn-TXFaHvtBR@pM$c#zrFG7rbXZCbTZYatIX+WU{s<-QKgI0eNotJ|)8 zmtceBl(UMmA7qeGGhNJJ_{4nyqjEtfWv#6_w9=diY7i!;qwzMWUA*f#Y1?uoGnKm zAU&nlrd<55ED%kys<^zJr!)FCsSU|?(G<48-BP*1fWn4%?u$q|4v6GcEt?@ZR6&uOd=H|_Z|N&H zWE(W?Hs?;>?RMXYWx4a@``lT@XPdL)kSCjH4Pq(H;joo`A)z-JRa+k_=$eq$UXp!Q zYG9w887tDV6N{%6UAR)kE3Tz5RDmfF$qKpUk`k=AUTXr+JwF@=*KU>*tkp`p9)vJC zrCnx10PKNC#}Ied*W)(Jt}_Aq58gvtF#b^4=?q$X_k$N`-m_3)`p4(YKW$>)OF|kC zWnlV48mHZJLK=_rLhcQ4QwkLC>w9YqcCgYO@Wb`T+`&R*Cs*IlybA@2NS%O)bl2z} zAX9$H3?^h1~Nz7UxeclMSl0q_J!?ORq6CtkV%3R|Y59{mpDO%io=W8CO;9 z1PcakRgRD^sn%y=&2CpFQ*2A|?eZSszzJ3WM|IhGv7W59rvIF_$^q-CN>&RXN7rH4 z9stk#!NI$$Iit^)Ugi^Ho)vMOZ%!*+QnOvL;rRpE6r*&y@WH{EWc>nI>-Cw-h9_;R z3`G)COSXnO@NuCnRrwXd%0z=PV@bK9yr+$}r`gx8)ZwCd0AAGo?vTpAmTm0;w7J0X z(s}5FUjuWtX3pFgAndPXi4HX#tx(jTE_cZ8vAyAquXRe=unh_W36NBbxGCnhpH}^li6%qg+o;c$cU3;f2K4865lf7xyN*7~CV7fl~=q zOUSVF^ir~)yqCU09{+$+V>v?!enJR>p6;C%LghE*)^fobF^pD*_!cX2`> z|GodAW5puK6p1R((C`~a!f+U8>Jo2!zXtyA_`!I13Ac*%v>C%Gt+y7y>JSOu}%rYFS6@8xuD)D$uro zy7f^=<_G=$FW2(6aFC@Ta9WyGpayojLc!)C;#*hM3{2ZfmcD^3cnqPQsu-cVZ`Mx7 ze;o)l|3G^ER%*DF(c%q8ueZH(2G1QOFeHyCyBXX+PnZ>I1-6-fvGANjis#dSv3hXVyZnn#4OtDn;|#56J1;{18^D22(K`;iM;`ik6ne)GyF(4TR{^_|fYwh; zJ|iew8Hjgd2J46gymEinf|s6ZU7@R?!C{fZK-tfusm>?u51KW>BvDZ3Q-oa6v1<$?8=^U&paxqcwB zGsD$R_<;!fZ68GUdU&}Qjd?&c*_8;f{usm-9HaQ{)sb8oSlPOS@B#KJ3nP0T2nBz- zC*#R0Hnb1PkdBgu;s;e1LCNtA5B(Ddqbf?yB@D}_(Ui8tbX!J9=7<;U$`kTZA!pTa zh-#tCLK(hEnqf3<+CdzCa7$@YoFbLh+77yF%w6{?@*N{3M8f3 z04HT`1_mhW=;m%}Z!d0RX>Rwwst?mr4VBSFF~aw3I}E#b#25ofiv8vF>Ga{UQATlO zW2q#WSrqRbb;6q)zb71Pe#o6V7bJ=oN938mRL9s`4e_gn_^> zFqI*4aF=6C9VHc-iF=W9dcJ5^1I|Pd*>OBzi0aFGKfMtbAlS6ke`L>X+E#~|KwF?3 z9(vnlD}NoUf4-)b487Lbrw?$`Do|lmrs>-mJ?O9broZjcffN={yVQN(ZgBf|$a;Oo zNaJh@u>k-{Rf|mI#1&>8mKtV+x4PJl_gBNB_IhRX%F{vF+vy{}#(>lQR;%-DLZBB| z`DaeyDsZcv)^vR$|IRt{@_|}u$5zlX^DYFzrdrHOKLSaUpgj2C9rJ(}I)2?c(#q`) zM~$D+k=W_UYCUyOlN!EeluVq|&$81(QV6T(S2Bb>0_eFgo^>H7>?Q%8`ws-={H9uf z2b<;6x^1hV{zUT*csm&>mE&Q0l&S%c-IUC7NMvaQ3 zrn^R1gNiQP+9AYI7K@Y+7$2@*EFSSFolz4|;zLZ9s(9m-UowFo7<&EA(dQXJBr0*r zf%2jzT#9uDsf}E*IS4j|L!?40w7C@pF8m^5!H>onH@uTRjUW2fy@fXAg<@)V(#<1} zrg1wuh`kaZLeepNI?*79CZf02L`>4nx~%d1kzg*46=lSTTjp;PD)w(=`$lij81RwB zny5Q3&tCeU_zBCyR5WG}t{!dr876e-zqy*M5v`j6pBAX0T$lfUL3`{8=3IgS0>Z?Y z!op1jtkZ?{*H~Q@rf%sq^=60)WwA$+A)z;w1~W@Rp}+y7WI$ZGlz&W)ok82{Wra0t zt;1}qtJ4GghslQL!F81sHgubtdiogv2fMnS9qgWf&A`Ai#eZl0S(6k#Fz$<~1Wjy!|ZOeQnuTC-x#9dNIDt9Kxu3EyFJZmLMbCo%|AUZ4% zz$qn}Pi)Fsnt^+?7gEo5PCUEcCL!E!Db1>IaY-z{_b)OobdmifAL5RkVQWBCdSQTC z+S8@xi#4U6*2}o$?UFb|0QZPygjdREL{%_drb{|I_23>mk91ELS7Yhzk+?f;SNfC- z1ZhY+>k^=TuNp{y#I9EC3+N2QgMY!j8<*aNmvkCkqL$y8{ic}s3CiJopW1AfQ2X;zkr<^;&jWD`67 zlRWJ_v~(MwN|R4&sFl)3yCfb_nmwIP0)2CSWj+mKckpVlIi)_oQ36GdraAfrWYQOm zhKN&PiP0>D2}-#~5Dk}l2eoa!P~#dbmhcZ{w<4vkx@X!2FWDXSJXFcEI8qdVMIJ9# zcJ17bJXy-e-VvSP$3(><%xS z#Z=t=0EHi$;+EysOe+qm+PP|_X`6&>O~LfV9S1$lH~ZFqy-o&E zhGsWnPYo2=m|n?{PyJRXV;2W7lVj)FA*MVzNq+VvsZw`&&59}|C$_W5qQ><|1vmt2 za%PMkC4Jqda7XI{-?yumk`iOs9Wtq(VZ)t8#VK`YrB1g7(Az>lh=xXUkaCYOl|1I( zxoGPlyWdv$lSXJP{%Mk9iPzr6l-!!7)dgr=_8BT7OqxrzTu+kl_oxCMJC#|LWJ9QK*pcB5jEZ1-Lw}DcGby3V3jwPSaXREW8e^plYGg+$~ z)hz7rbm!P^$kqus%`nu~PEK8sX=zoO=6STe*UNPH52p`AZ7?n;;~67%m^3nChWWTb zIh!3}^acK(VREuky(0BOlgB(cLgs_?AaY@x$vSjg&?Q!jb zIkApm>~Rf??OTM*B_1-|)BX=W5$6y%AE)yMV zs;yd?jw5p?zmeef6v7{z1T9XfRNP>sE5s-q1a1$DfoBivU@8H?VRn*PgJIpnc*Lqg zL<1T?s8M79#o zb6matQb-hDh8lQ7BwM{mu2agpgv))M8a9fZ7y1Xs4unQ_x&99F)t+@9YdKmnSvCq} zWZ%Y7g+sZKhjHuf*OdJp0PjYLO>y%kDm8DkM$b23C>poD4yIR?Liql|QIc$)^%RSq z)V7NYGm0;_F=`3d9M)ol02L{xMYV_T3dmVP8c$iL!3~VfmuDLURWl^RVn2)&CX|4s zC-lHGhEI`7<|lWtH(yv)SL=ocP$nyegE$%V;Br##=rO(iKb3t2P#s+oF7EE`?h*(Z z+}+(h!QH~egWJV5xVr{-cb5P`f&?dckax-M{{Nrds`u&*MV+~Qy1yeMXS%1S8$35z zzVrlIb(?W|5!#Ijv-_P{vtIT&*eSsi0gK|mt>ov6v?{0MBk&yj=E}|r%)|ipMg*M- zLrJR29>^6U@}2~fI^JRemP>AE~bIUkiERb$icyUp4Hw;ELeo*LOG)dqdQ zspOF}fsm2n44a?cDJa!-vYI*{SEYxMi!W?6BXP5uT2qN_HSssX!{wnnm4{uuqezwx zJ+6GRNIt{5d8Zl99Qs_J~HFSlOwXps^$#zt808Z0Va6x^@*29${ zVH4&Siuq;DN~9g{Dz0H>;gMb#gPC^AL?v#%-hSIXNmi_5*CeDb>>M;L7Db0Uh8$II zZ8L4aq}07(^x2$OT-QLu#V$eQ5t3Z0r5kG7IK=+1`&7l8;x#uvf71E;H8y&WlejwC z*&=5jo|!FfuCFHDfG;O55jz7HIR0x%G?rH(ZBcR)j^#FkeNE^`$rNN?F>Bj!aE2BR zyCXtQSo$Q*LUCIbpAmc4H6buhI@zucfQ>|9nnmQTY=S63p@k3#QVGgy7)L$dPdKP; zwb7WfU;|hh^}W4Y`egf!N!Mhl2QU@co0PE-_iF zAXNPG=QbX5ygr`PK=B14U)M0J&12nU^?5npO9CKd3qVr*ZvKAhkuVD|)+HwMLl^z< z%Idje5`ZT49RHG7d!AaSu)jog{iO3%z@-6`JGNxM=zG!DSMu5yqkdw*!XfFEQr63$ z5d?x#=3^R8rYUi79WN(SPgH`_$RX(<=R2*cQE#BlpdOLGi&KK0;-2H`q6M{g=FVn+ z`~uSZD**cid#?&_Bg-6Cv#NR)Siv!ijdfgcM$cD!31tY6^c(3D+LXkYPMkFE2)g|| z6WQ>i1<^ZmcdOQO*Q$Vx#Vpw#y4nRVy7G;4XgM_3m7^ra4fkB`+4sd2nLf~mZ)a&6 z2$t%Td!gO6{Lx|Gww0mu!na83U-5j0z#>A0P6EJOa9g)5e}DLVMYY6l__@O1ys@oG z-9Acn{UnsR5tol>_n;8Y;*s~a5q6xhhZW}I5181wzM4$@Bi7&(ckbv`B+OeN+ID;eN+dBOicA912)X-bSl+JEjHQRJ!|5r1QmfpL~! zv^yBNa%BGKK#>9@_JCA=MPSj{KA8+|S zd@Lk9BD6qTw|~NI{`@3v(M;TSo_HBa-FDCXGA|Q~>k`K;{nz*43Xj2a<%eWBZ%B;B z=pPIpCTz&P#zRsIh85D*`ZCjz+tOu`FlWQ*ehND4IeKf2WoZ;Cs}?V-A=8~vqev;fPqy%@Hj9K;(W=Pj|^qoJ2#&hifF3TCTme7}cXaj4iVb>yMT&!7>FydoHufSWQ zQdx$yNIu3P>`sE8K|6!zW^2v9yKaxD8vKbgZ74=6@r&Kp00qTRU_S|Yx}{D8p$RK$ zs#pp}T1XUgbTl?jQ`0H<(IQF8dfTM2cW6eDR%jIz2cd}G6~{bPs=gG@;pd?)AS#hp z;1KglhT@urqt+X<^m0C9_`ArNo2#I=czQk-74lT$_op{DnCT-57Pr&l?Ymk&VjF%8 zB{w5GQSXHwXoDYv-V_!x?V{c@j55NO)Z`Ssla}y^NvOx-OAc^>Hm@weZ$L|6Rdu#^ z)EpcAl5GE3NrZy5GVkfi#u0v&1JH_ob8T#o|1s1))ROb_zSW`2=X5#FL!9>eL(TCU z(>=S?pq-X{)XnHlFv%NSd4Abkg`hVbg}(llZ@e@;f6~r1Lo9TPI&4(Fspr@|Do$f; z582QQebz<4^g_tptq2n9DmyM-ft9Jg8Iv1+5tb0j(m5e6a2x}hD82F0yaOOUk)PGB z*{Hlj4rvd0E+plVbt!L*TU4_0a>&0R-`tgWdgEXt(FS7Wa!f3A;eFE3E1!AtS=Jt6%4+y_)5B&IWnBhu$2d|wH&kfb&7k%?y zyS7CN6Xj&GP{u{>a6by^tW1r>&n9;>==E7D+%6_3Q! zsWw8^nX0ri?FZ#;1Rs4%{;diItvYuBPY@Mn9mz*O7_C+Fu$*dqwL+{XabZnmnHyD~ zCC}&Z-SV-3wjWx);R6m}(9J>zzm3$)v`uE0SJ5OoMnOpNr*Q|mr56|k|w^42RFU8Dw$SHQoU$`r?LznERM)Z2CY`%yoLEptx_3UBlUsn4q#p2vWft@JAd zsjqE!<^0eUfIj+m9q4^WRAsa&AiU4(37e8jo8C;F@aIjoALXA>dOfT>-YOn<+Jx^=a{5)^M!pww(g3O;MjatYNJhQK84RTBPeS z25LyHmUU1AA1snD9nV$r9^Kr@t zJQI>RU_BXf;-Jf#ik~*P=LXuF-9X%ROV0GYcYsr(?pa=w-+KopR<>WIlxLB4iz{18AmJb+DIF_7mxaHOF-!#8Kk3nieyL_jz4IV9D<=q*IV1-)Ifo%GErMY2RV@ zFP5JVu*O>gLMl(ObwvTglE&;Mh6Aa%5R+r|LS+Gmpx?qLs9!%MS+-t>f5Wyjrg_v9*#mJI@H8eryr1%T&xA^*C;n3WqpFW#@G?fu&UMO(Vb>&=78dZW za>pk3h+U4UGAYT-0ToGW(XX)|iCT1-JRpb|{mp~YDn0=bvxStqRThzP;9M416@{ky zyOF8W3b#+)Ceb5a$4+U-LX9Ja!eeRrS&G_z8slH4+%Xb_%9A!{5PTocI}l0Fa_4q2 zzd6}(Is~{;F~b;JW||3nGT`McE)!NsRs+k-tfK+r=eJPs4`P_x*Yy=J-r1WJxV6|= z&B_^bqOIx0^vug(U(ULPj}q|`<~j&Cx@8>qadcjmI}N>K!$&=}qK9P*LQ$z89%jI48fJS~mW#q###={vSC_?S)sTUJ02zMZAP zc$>DdwHRau;x>}vIT>>-&5-rn-^vc^O&BaxArdg=S*K@zBtj)DbSt=gslxM0@%B-wWbYz-D8a^OV_M=&7w!Y~# z^AErBw4~^m&RZVxbWjjBTvENK)ke+fo1-Zm7p`8LCLCtFU|e}t?0gAfur--orjJTk zD&|Y2G)ViGgU4Ut{TlXNJ>P=DP{pHwfzg0mJ1BvU%5Z=~0O10*pqU(jmw;j3GCa4t za;C7kOjA0TLPl!hGDmoKoq@wV=!3FZ@oe#Q_IfIJ8#$Xf(c9!In?q5p)<~QJtmcBT z=4S=wt_^<9WLt9FyU(|$KCNe8T~a*upMIUZ-Gp#Ncu8;+TI6R#Q0Udr2TC(sDDR2j znw;Ojux10$O=RP0Ci!vX-0@JRmZ-y8de$-oY{Tzbf`dFYduh*Q;B!nvR0MDbi>U=- zr=*9s>Y3KDE_kzrSWVzq&(HJ1Si|m2@r)82v5Zi57%Lt%NI-5wMplP}6ov>p8BBhR zQ4XIjh~YN}Zd3a!**ZxFQSZ)~(OD6B*vxj5RCNLMwrXR%O_m1`yHQo;d+IW^ObZx} zcfGy$SL0W!m)lpps{>z-uU5BbMJ;av{fEajzPBbY(0j8tN`rY{RQOlXi#6LyG;1X$ zRZVdcQCAD{Hf62uWzQE!~sIA15G*vMeB(k!27Grg>vfKLu|c8(jqlCyx)U zdXxs3adlgy-&c2$8A(!an%dREQrl_l!mnB4G8N=7OC3nGVsPtI=jK-Dji+8-(B zut_&R5VozhXxvgI`Fz*agJ>mc}_3<5w_CzFrf)RZp?BsdE9|2q-SpEIzB5gpi3KWdLVP# zSTlR+sHM}$R9I5fnD>4KB2WomRdMoATE0p@N^=W-#dZL_yB;N;=U$ z;qfs&r3djdu`ej`Z637;fu6F140{#=5lrhR>%$y=fm-IfDxdW>#!Y8uWT6)lFVq(A z@%V@ViV8HQFh@&HrKrcn{t+hHVbtG5gVO-*W7+DdE1$@Kf$5HGf3M z#;@K|;&_f)g1-84JQ|#qJ|BUwhkAo%G9hi#~J08xIwz9nwLA^-7 z(j3p&+fQT3*ALaKi*?^Jl2!l|Q$?<2Wwp1iPfw~lmX?J4?}$G9m|*tT zY;O}k8Q(cz6+pRd5a?=~+gzuR1=vJ5k0^(=TGMOITc}|=7FXLZbAi^h~8%QS0@RpB)ENd)G}_Fw%+v*^lY) zxiVl@p~0Unmq-FuQolefy9p56k1=P8VI%)5)d5Hyzn5_#j(x%fRd->I+_Q40ofkTt z*3I3o@yw_%HpE@9K)`21tu=#vSY$#bEQT$}b(IZ&dO)(APq>nQBs(&gJI#$!g#raD z%5|mjV*`mo+zjba`7{@dR5CCxuAgR5e~QIB^C)}0U|F9mmhYVD$petC?EQ^QOqD;2 zd9L}#8wtyZE7E*8D0rzsBpLGAg0oao#7Ir>bfx}q<6m}HT23XQef7vj0uN>+PqJzV zN(nP+EFCSdYQ-MCg%m9qm=@k1CX0njgGLNF-Q}(^N-w(ZB<$W*_=3%EU`E^Q?P8VRE>c zpD+2^WWTbo@+6pMR?Mha%?7S5+b8{qV%v^|B%R?z6{+~`9%0UPj0%DJz8}NkRN8KH zb5qde)iF-QG`?y%ogunJv}6lPhG&p7>bS;lqP9^VWmoDQ^-%+s(xnMfE}(QSPo7~7 zt^{^q!yInC_(oELu!=EPsEYSCT>8ufbYxIqv#Tf;Yy83lV7&0Dge*XhqM*32&XjsV zZZJXGeRJlNVSaS>>?5unsd8;&g^;}fbo6*LzN_Vn!*+2tq83*QjA{xe#I3s{>vAdz z8IOgrW=als!W~1d0)|Y%t_lj$&(6*tj&>&-dEAP_?=a0lMw1Jy!uQBS;hH;s#Yg>f z%17Z8EbC8KFWAk}k)G4cY7gDB(cWOhFOYxwEd&E5j(R~6AALce$*}$QpzP2h2fRjj zG)Sl+XJVd)K%T{kWZvSej8IptNrnl(q!yvO&Sx>nUUpXk9K zN_$8^;V>K0dMQ|~eLkf@rYdMe1*|^bB=Q(TVsOQw16me4dP; z8*f9I4c}jhzzm$uQ%!LrVKp%jU<1vGUnF-x65v*G|~&<7pIuTF^o>L%L{&l^%#4uy^O|OV0Pv&fd>GhJp{hO>}2I-CYc^ zm3#EmwjwJ?W%C}!fvS6p3vuZ!xis!Nl#F@L*-UH%jn3VPPYVlVwKd5y}~Nu9g_Z3+=#UE7&>Biuo@~{gG9*5$y@x5LcXRZ zIYEePKW4vnI_- z8539BN^||<$I}jCse=5dxyo@f!%w4_hCIu|LCNeZUt9927xB0DRN|Ct%KS&&0$N+* z-*C}daRROm^@VDuzO37=l+9f)G1FM|JF5eC(EtpuN{jQM0uD(Tn-%;g zni<16;?Z&@L6PVruFJZMm8PuYgst<=2KAkIyE26%OI3*FMm*M!+|3EhgU!LF8t@lX z78h2}pAJ+lNZhr*`C{L3Qg{D^yEH=n_9fRZ!VK4n%X(jFSS#=niP#z90UHbDKh=L&$;~d-qsU(XJ=d6`HV7u2ckt< z+7ss!q}Tif?s6#Kc9QJ04}dU>vrRe0V@CKYcdyUu4Tppp$pqxBAvve&pv{CRX^sUdx3z`CgBq@8NRu7&A^t+HT1n6o( z|CfZohnP<#YK|AA@&d+1OY6x-rlC$+ZIA!*cB#+SfB5G|fl`kpsMw&p9TCX!h3@wq zk7)`UxN3wBKtdn%kF!OBf@3ULPTgJTGpgp05!DKSrZ z5$p|y9Xo_To?_wNwfkKs{4=;M&@)xxH>8cq&p1kSfYeVe92@z@AFXv2uJ7H!nb)#yFT~3bq~iqBN8e0 z+3pKE%@>Rxr?Rt{h{f7OH^=Jf^5r+@)HM*Te-Jtz&k8IywrMi4JXBpzwTaK%@gCi9 z(0-G?m>66+<9xnG?w+-%2Hh%Y9H7Xlkj{L}UgvFO;;h~b)2On@_0_DXVVrz|!}E-! zZXHmie@TD11wq4rl$F!aI3I4vyuVStVrGu)QyqMbt^3es-$xFQVNiZcC9!mvnC&8n zRzr*A>^qi~*7C;ckauGCme5PKVg@1zNhdT|Vg@@GAa#k^hqlyu3i z+Pi=-D-qgIRN&#oJiG!CH4fx4Eakw$B2-|{(j-5avS^adeOUb6o2yB#-gX_s3Bh`i zM@Mg;GANOaNE%+D8I#Urqy+Vud9+sx8zHXGeL9mWV?X}aS7T+nipY(MuP{O%HJSmY z#Sd>JSGHWv6ys+fQZCk?Up=*#uL46Jw#-qp7>EiBsm%we4!F3YmmtJN?nWO+;4oKur zia6W$VLBn;%Hgjd!LIH*(je;opg|V$p)g}J)?4%|>Ben3fAR@wZsQ9-*>wN$ z#78`$n_BH_j3YH>#Isjc?itGSvm_$FGw;Wx-*^&QX8zlse8qqJ6_t2?RXK)|+&FzXIafI+?Hyy7fZVG@I z^sT}sNUKF9K?6yomXFrX)Cox{o2Za`a77$6YxAB*RzO>M^e*Oiw&JT_50`orxbNH~ zyR4G0M%Q{Cfcj`2f*I>u$rZQf*7qRR|f@T~qZ&lD|-<{GdkexRbD~j3@uW}h#V`CwkH>0P#ZR=?>S?^ z6w_!Z$%E4s)yiYzLZURNS8XhFjGZkhK*Y`e=>wEmq)XExuO98>qLHv!j$ z_`gjstWbcPEw@e{Iek0BB3w+_|FNQE1acdR0&45L2;a67g&Jq$O(gTDeaG7xZTqZ& zrqS^q)Bx-UWhsP2jt`CHff+h(BxwkMgBFe)8VbESXOy*1BapXOgL+mqL4N$K!iPIw z6rdWC%@=ULSLMy`s#g{&Gm*)dBxoqPP);c9>Mv4Kxau8R@>KJ%MKwU>OM8i_Pmyom z4NJR(U8U!>nqB3y!ZxEcPmeE#WU7DF&>0#MAOXOAA&FxH@9Wq+-9Wv38Xeoo9_!iu zzQeQiz#s+#&3jWBbG6?PE{J75>d@u z9*fT05R26Asbyg!V4X>vx&K_1+#~QBw#=lw_ZQ~@qr!{wxy38D7tqS=z)tL2%by@te%Ai%FSW}1~iWp zF;&o>73l?Qn3a{$&cd*BnH!+dYy3r%BC!xeWX?C6430!ssMjQXp>uk*y2XQJO&^Od z(xRLu?E_5pn(#1QGRMX<9XfgWIeq+|pD(ey$uz0nP|iw=sf!{~V3^`vnZn)jgz}yu zPLx2!u*bj>?O?R0CX#KD4^hSd=wV{-WtpM!kk)0^hb5dtGbb41MNYUocV~$Qg3QO@g%T(C3JZ2@NVK&=~=p`vwaS;%7qIsN=)O&LQJRNCfV+dMm zB=BOus><{_lcFXCjU^roq{^FBcL`Zwjo}r|+ubE+D%KBQhRbY9bXg2p4qYq_LluaA zm{ZcSR3XK7X4|0C&=Q*RU0c>et{baA5*dPvr7$e1~2vWp05jiWkGH z%I-bLJWEc>S6=Gd67eB$nS#15KPZIZoOl82iHjyROITgEP*3E83=)0l7ND$F*R)Ph z@nkgAnWN}^L{B{~^7l>`oUC-HCiL%VnIm)|qQ-Azb@QmV}t<2tY=>vV&0twixk%GJ;nHd`oyu+(GvBjS`el_ci?Kxz&8zIdXR4 z^O-e2m=S7x5&7`VOp6DY%@f$hwQ_1x=d+l^T(*Sfv^A!&EqbLb{0UPCm|krOlpU>< zI*lXZy0}P%{oJSo_$Tyk;@(4Y8^~o=C>{a7fE7Ke42sI`EltgBS>P5S z=x!GrktVAry$};J%~!DhdgB%DcM0*2v|3S-3Yd?e>(31s6Ceq2UQ`PbbR>K*QP3GDuEG3`-w+&lf<$MRLFAw4g&F3iAde&=}-fryN|902ir`$A0=T6n&F~BaAO-mg`>;1+vilD)9F3wmZI>#YJlNEz((ui=ucO z$RnO&XAXn~nq+RZ48^^}Dc-*P2%T}4xWfreZOh*Oc&C02mcz+(@7?Nfn!1QtFK3x8 zcr4w+8wIa!#7>3kQDdq_z_spLCA4s5Ns{>?J&MbP3SdCpk(%OP8-DT7|3++=Ob7A1 zRO_CjZ>6w=j^{ic%YA5rpR{n_I%=>10}k$sp3?zz+`jYo@b^b2{wVyZ{LdeXrS@{F zq-bJ_`)fATBz|10jFc4}UVDR9Ig%w`)HAQqp9_vr??WR3=#cCn%HCtIoA7KR%#9^# zdFV;N!U2+@0xt~FY@N_@wz%E9js`~w=>#jG+q4uZt0{Ax6-#y0!jlVGcu0vR=KT&y zemHC0{p!nUxasl-e0k|Aks}^rFrDWIrJfj|Kv!wE!4?;ucV=r>Xwz#zovols5F(G0 zE^8r+*1!a=jdJ!csSJ&d|ymjyI!Rh1+sL~TK4I_N$j5`BDA?h_|F zg0&{GDY-2Lm7Oni&FKloiash!IeV6R-A~&o<%;S|FSF(tc$H-3gGVQ!B-^#EPM^y1 zPRblNi1h}IxlpP=OV>fr1rkGflQP9v4vI5UCAkkzPcp(lGRB~=$#Eg1^IO2`rz%qj zAQV8SqR1LsIMq)TzF>Hdu8KU`=DjSZeOy3S{7xK?XfY)jgjGf^jVn}6VGXfnSr))g3ZgB+%}{4XSIG-O?ZL%SMI&~rI4C`8bo}LRVSxGApmL6!N3H6 zuhFM-M1TqHLk~=l8MuRcqF@eOE}lWcN&FzeH6V~*^Mgc1gG40`BMZ_<97+MFo8@dE z;b~h~gtUsIm=msmYQ;D2uc$Af`*&|Wd46hcZa2Pq++I@y9)DgpgXvy` zM0tOu&Ik99Ymt(q=T5{F-?5-%buz?gKZt6T5e0Arg2RVKNh~wvL+ITlMi|#X&@f37 zB&bCC+|MO{8!Z`hgrejyU1l&NJl)CoW$`r%7Vn}5j>L5M2H|VqCoeDY-hAIDJR>2) z0NNUZUZDI#Vle2PEahiyu_}|ajw-uU8Tcu5^AjkzkM8M3_8tXqhGvKZl3B(So2cxV z<7WYS+w}Q`eFQ}FQ%(^9Yp#7U15 zX}P$;PhmDzi?TvOrAHp|f#+xtFRI!!)zy8d=dNfhFrZwRwcue{Pr48QM@USPk^<#Mx zyF^?W3u9H`!<)5lXJB~Fut?4t*93C}>>n#>yk0K15wK{er$&-j>}ltMS`QKQ^>WTO zLeoOI^DOQ)zSm=>N`zf7udgZ_uiM6iShfj1Xr`#6YwHS`rPTvleS-FOndmDWTKYHv zF?63)`Fu*p<#pLA=7#b$q z8jOx{80vcIOpYN`_1QR*eY(}P;jc1sa5OIe>6p$-jHMr*)P9VWno)l0zP8xp6#jET z4uNIB0wK>@b2_jPAvss0$5}t`n`8w#AS_EaOCFN~TQdtS|ep$ubg55-;Er8%|08tkd__cE4jA-7`23f5Fmg`4X3T`UZQCkrX z7IF#%q^V&pegC*xYnm&pfKJx@PueadxC&|vwGvKKYaI2NFl(j4iumKi&+!cSlMGdx zb45uG#oZ4!xA&Yj9oV`8Rs56N7L3WNsHdri^7IEIrIOz*-~^k7ZPEn3v;q8gBB#pQ zO+J>z%b?;9wZvMe(me#;8fknKdq%9GAuN}7Nkgj-i=Wu~A=97sl?GNhmN}k(1j%;s z5F8B2~qN=XY>3#N<(V zBNaYLq4NdSSs%mo&J0q<Vgp{5bf4RHQO(`$p;qh(9!Yh%*PLWDFcHl8i8*8)~Dp#+COwu#5VjraT9oi z)4O7IozU#I#ats|1rWRShWl}*s+ zwP7N5AUp`YZxvWqEz%`zJ>jKQy3hd8o))8&<>_s@BVQ#9a1G)vbrJ}-eYUCcHR4x!Z68!p{F8KX0 z0N=EndVgvq`qo8WHf3zDmE5J` zf)U}t(}kgK-3+s3x+*SDzd|Nu7On78Oo|*u34WO|8^Z3LCii#quQ*uIQJtTy%_|Ti zBnM9nXUsG;DsJyK!fB?EP5Qrmjz86!_Hzn*DRZ1w-0T?d4x2;pkPrJxW4_1QowhA3;hjuyz3-2$f6=G+u?f8RL6WTYtd^**q57`O zC+y=ckr7~N3pq$IdkMdOx8)}+2z9xe^0nE=zPVRjdU{|M9Pe+mr)8l$Y>@Jf3Hm1m z0}8K@gLHd%^zRG|6O1F{3XT6z@;RvgR`ShkZT=q$J~#vx zK|)=GSw>M36gl`e*I$lVe`V?gF@av8{IwMX08597A%5cl*M|`>Uy(9E)vqag{|yq~ zgHQG^f!E5ee^>s?Ch<29HtOp@|FymH{Q$kC!DalfZv2tv_dndAvLJvM92n1v{hIan z&404~{`VMc{>mK-2FCn{FeT)_g-Jmp0cA)ehbjn&hWkwer9$NVcNXFQ#``00;vaa9 z@c)thXBtJO{|5LY3)&w52q^ysApakszq34nfsy|q6o&p=sC^XkbrdtBucIg<|4joW zar_^kKY~F20hCVhFCcs}kbe35JOIXzy`tsP1JTC`fiq+Hw2*(FsDA|9`~%2>3B=F- z+M75Qe4xQN90)_58w8F61dL<;6XTDVPJdv$dkf;H`k&taBlN<59n5(lkO1+!|Bb`& z-_!s1hwxxvWPh}oAqs*Z0xG|S`D+3Ia`>QN+5g%K0(`YV4A?;13G{ys;%`q=Ffg9~ zFoVj106N{@{G^k}z`+UPf6Dyf@%V=fqW-^S@czmFhwH{4{Gn#Q`8%|TfpL@2ualk) z%8*<$`8w&z*1zjU^$GrGBL8?Z`UhbAhu=a>Qw0C)z@H|^zXb{cj$J_pNCr^DpnHqx ztJ(2CTS0)l+y93V^54e^gaWLZLV$U_86>#@JEw47#{{s!2cAv6j;Y@bNN$S{thOQe z>mYGWzn-N^o}dcBzlhntiC`arpZ$n{o6|(EC@RkXJetXVzw6%3V7=DWy5IxtW?oyk z0sN-(E~CHFOJ-id1wuhIO5phn(myBV4~yr2h7BqU0%$*h_~}5#Pl8trtJznKu?P^2 z>wk{KUuGLbFff5XKsuv;H;Oh#_RmHe7|L}OK+#9eK%P93fAC`^{O0$bWB4ckA1dD; z`~!)<`A_G_Upt9D|Jup(jNde!d9qiU&u`jUHt_Sj`0MYZBz)k@{OcrL<$`GRK*nS= zV9wm{(@t*TRUWDk=(r&C%3J&UH+b=HKG2#9e3t?9KZCatajHuM4aO6c+*|OE&OMd> zTafS-?Bn7q_;JPW&JpLL12^Y?H@g0tE?oVaPQ66=&w;Z1a z1@gb*wEX8@m%a4b#C$*Steb-4kE*|xL$6I}*Hut&B|yDp`U7!m93;l_*U2Zp{E7uV z4Rl%-dF}r606uVH`E}k_W`HZp!vE~%p9}uqs09HPbD-s)1|$!)nfpLTWet;%JZ2tAv{{g1mJK6vM delta 29482 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 Xl$U}8nGXl$9Q3CH>hr|=o}d07!s7Wl diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7a0e9a89..ffed3a25 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Tue Nov 27 07:46:21 CET 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-all.zip diff --git a/gradlew b/gradlew index af6708ff..4f906e0c 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat old mode 100644 new mode 100755 index 0f8d5937..ac1b06f9 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy b/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy index 6fc84a85..d7d54150 100644 --- a/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy +++ b/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy @@ -24,7 +24,7 @@ class FieldsGrailsPlugin extends Plugin { static final String CONSTRAINTS_EVALULATOR_BEAN_NAME = 'validateableConstraintsEvaluator' - def grailsVersion = '3.0 > *' + def grailsVersion = '5.0 > *' def loadAfter = ['domainClass'] From 453641eebf9baa64751ad4772519c3463a90aaa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Berg=20Glasius?= Date: Tue, 2 Jan 2024 22:18:33 +0100 Subject: [PATCH 2/3] Refactored code * Mostly typed * All CompileStatic --- .../formfields/BeanPropertyAccessor.groovy | 184 ++++++------ .../BeanPropertyAccessorFactory.groovy | 249 ++++++++-------- .../BeanPropertyAccessorImpl.groovy | 42 ++- .../DelegatingBeanPropertyAccessorImpl.groovy | 267 +++++++++--------- .../formfields/FieldsGrailsPlugin.groovy | 38 +-- .../FormFieldsTemplateService.groovy | 145 +++++----- .../formfields/PropertyPathAccessor.groovy | 74 +++-- .../DomainClassPropertyAccessorSpec.groovy | 6 +- 8 files changed, 506 insertions(+), 499 deletions(-) diff --git a/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessor.groovy b/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessor.groovy index 8e5eedd0..fabe1e0a 100644 --- a/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessor.groovy +++ b/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessor.groovy @@ -17,104 +17,104 @@ package grails.plugin.formfields import grails.core.GrailsDomainClass -import org.grails.scaffolding.model.property.Constrained import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.scaffolding.model.property.Constrained import org.springframework.validation.FieldError interface BeanPropertyAccessor { - /** - * @return the object at the root of a path expression, e.g. for a `person` bean and `address.street` then `person` is returned. - */ - Object getRootBean() - - /** - * @return the type of the object at the root of a path expression, e.g. for a `person` bean and `address.street` then the type of `person` is returned. - */ - Class getRootBeanType() - - /** - * @return the full path from the root bean to the requested property. - */ - String getPathFromRoot() - - /** - * @return the name of the property at the end of the path, e.g. for `address.home.street`, `street` is returned. - */ - String getPropertyName() - - /** - * @return the type of the object that owns the property at the end of the path, e.g. for a `address.home.street` then the type of `home` is returned. - */ - Class getBeanType() - - /** - * @return the GORM domain type of `beanType`. This will be null if `beanType` is not a domain class. - * @deprecated use {@link #getEntity} - */ - @Deprecated - GrailsDomainClass getBeanClass() - - /** - * @return the GORM domain type of `beanType`. This will be null if `beanType` is not a domain class. - */ - PersistentEntity getEntity() - - /** - * @return all superclasses and interfaces of `beanType` excluding `Object`, `Serializable`, `Comparable` and `Cloneable`. - */ - List getBeanSuperclasses() - - /** - * @return the type of the property at the end of the path, e.g. for `address.home.street` then the type of `street` is returned. - */ - Class getPropertyType() - - /** - * @return all superclasses and interfaces of `propertyType` excluding `Object`, `Serializable`, `Comparable` and `Cloneable`. - */ - List getPropertyTypeSuperclasses() - - /** - * @return the value of the property at the end of the path, e.g. for `address.home.street` then the value of `street` is returned. - */ - Object getValue() - - /** - * @return the GORM persistent property descriptor for the property at the end of the path, e.g. for `address.home.street` then the descriptor of `street` is returned. This will be null for non-domain properties. - */ - PersistentProperty getDomainProperty() - - /** - * @return the constraints of the property at the end of the path, e.g. for `address.home.street` then the constraints of `street` are returned. This will be null for non-domain properties. - */ - Constrained getConstraints() - - /** - * @return the i18n keys used to resolve a label for the property at the end of the path in order of preference. - */ - List getLabelKeys() - - /** - * @return default label text for the property at the end of the path. - */ - String getDefaultLabel() - - /** - * @return the resolved messages for any validation errors present on the property at the end of the path. This will be an empty list if there are no errors or the property is not a validateable type. - */ - List getErrors() - - /** - * @return whether or not the property is required as determined by constraints. This will always be false for non-validateable types. - */ - boolean isRequired() - - /** - * @return whether or not the property has any validation errors (i.e. `getErrors` will return a non-empty list). This will always be false for non-validateable types. - */ - boolean isInvalid() + /** + * @return the object at the root of a path expression, e.g. for a `person` bean and `address.street` then `person` is returned. + */ + Object getRootBean() + + /** + * @return the type of the object at the root of a path expression, e.g. for a `person` bean and `address.street` then the type of `person` is returned. + */ + Class getRootBeanType() + + /** + * @return the full path from the root bean to the requested property. + */ + String getPathFromRoot() + + /** + * @return the name of the property at the end of the path, e.g. for `address.home.street`, `street` is returned. + */ + String getPropertyName() + + /** + * @return the type of the object that owns the property at the end of the path, e.g. for a `address.home.street` then the type of `home` is returned. + */ + Class getBeanType() + + /** + * @return the GORM domain type of `beanType`. This will be null if `beanType` is not a domain class. + * @deprecated use {@link #getEntity} + */ + @Deprecated + GrailsDomainClass getBeanClass() + + /** + * @return the GORM domain type of `beanType`. This will be null if `beanType` is not a domain class. + */ + PersistentEntity getEntity() + + /** + * @return all superclasses and interfaces of `beanType` excluding `Object`, `Serializable`, `Comparable` and `Cloneable`. + */ + List getBeanSuperclasses() + + /** + * @return the type of the property at the end of the path, e.g. for `address.home.street` then the type of `street` is returned. + */ + Class getPropertyType() + + /** + * @return all superclasses and interfaces of `propertyType` excluding `Object`, `Serializable`, `Comparable` and `Cloneable`. + */ + List getPropertyTypeSuperclasses() + + /** + * @return the value of the property at the end of the path, e.g. for `address.home.street` then the value of `street` is returned. + */ + Object getValue() + + /** + * @return the GORM persistent property descriptor for the property at the end of the path, e.g. for `address.home.street` then the descriptor of `street` is returned. This will be null for non-domain properties. + */ + PersistentProperty getDomainProperty() + + /** + * @return the constraints of the property at the end of the path, e.g. for `address.home.street` then the constraints of `street` are returned. This will be null for non-domain properties. + */ + Constrained getConstraints() + + /** + * @return the i18n keys used to resolve a label for the property at the end of the path in order of preference. + */ + List getLabelKeys() + + /** + * @return default label text for the property at the end of the path. + */ + String getDefaultLabel() + + /** + * @return the resolved messages for any validation errors present on the property at the end of the path. This will be an empty list if there are no errors or the property is not a validateable type. + */ + List getErrors() + + /** + * @return whether or not the property is required as determined by constraints. This will always be false for non-validateable types. + */ + boolean isRequired() + + /** + * @return whether or not the property has any validation errors (i.e. `getErrors` will return a non-empty list). This will always be false for non-validateable types. + */ + boolean isInvalid() } diff --git a/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorFactory.groovy b/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorFactory.groovy index 128233d7..9e041caf 100644 --- a/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorFactory.groovy +++ b/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorFactory.groovy @@ -17,11 +17,13 @@ package grails.plugin.formfields import grails.core.GrailsApplication +import grails.core.support.GrailsApplicationAware +import grails.core.support.proxy.ProxyHandler +import grails.gorm.validation.ConstrainedProperty import grails.gorm.validation.DefaultConstrainedProperty import grails.validation.Validateable +import groovy.transform.CompileStatic import groovy.transform.PackageScope -import grails.core.support.GrailsApplicationAware -import grails.core.support.proxy.ProxyHandler import org.grails.datastore.gorm.validation.constraints.eval.ConstraintsEvaluator import org.grails.datastore.gorm.validation.constraints.registry.DefaultConstraintRegistry import org.grails.datastore.mapping.model.MappingContext @@ -38,158 +40,159 @@ import org.springframework.beans.PropertyAccessorFactory import org.springframework.context.support.StaticMessageSource import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type +import java.util.regex.Matcher import java.util.regex.Pattern +@CompileStatic class BeanPropertyAccessorFactory implements GrailsApplicationAware { - GrailsApplication grailsApplication - ConstraintsEvaluator constraintsEvaluator - ProxyHandler proxyHandler - DomainPropertyFactory fieldsDomainPropertyFactory - MappingContext grailsDomainClassMappingContext - - BeanPropertyAccessor accessorFor(bean, String propertyPath) { - if (bean == null) { - new PropertyPathAccessor(propertyPath) - } else { - resolvePropertyFromPath(bean, propertyPath) - } - } - - private PersistentEntity resolveDomainClass(Class beanClass) { - grailsDomainClassMappingContext.getPersistentEntity(beanClass.name) - } + GrailsApplication grailsApplication + ConstraintsEvaluator constraintsEvaluator + ProxyHandler proxyHandler + DomainPropertyFactory fieldsDomainPropertyFactory + MappingContext grailsDomainClassMappingContext + + BeanPropertyAccessor accessorFor(bean, String propertyPath) { + if (bean == null) { + new PropertyPathAccessor(propertyPath) + } else { + resolvePropertyFromPath(bean, propertyPath) + } + } - private BeanPropertyAccessor resolvePropertyFromPath(bean, String pathFromRoot) { - def beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean) - def pathElements = pathFromRoot.tokenize(".") + private PersistentEntity resolveDomainClass(Class beanClass) { + grailsDomainClassMappingContext.getPersistentEntity(beanClass.name) + } - def params = [rootBean: bean, rootBeanType: bean.getClass(), pathFromRoot: pathFromRoot, grailsApplication: grailsApplication] + private BeanPropertyAccessor resolvePropertyFromPath(bean, String pathFromRoot) { + BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean) + List pathElements = pathFromRoot.tokenize(".") - DomainProperty domainProperty = resolvePropertyFromPathComponents(beanWrapper, pathElements, params) + Map params = [rootBean: bean, rootBeanType: bean.getClass(), pathFromRoot: pathFromRoot, grailsApplication: grailsApplication] - if (domainProperty != null) { - new DelegatingBeanPropertyAccessorImpl(bean, params.value, params.propertyType, pathFromRoot, domainProperty) - } else { - new BeanPropertyAccessorImpl(params) - } + DomainProperty domainProperty = resolvePropertyFromPathComponents(beanWrapper, pathElements, params) - } + if (domainProperty != null) { + new DelegatingBeanPropertyAccessorImpl(bean, params.value, params.propertyType as Class, pathFromRoot, domainProperty) + } else { + new BeanPropertyAccessorImpl(params) + } - private DomainProperty resolvePropertyFromPathComponents(BeanWrapper beanWrapper, List pathElements, params) { - def propertyName = pathElements.remove(0) - PersistentEntity beanClass = resolveDomainClass(beanWrapper.wrappedClass) - def propertyType = resolvePropertyType(beanWrapper, beanClass, propertyName) - def value = beanWrapper.getPropertyValue(propertyName) - if (pathElements.empty) { - params.value = value - params.propertyType = propertyType - - PersistentProperty persistentProperty - String nameWithoutIndex = stripIndex(propertyName) - if (beanClass != null) { - persistentProperty = beanClass.getPropertyByName(nameWithoutIndex) - if (!persistentProperty && beanClass.isIdentityName(nameWithoutIndex)) { - persistentProperty = beanClass.identity - } - } + } - if (persistentProperty != null) { - fieldsDomainPropertyFactory.build(persistentProperty) - } else { - params.entity = beanClass - params.beanType = beanWrapper.wrappedClass - params.propertyType = propertyType - params.propertyName = nameWithoutIndex - params.domainProperty = null - params.constraints = resolveConstraints(beanWrapper, params.propertyName) - null - } - } else { - resolvePropertyFromPathComponents(beanWrapperFor(propertyType, value), pathElements, params) - } - } + private DomainProperty resolvePropertyFromPathComponents(BeanWrapper beanWrapper, List pathElements, Map params) { + String propertyName = pathElements.remove(0) + PersistentEntity beanClass = resolveDomainClass(beanWrapper.wrappedClass) + Class propertyType = resolvePropertyType(beanWrapper, beanClass, propertyName) + Object value = beanWrapper.getPropertyValue(propertyName) + if (pathElements.empty) { + params.value = value + params.propertyType = propertyType + + PersistentProperty persistentProperty + String nameWithoutIndex = stripIndex(propertyName) + if (beanClass != null) { + persistentProperty = beanClass.getPropertyByName(nameWithoutIndex) + if (!persistentProperty && beanClass.isIdentityName(nameWithoutIndex)) { + persistentProperty = beanClass.identity + } + } + + if (persistentProperty != null) { + return fieldsDomainPropertyFactory.build(persistentProperty) + } else { + params.entity = beanClass + params.beanType = beanWrapper.wrappedClass + params.propertyType = propertyType + params.propertyName = nameWithoutIndex + params.domainProperty = null + params.constraints = resolveConstraints(beanWrapper, params.propertyName as String) + return null + } + } else { + return resolvePropertyFromPathComponents(beanWrapperFor(propertyType, value), pathElements, params) + } + } - private Constrained resolveConstraints(BeanWrapper beanWrapper, String propertyName) { + private Constrained resolveConstraints(BeanWrapper beanWrapper, String propertyName) { Class type = beanWrapper.wrappedClass boolean defaultNullable = Validateable.class.isAssignableFrom(type) ? type.metaClass.invokeStaticMethod(type, 'defaultNullable') : false - grails.gorm.validation.Constrained constraint = constraintsEvaluator.evaluate(type, defaultNullable)[propertyName] - if (!constraint) { - constraint = createDefaultConstraint(beanWrapper, propertyName) - } - new Constrained(constraint) - } + ConstrainedProperty constraint = constraintsEvaluator.evaluate(type, defaultNullable)[propertyName] - private grails.gorm.validation.Constrained createDefaultConstraint(BeanWrapper beanWrapper, String propertyName) { - def defaultConstraint = new DefaultConstrainedProperty(beanWrapper.wrappedClass, propertyName, beanWrapper.getPropertyType(propertyName), new DefaultConstraintRegistry(new StaticMessageSource())) - defaultConstraint.nullable = true - defaultConstraint + new Constrained(constraint ?: createDefaultConstraint(beanWrapper, propertyName)) } - private Class resolvePropertyType(BeanWrapper beanWrapper, PersistentEntity beanClass, String propertyName) { - Class propertyType = null - if (beanClass) { - propertyType = resolveDomainPropertyType(beanClass, propertyName) - } - if (!propertyType) { - propertyType = resolveNonDomainPropertyType(beanWrapper, propertyName) - } - propertyType - } + private static ConstrainedProperty createDefaultConstraint(BeanWrapper beanWrapper, String propertyName) { + new DefaultConstrainedProperty(beanWrapper.wrappedClass, propertyName, beanWrapper.getPropertyType(propertyName), new DefaultConstraintRegistry(new StaticMessageSource())).tap { + nullable = true + } + } - private Class resolveDomainPropertyType(PersistentEntity beanClass, String propertyName) { - def propertyNameWithoutIndex = stripIndex(propertyName) - def persistentProperty = beanClass.getPropertyByName(propertyNameWithoutIndex) - if (!persistentProperty && beanClass.isIdentityName(propertyNameWithoutIndex)) { - persistentProperty = beanClass.identity - } - if (!persistentProperty) { - return null - } - boolean isIndexed = propertyName =~ INDEXED_PROPERTY_PATTERN - if (isIndexed) { - if (persistentProperty instanceof Basic) { - persistentProperty.componentType - } else if (persistentProperty instanceof Association) { - persistentProperty.associatedEntity.javaClass - } - } else { - persistentProperty.type - } - } + private static Class resolvePropertyType(BeanWrapper beanWrapper, PersistentEntity beanClass, String propertyName) { + return resolveDomainPropertyType(beanClass, propertyName) ?: resolveNonDomainPropertyType(beanWrapper, propertyName) + } + + private static Class resolveDomainPropertyType(PersistentEntity beanClass, String propertyName) { + if(beanClass) { + String propertyNameWithoutIndex = stripIndex(propertyName) + PersistentProperty persistentProperty = beanClass.getPropertyByName(propertyNameWithoutIndex) + if (!persistentProperty && beanClass.isIdentityName(propertyNameWithoutIndex)) { + persistentProperty = beanClass.identity + } + if (!persistentProperty) { + return null + } + boolean isIndexed = propertyName =~ INDEXED_PROPERTY_PATTERN + if (isIndexed) { + if (persistentProperty instanceof Basic) { + return (persistentProperty as Basic).componentType + } else if (persistentProperty instanceof Association) { + return (persistentProperty as Association).associatedEntity.javaClass + } + } else { + return persistentProperty.type + } + } + return null + } - private Class resolveNonDomainPropertyType(BeanWrapper beanWrapper, String propertyName) { - def type = beanWrapper.getPropertyType(propertyName) + private static Class resolveNonDomainPropertyType(BeanWrapper beanWrapper, String propertyName) { + Class type = beanWrapper.getPropertyType(propertyName) if (type == null) { - def match = propertyName =~ INDEXED_PROPERTY_PATTERN + String match = getPropertyMatch(propertyName) if (match) { - def genericType = beanWrapper.getPropertyDescriptor(match[0][1]).readMethod.genericReturnType + Type genericType = beanWrapper.getPropertyDescriptor(match).readMethod.genericReturnType if (genericType instanceof ParameterizedType) { - switch (genericType.rawType) { + ParameterizedType parameterizedType = genericType as ParameterizedType + switch (parameterizedType.rawType) { case Collection: - type = genericType.actualTypeArguments[0] - break + return parameterizedType.actualTypeArguments[0] as Class case Map: - type = genericType.actualTypeArguments[1] - break + return parameterizedType.actualTypeArguments[1] as Class } } else { - type = Object + return Object } } } - type + return type } - private BeanWrapper beanWrapperFor(Class type, value) { - value ? PropertyAccessorFactory.forBeanPropertyAccess(proxyHandler.unwrapIfProxy(value)) : new BeanWrapperImpl(type) - } + private BeanWrapper beanWrapperFor(Class type, value) { + value ? PropertyAccessorFactory.forBeanPropertyAccess(proxyHandler.unwrapIfProxy(value)) : new BeanWrapperImpl(type) + } - private static final Pattern INDEXED_PROPERTY_PATTERN = ~/^(\w+)\[(.+)\]$/ + private static final Pattern INDEXED_PROPERTY_PATTERN = ~/^(\w+)\[(.+)]$/ - @PackageScope - static String stripIndex(String propertyName) { - def matcher = propertyName =~ INDEXED_PROPERTY_PATTERN - matcher.matches() ? matcher[0][1] : propertyName - } + private static String getPropertyMatch(String propertyName) { + Matcher matcher = propertyName =~ INDEXED_PROPERTY_PATTERN + matcher.matches() ? (matcher[0] as String[])[1] : null + } + + @PackageScope + static String stripIndex(String propertyName) { + def matcher = propertyName =~ INDEXED_PROPERTY_PATTERN + matcher.matches() ? (matcher[0] as String[])[1] : propertyName + } } diff --git a/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorImpl.groovy b/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorImpl.groovy index 00b77975..64938b16 100644 --- a/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorImpl.groovy +++ b/src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorImpl.groovy @@ -20,20 +20,23 @@ import grails.core.GrailsApplication import grails.core.GrailsDomainClass import grails.gorm.Entity import grails.gorm.validation.ConstrainedProperty -import grails.plugins.VersionComparator import grails.util.GrailsNameUtils +import grails.validation.Validateable import grails.web.databinding.WebDataBinding import groovy.transform.Canonical import groovy.transform.CompileStatic +import groovy.transform.Memoized import groovy.transform.TupleConstructor import org.apache.commons.lang.ClassUtils import org.grails.datastore.gorm.GormEntity +import org.grails.datastore.gorm.GormValidateable import org.grails.datastore.mapping.dirty.checking.DirtyCheckable import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.PersistentProperty import org.grails.scaffolding.model.property.Constrained import org.springframework.validation.FieldError +@CompileStatic @Canonical @TupleConstructor(includes = ['beanType', 'propertyName', 'propertyType']) class BeanPropertyAccessorImpl implements BeanPropertyAccessor { @@ -51,22 +54,10 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor { PersistentEntity entity GrailsApplication grailsApplication - /** - * Since Grails 2.3 blank values that are provided for String properties are - * converted to null by default - */ - @Lazy - private boolean convertBlanksToNull = { -> - - String applicationGrailsVersion = grailsApplication.metadata.getGrailsVersion() - boolean isAtLeastGrails2Point3 = new VersionComparator().compare(applicationGrailsVersion, '2.3') != -1 - - if (isAtLeastGrails2Point3) { - getDataBindingConfigParamValue('convertEmptyStringsToNull') && getDataBindingConfigParamValue('trimStrings') - } else { - false - } - }() + @Memoized + private boolean convertBlanksToNull() { + getDataBindingConfigParamValue('convertEmptyStringsToNull') && getDataBindingConfigParamValue('trimStrings') + } /** * Returns the effective value of a a boolean config param from the grails.databinding node @@ -90,7 +81,7 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor { [ "${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, ''), "${GrailsNameUtils.getPropertyName(beanType.simpleName)}.${propertyName}.label" - ].unique() + ].unique() as List } String getDefaultLabel() { @@ -98,12 +89,12 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor { } List getErrors() { - if (rootBean.metaClass.hasProperty(rootBean, 'errors') && rootBean.errors) { - - rootBean.errors.getFieldErrors(pathFromRoot) - } else { - [] + if (rootBean instanceof Validateable) { + return (rootBean as Validateable).errors.getFieldErrors(pathFromRoot) + } else if (rootBean instanceof GormValidateable) { + return (rootBean as GormValidateable).errors.getFieldErrors(pathFromRoot) } + return [] } boolean isRequired() { @@ -113,7 +104,7 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor { // if the property prohibits nulls and blanks are converted to nulls, then blanks will be prohibited even if a blank // constraint does not exist boolean hasBlankConstraint = constraints?.hasAppliedConstraint(ConstrainedProperty.BLANK_CONSTRAINT) - boolean blanksImplicitlyProhibited = !hasBlankConstraint && !constraints?.nullable && convertBlanksToNull + boolean blanksImplicitlyProhibited = !hasBlankConstraint && !constraints?.nullable && convertBlanksToNull() !constraints?.nullable && (!constraints?.blank || blanksImplicitlyProhibited) } else { !constraints?.nullable @@ -124,8 +115,7 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor { !errors.isEmpty() } - @CompileStatic - private List getSuperclassesAndInterfaces(Class type) { + private static List getSuperclassesAndInterfaces(Class type) { List superclasses = [] superclasses.addAll(ClassUtils.getAllSuperclasses(ClassUtils.primitiveToWrapper(type))) for (Object it in ClassUtils.getAllInterfaces(type)) { diff --git a/src/main/groovy/grails/plugin/formfields/DelegatingBeanPropertyAccessorImpl.groovy b/src/main/groovy/grails/plugin/formfields/DelegatingBeanPropertyAccessorImpl.groovy index f8209944..5b18994f 100644 --- a/src/main/groovy/grails/plugin/formfields/DelegatingBeanPropertyAccessorImpl.groovy +++ b/src/main/groovy/grails/plugin/formfields/DelegatingBeanPropertyAccessorImpl.groovy @@ -7,6 +7,7 @@ import grails.validation.Validateable import grails.web.databinding.WebDataBinding import groovy.transform.Canonical import groovy.transform.CompileStatic +import groovy.transform.ToString import org.apache.commons.lang.ClassUtils import org.grails.datastore.gorm.GormEntity import org.grails.datastore.gorm.GormValidateable @@ -19,141 +20,137 @@ import org.springframework.validation.Errors import org.springframework.validation.FieldError @CompileStatic -@Canonical(includes = ['beanType', 'propertyName', 'propertyType']) +@Canonical +@ToString(includes = ['beanType', 'propertyName', 'propertyType']) class DelegatingBeanPropertyAccessorImpl implements BeanPropertyAccessor { - private DomainProperty domainProperty - private Object rootBean - private Object value - private String pathFromRoot - final Class beanType - final String propertyName - final Class propertyType - - DelegatingBeanPropertyAccessorImpl(Object rootBean, Object value, Class propertyType, String pathFromRoot, DomainProperty domainProperty) { - this.rootBean = rootBean - this.value = value - this.pathFromRoot = pathFromRoot - this.domainProperty = domainProperty - this.propertyType = propertyType - this.propertyName = domainProperty.name - this.beanType = domainProperty.beanType - } - - @Override - Object getRootBean() { - rootBean - } - - @Override - Class getRootBeanType() { - rootBean.getClass() - } - - @Override - String getPathFromRoot() { - pathFromRoot - } - - @Override - @Deprecated - GrailsDomainClass getBeanClass() { - throw new UnsupportedOperationException() - } - - @Override - PersistentEntity getEntity() { - domainProperty.domainClass - } - - @Override - List getBeanSuperclasses() { - getSuperclassesAndInterfaces(beanType) - } - - @Override - List getPropertyTypeSuperclasses() { - getSuperclassesAndInterfaces(propertyType) - } - - @Override - Object getValue() { - value - } - - @Override - PersistentProperty getDomainProperty() { - domainProperty.persistentProperty - } - - @Override - Constrained getConstraints() { - domainProperty.constrained - } - - @Override - List getLabelKeys() { - List labelKeys = [] - if (rootBean) { - labelKeys.add("${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, '')) - } - labelKeys.addAll(domainProperty.labelKeys) - labelKeys.unique() - } - - @Override - String getDefaultLabel() { - domainProperty.defaultLabel - } - - @Override - List getErrors() { - Errors errors - if (rootBean instanceof Validateable) { - errors = ((Validateable) rootBean).errors - } else if (rootBean instanceof GormValidateable) { - errors = ((GormValidateable) rootBean).errors - } - if (errors) { - errors.getFieldErrors(pathFromRoot) - } else { - [] - } - } - - @Override - boolean isRequired() { - domainProperty.required - } - - @Override - boolean isInvalid() { - !errors.isEmpty() - } - - @Override - int hashCode() { - return Objects.hash(beanType, propertyName, propertyType) - } - - @Override - boolean equals(Object obj) { - this.hashCode() == obj?.hashCode() - } - - private List getSuperclassesAndInterfaces(Class type) { - List superclasses = [] - superclasses.addAll(ClassUtils.getAllSuperclasses(ClassUtils.primitiveToWrapper(type))) - for (Object it in ClassUtils.getAllInterfaces(type)) { - Class interfaceCls = (Class) it - String name = interfaceCls.name - if (name.indexOf('$') == -1) { - if (interfaceCls.package != GormEntity.package) { - superclasses.add(interfaceCls) - } - } - } - superclasses.removeAll([Object, GroovyObject, Serializable, Cloneable, Comparable, WebDataBinding, DirtyCheckable, Entity]) - return superclasses.unique() - } + private DomainProperty domainProperty + private Object rootBean + private Object value + private String pathFromRoot + final Class beanType + final String propertyName + final Class propertyType + + DelegatingBeanPropertyAccessorImpl(Object rootBean, Object value, Class propertyType, String pathFromRoot, DomainProperty domainProperty) { + this.rootBean = rootBean + this.value = value + this.pathFromRoot = pathFromRoot + this.domainProperty = domainProperty + this.propertyType = propertyType + this.propertyName = domainProperty.name + this.beanType = domainProperty.beanType + } + + @Override + Object getRootBean() { + rootBean + } + + @Override + Class getRootBeanType() { + rootBean.getClass() + } + + @Override + String getPathFromRoot() { + pathFromRoot + } + + @Override + @Deprecated + GrailsDomainClass getBeanClass() { + throw new UnsupportedOperationException() + } + + @Override + PersistentEntity getEntity() { + domainProperty.domainClass + } + + @Override + List getBeanSuperclasses() { + getSuperclassesAndInterfaces(beanType) + } + + @Override + List getPropertyTypeSuperclasses() { + getSuperclassesAndInterfaces(propertyType) + } + + @Override + Object getValue() { + value + } + + @Override + PersistentProperty getDomainProperty() { + domainProperty.persistentProperty + } + + @Override + Constrained getConstraints() { + domainProperty.constrained + } + + @Override + List getLabelKeys() { + List labelKeys = [] + if (rootBean) { + labelKeys.add("${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, '')) + } + labelKeys.addAll(domainProperty.labelKeys) + labelKeys.unique() as List + } + + @Override + String getDefaultLabel() { + domainProperty.defaultLabel + } + + @Override + List getErrors() { + if (rootBean instanceof Validateable) { + return (rootBean as Validateable).errors.getFieldErrors(pathFromRoot) + } else if (rootBean instanceof GormValidateable) { + return (rootBean as GormValidateable).errors.getFieldErrors(pathFromRoot) + } + return [] + } + + @Override + boolean isRequired() { + domainProperty.required + } + + @Override + boolean isInvalid() { + !errors.isEmpty() + } + + @Override + int hashCode() { + return Objects.hash(beanType, propertyName, propertyType) + } + + @Override + boolean equals(Object obj) { + this.hashCode() == obj?.hashCode() + } + + private List getSuperclassesAndInterfaces(Class type) { + List superclasses = [] + superclasses.addAll(ClassUtils.getAllSuperclasses(ClassUtils.primitiveToWrapper(type))) + for (Object it in ClassUtils.getAllInterfaces(type)) { + Class interfaceCls = (Class) it + String name = interfaceCls.name + if (name.indexOf('$') == -1) { + if (interfaceCls.package != GormEntity.package) { + superclasses.add(interfaceCls) + } + } + } + superclasses.removeAll([Object, GroovyObject, Serializable, Cloneable, Comparable, WebDataBinding, DirtyCheckable, Entity]) + return superclasses.unique() + } } diff --git a/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy b/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy index d7d54150..b8245e19 100644 --- a/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy +++ b/src/main/groovy/grails/plugin/formfields/FieldsGrailsPlugin.groovy @@ -16,30 +16,32 @@ package grails.plugin.formfields import grails.plugins.Plugin - import org.grails.scaffolding.model.DomainModelServiceImpl import org.grails.scaffolding.model.property.DomainPropertyFactoryImpl class FieldsGrailsPlugin extends Plugin { - static final String CONSTRAINTS_EVALULATOR_BEAN_NAME = 'validateableConstraintsEvaluator' + static final String CONSTRAINTS_EVALULATOR_BEAN_NAME = 'validateableConstraintsEvaluator' - def grailsVersion = '5.0 > *' + def grailsVersion = '5.0 > *' - def loadAfter = ['domainClass'] + def loadAfter = ['domainClass'] - @Override - Closure doWithSpring() {{-> - beanPropertyAccessorFactory(BeanPropertyAccessorFactory) { - constraintsEvaluator = ref(CONSTRAINTS_EVALULATOR_BEAN_NAME) - proxyHandler = ref('proxyHandler') - fieldsDomainPropertyFactory = ref('fieldsDomainPropertyFactory') - grailsDomainClassMappingContext = ref('grailsDomainClassMappingContext') - } - formFieldsTemplateService(FormFieldsTemplateService) - fieldsDomainPropertyFactory(DomainPropertyFactoryImpl) - domainModelService(DomainModelServiceImpl) { - domainPropertyFactory: ref(fieldsDomainPropertyFactory) - } - }} + @Override + Closure doWithSpring() { + { -> + beanPropertyAccessorFactory(BeanPropertyAccessorFactory) { + constraintsEvaluator = ref(CONSTRAINTS_EVALULATOR_BEAN_NAME) + proxyHandler = ref('proxyHandler') + fieldsDomainPropertyFactory = ref('fieldsDomainPropertyFactory') + grailsDomainClassMappingContext = ref('grailsDomainClassMappingContext') + } + formFieldsTemplateService(FormFieldsTemplateService) + fieldsDomainPropertyFactory(DomainPropertyFactoryImpl) + domainModelService(DomainModelServiceImpl) { + domainPropertyFactory: + ref(fieldsDomainPropertyFactory) + } + } + } } diff --git a/src/main/groovy/grails/plugin/formfields/FormFieldsTemplateService.groovy b/src/main/groovy/grails/plugin/formfields/FormFieldsTemplateService.groovy index 79737242..2c14aeae 100644 --- a/src/main/groovy/grails/plugin/formfields/FormFieldsTemplateService.groovy +++ b/src/main/groovy/grails/plugin/formfields/FormFieldsTemplateService.groovy @@ -17,8 +17,10 @@ package grails.plugin.formfields import grails.core.GrailsApplication +import grails.plugins.GrailsPlugin import grails.plugins.GrailsPluginManager import grails.util.GrailsNameUtils +import groovy.transform.CompileStatic import groovy.transform.Memoized import groovy.util.logging.Slf4j import org.grails.datastore.mapping.model.types.ManyToMany @@ -28,14 +30,13 @@ import org.grails.datastore.mapping.model.types.OneToOne import org.grails.scaffolding.model.property.Constrained import org.grails.web.gsp.io.GrailsConventionGroovyPageLocator import org.grails.web.servlet.mvc.GrailsWebRequest -import org.grails.web.util.GrailsApplicationAttributes import org.springframework.beans.factory.annotation.Autowired -import org.springframework.web.context.request.RequestAttributes import org.springframework.web.context.request.RequestContextHolder import static org.grails.io.support.GrailsResourceUtils.appendPiecesForUri @Slf4j +@CompileStatic class FormFieldsTemplateService { public static final String SETTING_WIDGET_PREFIX = 'grails.plugin.fields.widgetPrefix' @@ -55,69 +56,16 @@ class FormFieldsTemplateService { return shouldCache ? widgetPrefixCached : widgetPrefixNotCached } - @Memoized - private String getWidgetPrefixCached() { - widgetPrefixNotCached - } - - private String getWidgetPrefixNotCached() { - return grailsApplication?.config?.getProperty(SETTING_WIDGET_PREFIX, 'widget-') - } - - String getTemplateFor(String property) { shouldCache ? getTemplateForCached(property) : getTemplateForNotCached(property) } - - @Memoized - private getTemplateForCached(String templateProperty) { - getTemplateForNotCached(templateProperty) - } - Map findTemplate(BeanPropertyAccessor propertyAccessor, String templateName, String templatesFolder, String theme = null) { shouldCache ? findTemplateCached(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, theme) : findTemplateNotCached(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, theme) } - private getTemplateForNotCached(String templateProperty) { - return grailsApplication?.config?.getProperty("grails.plugin.fields.$templateProperty", templateProperty) ?: templateProperty - } - - @Memoized - private findTemplateCached(BeanPropertyAccessor propertyAccessor, String controllerNamespace, String controllerName, String actionName, String templateName, String templatesFolder, String themeName) { - findTemplateNotCached(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, themeName) - } - - private findTemplateNotCached(BeanPropertyAccessor propertyAccessor, String controllerNamespace, String controllerName, String actionName, String templateName, String templatesFolder, String themeName) { - List candidatePaths - if (themeName) { - //if theme is specified, first resolve all theme paths and then all the default paths - String themeFolder = THEMES_FOLDER + "/" + themeName - candidatePaths = candidateTemplatePaths(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, themeFolder) - candidatePaths = candidatePaths + candidateTemplatePaths(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, null) - } else { - candidatePaths = candidateTemplatePaths(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, null) - } - - candidatePaths.findResult { String path -> - log.debug "looking for template with path $path" - def source = groovyPageLocator.findTemplateByPath(path) - if (source) { - Map template = [path: path] - def plugin = pluginManager.allPlugins.find { - source.URI.startsWith(it.pluginPath) - } - template.plugin = plugin?.name - log.debug "found template $template.path ${plugin ? "in $template.plugin plugin" : ''}" - return template - } else { - null - } - } - } - static String toPropertyNameFormat(Class type) { def propertyNameFormat = GrailsNameUtils.getLogicalPropertyName(type.canonicalName, '') if (propertyNameFormat.endsWith('[]')) { @@ -194,15 +142,6 @@ class FormFieldsTemplateService { templateResolveOrder } - private String getAssociationPath(BeanPropertyAccessor propertyAccessor) { - String associationPath = null - if (propertyAccessor.domainProperty instanceof OneToOne) associationPath = 'oneToOne' - if (propertyAccessor.domainProperty instanceof OneToMany) associationPath = 'oneToMany' - if (propertyAccessor.domainProperty instanceof ManyToMany) associationPath = 'manyToMany' - if (propertyAccessor.domainProperty instanceof ManyToOne) associationPath = 'manyToOne' - associationPath - } - protected String getWidget(Constrained cp, Class propertyType) { if (null == cp) { return null @@ -224,18 +163,54 @@ class FormFieldsTemplateService { return widget } - private String getControllerNamespace() { - if (GrailsWebRequest.metaClass.respondsTo(GrailsWebRequest, "getControllerNamespace").size() > 0) { - return RequestContextHolder.requestAttributes?.getAttribute(GrailsApplicationAttributes.CONTROLLER_NAMESPACE_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST) - } + @Memoized + private String getWidgetPrefixCached() { + widgetPrefixNotCached + } + + private String getWidgetPrefixNotCached() { + return grailsApplication?.config?.getProperty(SETTING_WIDGET_PREFIX, 'widget-') + } + + @Memoized + private String getTemplateForCached(String templateProperty) { + getTemplateForNotCached(templateProperty) + } + + private String getTemplateForNotCached(String templateProperty) { + grailsApplication?.config?.getProperty("grails.plugin.fields.$templateProperty", templateProperty) ?: templateProperty } - private String getControllerName() { - RequestContextHolder.requestAttributes?.controllerName + @Memoized + private Map findTemplateCached(BeanPropertyAccessor propertyAccessor, String controllerNamespace, String controllerName, String actionName, String templateName, String templatesFolder, String themeName) { + findTemplateNotCached(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, themeName) } - private String getActionName() { - RequestContextHolder.requestAttributes?.actionName + private Map findTemplateNotCached(BeanPropertyAccessor propertyAccessor, String controllerNamespace, String controllerName, String actionName, String templateName, String templatesFolder, String themeName) { + List candidatePaths + if (themeName) { + //if theme is specified, first resolve all theme paths and then all the default paths + String themeFolder = THEMES_FOLDER + "/" + themeName + candidatePaths = candidateTemplatePaths(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, themeFolder) + candidatePaths = candidatePaths + candidateTemplatePaths(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, null) + } else { + candidatePaths = candidateTemplatePaths(propertyAccessor, controllerNamespace, controllerName, actionName, templateName, templatesFolder, null) + } + + candidatePaths.findResult { String path -> + log.debug "looking for template with path $path" + def source = groovyPageLocator.findTemplateByPath(path) + if (source) { + Map template = [path: path] + GrailsPlugin plugin = pluginManager.allPlugins.find { + source.URI.startsWith(it.pluginPath) + } + template.plugin = plugin?.name + log.debug "found template $template.path ${plugin ? "in $template.plugin plugin" : ''}" + return template + } + return null + } } private boolean getShouldCache() { @@ -244,4 +219,30 @@ class FormFieldsTemplateService { return !cacheDisabled } + private static String getAssociationPath(BeanPropertyAccessor propertyAccessor) { + switch (propertyAccessor.domainProperty) { + case OneToOne: return 'oneToOne' + case OneToMany: return 'oneToMany' + case ManyToMany: return 'manyToMany' + case ManyToOne: return 'manyToOne' + default: return null + } + } + + private static String getControllerNamespace() { + return grailsWebRequest?.getControllerNamespace() + } + + private static String getControllerName() { + grailsWebRequest?.controllerName + } + + private static String getActionName() { + grailsWebRequest?.actionName + } + + private static GrailsWebRequest getGrailsWebRequest() { + RequestContextHolder.requestAttributes as GrailsWebRequest + } + } diff --git a/src/main/groovy/grails/plugin/formfields/PropertyPathAccessor.groovy b/src/main/groovy/grails/plugin/formfields/PropertyPathAccessor.groovy index da8b20bc..4071e479 100644 --- a/src/main/groovy/grails/plugin/formfields/PropertyPathAccessor.groovy +++ b/src/main/groovy/grails/plugin/formfields/PropertyPathAccessor.groovy @@ -1,51 +1,65 @@ package grails.plugin.formfields +import grails.core.GrailsDomainClass import grails.gorm.validation.DefaultConstrainedProperty import grails.util.GrailsNameUtils import groovy.transform.Canonical import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode import groovy.transform.ToString import org.grails.datastore.gorm.validation.constraints.registry.DefaultConstraintRegistry -import org.grails.datastore.gorm.validation.constraints.registry.DefaultValidatorRegistry import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.PersistentProperty import org.grails.scaffolding.model.property.Constrained import org.springframework.context.support.StaticMessageSource import org.springframework.validation.FieldError + import static grails.plugin.formfields.BeanPropertyAccessorFactory.stripIndex import static java.util.Collections.EMPTY_LIST import static org.apache.commons.lang.StringUtils.substringAfterLast -import grails.core.* @CompileStatic -@Canonical(includes = ['beanType', 'propertyName', 'propertyType']) +@Canonical +@ToString(includes = ['beanType', 'propertyName', 'propertyType']) class PropertyPathAccessor implements BeanPropertyAccessor { - final String pathFromRoot - final String propertyName = stripIndex pathFromRoot.contains('.') ? substringAfterLast(pathFromRoot, '.') : pathFromRoot - final Class beanType = null - final Class propertyType = Object - - PropertyPathAccessor(String pathFromRoot) { - this.pathFromRoot = pathFromRoot - } - - String getDefaultLabel() { - GrailsNameUtils.getNaturalName(propertyName) - } - - Object getRootBean() { null } - Class getRootBeanType() { null } - GrailsDomainClass getBeanClass() { null } - PersistentEntity getEntity() { null } - List getBeanSuperclasses() { EMPTY_LIST } - List getPropertyTypeSuperclasses() { EMPTY_LIST } - Object getValue() { null } - Constrained getConstraints() { new Constrained(new DefaultConstrainedProperty(Object, propertyName, String, new DefaultConstraintRegistry(new StaticMessageSource()))) } - PersistentProperty getDomainProperty() { null } - List getLabelKeys() { EMPTY_LIST } - List getErrors() { EMPTY_LIST } - boolean isRequired() { false } - boolean isInvalid() { false } + final String pathFromRoot + final String propertyName = stripIndex pathFromRoot.contains('.') ? substringAfterLast(pathFromRoot, '.') : pathFromRoot + final Class beanType = null + final Class propertyType = Object + + PropertyPathAccessor(String pathFromRoot) { + this.pathFromRoot = pathFromRoot + } + + String getDefaultLabel() { + GrailsNameUtils.getNaturalName(propertyName) + } + + Object getRootBean() { null } + + Class getRootBeanType() { null } + + GrailsDomainClass getBeanClass() { null } + + PersistentEntity getEntity() { null } + + List getBeanSuperclasses() { EMPTY_LIST } + + List getPropertyTypeSuperclasses() { EMPTY_LIST } + + Object getValue() { null } + + Constrained getConstraints() { + new Constrained(new DefaultConstrainedProperty(Object, propertyName, String, new DefaultConstraintRegistry(new StaticMessageSource()))) + } + + PersistentProperty getDomainProperty() { null } + + List getLabelKeys() { EMPTY_LIST } + + List getErrors() { EMPTY_LIST } + + boolean isRequired() { false } + + boolean isInvalid() { false } } diff --git a/src/test/groovy/grails/plugin/formfields/DomainClassPropertyAccessorSpec.groovy b/src/test/groovy/grails/plugin/formfields/DomainClassPropertyAccessorSpec.groovy index 21e54a5d..acf89670 100644 --- a/src/test/groovy/grails/plugin/formfields/DomainClassPropertyAccessorSpec.groovy +++ b/src/test/groovy/grails/plugin/formfields/DomainClassPropertyAccessorSpec.groovy @@ -256,10 +256,10 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory { person.address.country = "Australia" person.errors.rejectValue('address.country', 'not.inList') // http://jira.grails.org/browse/GRAILS-8480 - and: - def propertyAccessor = factory.accessorFor(person, "address.country") + when: + BeanPropertyAccessor propertyAccessor = factory.accessorFor(person, "address.country") - expect: + then: propertyAccessor.errors.first().code == "not.inList" propertyAccessor.invalid } From 1253e7f70e7eefb16274dc5bf01b85449f86d2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Berg=20Glasius?= Date: Tue, 2 Jan 2024 22:21:29 +0100 Subject: [PATCH 3/3] Updated documentation --- src/docs/asciidoc/changelog.adoc | 14 +++++++++++++- src/docs/asciidoc/installation.adoc | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/docs/asciidoc/changelog.adoc b/src/docs/asciidoc/changelog.adoc index e6e1d141..1f52f841 100644 --- a/src/docs/asciidoc/changelog.adoc +++ b/src/docs/asciidoc/changelog.adoc @@ -1,5 +1,17 @@ == Changelog +=== Version 5.0.0 - Grails 5.3.x + +In your `build.gradle` file, use: + +[source,groovy] +---- +implementation 'io.github.gpc:fields:5.0.0' +---- + +Changelog: +https://github.com/gpc/fields/compare/v4.0.0\...v5.0.0 + === Version 4.0.0 - Grails 4.1.x **Important** @@ -9,7 +21,7 @@ New `group` id. In your `build.gradle` file, use: -[source,groovy,subs="attributes"] +[source,groovy] ---- compile 'io.github.gpc:fields:4.0.0' ---- diff --git a/src/docs/asciidoc/installation.adoc b/src/docs/asciidoc/installation.adoc index 43b55ac1..b56d665c 100644 --- a/src/docs/asciidoc/installation.adoc +++ b/src/docs/asciidoc/installation.adoc @@ -4,6 +4,6 @@ The plugin is available on Maven Central and should be a dependency like this: [source,groovy,subs="attributes"] ---- dependencies { - compile 'io.github.gpc:fields:{version}' + implementation 'io.github.gpc:fields:{version}' } ----