From bc075d52236ed85a10376ea0ced9a2d4aac3b74e Mon Sep 17 00:00:00 2001 From: arievanwi Date: Sun, 10 Jan 2016 21:46:01 +0100 Subject: [PATCH 01/18] First version with SCR 1.3 changes applied. --- ca.ecliptical.pde.ds-feature/feature.xml | 2 +- ca.ecliptical.pde.ds.classpath/.classpath | 1 + .../META-INF/MANIFEST.MF | 7 +- .../annotations.jar | Bin 12375 -> 0 bytes .../annotationssrc.zip | Bin 12230 -> 18186 bytes .../build.properties | 3 +- ...gi.service.component.annotations-1.3.0.jar | Bin 0 -> 35919 bytes .../DSAnnotationClasspathContributor.java | 2 +- ca.ecliptical.pde.ds/META-INF/MANIFEST.MF | 2 +- .../pde/internal/ds/AnnotationProcessor.java | 458 +++++++++++++----- .../ecliptical/pde/internal/ds/Messages.java | 2 + .../pde/internal/ds/messages.properties | 3 +- 12 files changed, 360 insertions(+), 120 deletions(-) delete mode 100644 ca.ecliptical.pde.ds.classpath/annotations.jar create mode 100644 ca.ecliptical.pde.ds.classpath/org.osgi.service.component.annotations-1.3.0.jar diff --git a/ca.ecliptical.pde.ds-feature/feature.xml b/ca.ecliptical.pde.ds-feature/feature.xml index 70eb6bd..c0a86ae 100644 --- a/ca.ecliptical.pde.ds-feature/feature.xml +++ b/ca.ecliptical.pde.ds-feature/feature.xml @@ -12,7 +12,7 @@ diff --git a/ca.ecliptical.pde.ds.classpath/.classpath b/ca.ecliptical.pde.ds.classpath/.classpath index 64c5e31..c3a4501 100644 --- a/ca.ecliptical.pde.ds.classpath/.classpath +++ b/ca.ecliptical.pde.ds.classpath/.classpath @@ -1,5 +1,6 @@ + diff --git a/ca.ecliptical.pde.ds.classpath/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds.classpath/META-INF/MANIFEST.MF index 3058650..5dab17f 100644 --- a/ca.ecliptical.pde.ds.classpath/META-INF/MANIFEST.MF +++ b/ca.ecliptical.pde.ds.classpath/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: ca.ecliptical.pde.ds.classpath;singleton:=true -Bundle-Version: 1.0.0.qualifier +Bundle-Version: 1.3.0.qualifier Bundle-Activator: ca.ecliptical.pde.ds.internal.classpath.Activator Bundle-Vendor: %Bundle-Vendor Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.9.100,4.0.0)", @@ -11,4 +11,7 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.9.100,4.0.0)", org.eclipse.pde.core;bundle-version="[3.9.1,3.11.0)" Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy -Export-Package: ca.ecliptical.pde.ds.classpath;version="1.0.0" +Export-Package: ca.ecliptical.pde.ds.classpath;version="1.0.0", + org.osgi.service.component.annotations;version="1.3.0" +Bundle-ClassPath: org.osgi.service.component.annotations-1.3.0.jar, + . diff --git a/ca.ecliptical.pde.ds.classpath/annotations.jar b/ca.ecliptical.pde.ds.classpath/annotations.jar deleted file mode 100644 index 6630de342e38bdf2486be59a13b804a7c02ad158..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12375 zcmbVS1yEgC(tfxE2<{NvCAho0ySwYdA-DyC1$TE3?gW?M9^5r(aQn&Z?qnuAyEXft zSNGO=Ro(ra+qe73?N43`3>*~zfQAOl0km{sBlvpqA!ACFV~BSpTaT%vJ#>qO3HLHq8Bn>hNY!w=_cT%XsJfOjMXT`>lb$-0d9A=zomtuO(pqDPadRv-mG!NPiRqnmD>x7@7PRah$&rH?p&_ zx3e{|b^3#1@;|-$BL7<>f4Tn{@NdKU7tT*b16x}=Cj%!7J6qsyM*i_?emr#&07d5V zML7Wk0ATt1Mg)wUEL;qnOz4cP4S>L)XaOlO2Gqb^mBks$I(!k~v+>-|HH9M7_*C)7 z8@)={_)?Orlr0NEPb{yW06uX^(3-DbL4IU=yDBZt%e%S)vb09>S;2o3+^FjoCHsYL?PXbV_V8SIr`%fVG)@bGw&-7Id_=P72Nv{jVAQrPta!SIw) zRciF1cq=ooJ93@y%zl)<#k*U_-XRn^PP?V2fk6JfptgWUkNHR5d(1$0bY*9`>HSS3jfnhaca@7a?xE_z?UhbXfnsABFz)ZvWxPe8pwkskfMqt*m)C@~rg? zeV904k}cs770F~OCgS2~}z9&i&IIQ1XoN?^l zzxR1)Z6nUPRt6StOll>Td*%eV9^_RRZt^JPBg|Xg70#``OfEDv2^)%=}ys(Q8SZX{%Sn5QVAtg zj!|%PyKnxQSSfr%vL$6x0IGokp;`q1qFWLnvZWvvHQP9Qdgi(q&s!xLH(+A8c z&Z}m%9opmTWT@z^x!c#fF`?`8O<2&;Vd-|?fTAZ)?j9Ur95TyVYI8&Yrz+-kCjzu! zwmSd#w!(W6V{)iMPj)n9N02ykv_PcsUNd+rlOC_C0Wa}w278GpK-m~54gjB?k=4Qq zt`oQOis;msVHkmD1Vn6{jt8uktzW=>Oibi>>eZ2FD1!Yviak=iiBg&)WR&kID-WQ5 zj+!~DLy*>&nQ;yR0O0>$qQ=(L!pzz6w~Uszv$imD|7ZLt+sIAHqk13DIxiQ2!KV5` zxBJ=(6&3k@8Vony_-G;oDHWP2Nn0~tH}6dHM2$ryn!8@ZH{@n(0lmiC$=v%+7GPs6LYVnapR%x9D=)Nfj3?9an4^>&F#xsyX#&Y1Gh13_sk4RU-;XnPJ3?n{0O}Do!w+ny;7H$RjIY{h&G+||DzJ&uiqPp^o^+x8`u+|#o+misd&jbIvyqBq zWQ7LH2n&ZsC&j#GKwuJ^&6Jj*xa%6N3q`(Y_6k>QA5v+Gv(|+sUA}}%Ns2b<7>5vC zz`k@L@Z)(iS{$P6Mf5pDrT?;}le1kyD50*iW`%7lv-_eDUaKj4_6Gy+IWc1zgJgrV zX9<%JMk0MWWkZdTgq9N=YpNp~sVIz)Z^g-vvEOJGSPh6fQcTj|MJnQw(na}JG}|Iw z&k(A*T*EKtqVLj0`ixAbjA$2XIdM?;=5ykC6x1_H@l^@h2^gCsslAGphGgs|)hK4k zvI~rg@FWVx!G!3R5p)$lr(c1*4;!cK%7kY4FhO-!?{NO6zr522fzYSH3?O)gLsW?9 z#=oc7X`{K##72a9{*75q(>+WLT4`b{CLNo{FDd=We{aG)5?q5LBGf%h_Y{~v(8nZu z2k#KkfZE5ycfiY#9|HrcEaA8#e#MldCN-=>L5rLh8{Mmn_>kdp+wcU|6-g8nKM%9f zzju~RLow%-bZTHHynkWPaT9Cb7?E)hIWC&hjAD`7;Q{C3#4Fi*;)dKN%p&^wnpuVt zb^6&CF_Fm!9!ye46kTYx&wUe3fC73?=|*alqcnhkn! zkbcm~fK|)WL`_rEtd^%+&*sI$eQwwJ zF4zV`1-k;XiTW;W<{Yety}$SMpPeZ>x+8r3;>7=Pk(aSEwlK9YG5)WMd=A=c+TRxW z6yC%*YekouSC3Zs475STZi|XD4lqtL}|k85#!`eMru1@nY9Lj zb{pt-Iu{!*l++UJnenrSAM~8FcY{M_IqBP6>It-?tVA&_ln5Jwld8?MN<9TzJ{G{$ z1^A3(nAK{|`GuUljf6=Y@e|&myzg9Qz9;V1FqfXCMvymSl*}lM0$)E@ZX}w?S!VX;WngAPYhi0@_YZH%47b5|y+sUOA6xH7 zMuP(-kHa&kOj@f#7$#g2sUNgQh3k|L%K%)R;=hW@?>;cT#@&kcaF~a~ zt-v)&{chM2Ddu8MEA|N)1#Qp>R`9cJK(RTgYTXb^GWwk!-t20!$Ejo=${+Fe5FO(DSkwrMOKHYgh`wrMM){Y z@pIpGcn3;#SwwM*0K*L_{WoUERUMBgpPsV2RbF~JF+fzq6iaM4a zrr+}W1QDqce>R8U4h`LdTMk{5xEtl2DQn1ZKX+wz0|2;sClM5r`bId8diwwklC5D- zwn`b=I43J+GfPDS2f|Wcx_@m@3!IJKph1Tx*c{%{?lY@ci&vYNo9{dBqmWfX=%~}1 zU*;80Hh|aR%l3r;5&#hR``Pe&@CX?=8e7;JSX(&#YyYBRqlB!0>aDthzT~A5ANYl_ z9DqW?#1c)=>tqT6jKL}}O+!=DgmO(aaVR)>%<35`DCiq|EGht2P8Xd;Jlc=Bk1hg7 z7g|mauNyJ}AJwnBBbopi`YRz0Z!#d?`a`(TFphl;%?dRGQB;B}6!{RQZfYqiEZzew zFck?D*-EmfXBg!aP-rJHkLy z6Ei>i+DVJ8^e)Ww1gAiiE$x~TL0bs-}0f*IVHc*54INv-UiWs@F* zblW)h*QswG>($7^qhe8CgSUn~@X6ImX=+rLXttFtKpI&oHOqVp4P>)P3XVlW+~;qd z$WCO;=d^>@4Ao%I|PNnj1=Z-qhio0Z_u(AQI|YZR9>;h&u>ZL z6+#7>vHYs?ZbTm#!Wq3A&OlM9iZ79iduLd_01wCGZj$ zFj#W z4kZyQj|-%o^$wF4`LpHs7!36+&G5Lk9xI5!ek##herY4(Cd5sRsh;o9Oy3|;NU&Gn6Mo;mzXoVB-%D_u|6D_QQ`)_TC4k2 z+wr+Bl#)!5p|gQ4*APhx^ddc`81pj_%~e5uBEL@#rrW~%C*SD-M zL6iz)4{v8S`i&TJ!&_)S!t=g?^F&U+o3ILC4{r?lTWKSruQ+)(NFVAqcE{1mU9JU& ze1slwk;$EOBpPLMzruCnkrlo>z>R=AgJnRLLb`oF!9;Q%t4HugPe0Eh(MKHSi~N;! zV0<=@XzLra9ws_U(G8A65Qd0N1F0<8gyhZJvFJ)Etws{s6@R5u=qRm0t=gg0FR#V9 zQjn)Unu(|9a5<_-v>-L@-^iUCk?&O;iIa<~$r47=H_y`Oe~>l$81{zbso)EZW6a1i z#Gf}DX-Covi7%0!^AhQT|L;hbv;Xli>z{{SB`rA=0o12p+m^*o=-OGDozz7(O*A5( zU%^l6w~@d{qo*KdCYTgS)z=uekp45`&3xj{zSI-?zB}YiW7@rYxBKNNvxCnYY##R`4x2UZ&)IGNe$qKQ~>8+bCjX+>lmjYHyuP@b*i< z%T#K(ej&SljhC8QKgWIwj^SqkvksxU=9XijdV_!6RCOD?%Xu|%=jTMv(6gct(vGJeXWSfs8{xUwED*JvkR=S&K#!p<{<5M%$$C{*z3JcQ2J zQW@LJ(VXr&2V5$ zkq7PSjvXGyjrm-w226>OaQj(YB!=6d{2qjQKc?<}V+bjp5N3h%?UK!cA1Ua5Tcr^q zo5izp=EX8GUU2P4HL=1F+m=ArckYK=_e%)oRYAj6dd}shz~aUGrmyh_ipz9dT3Uls zK3x3IZJ$0Sd)R_=gdS0BGzKl|i^pC@*FGSV>#vTxMh=*LGM^(cGN6&U zHNrgEqR*oLe)U>l@SWIq+%THG7DfAzycOAQ9T-^$O|pVaG>sfweXiiBD()^)b7cx-Vi}TGS^7k3+!6S&Kv9lLxGeS9RSL0L2Ms zirFjWbdi~9Aei&|k6Wc2NrBqhjbNMC0DvwH008w8_R&kh@ z0woK`f^=pJoZa`;TfyZ*7Q#)oWU(QnRRj%?RReScVUL$QiEVy)#;$j&G6gIWM+-H( z=jkVDy|2!iIz;G)(~(b{i#ZE*xi7Z(;+(%aZ5-Yn3O>lVLwrS|lo+PrB*15R?$$85vUpctG^K=DX zuG4z!j4P=PUp_9MhrGUlhq1-%Yse44eQL4FullLO6C2CK$*&*U_0pT?1#nl z8`&gmUURq6IHS_$b#;0pIaF#HpDK?QoWKRwAy|1B^n-PRE%u`iGtUK&t!Y5&;B;JY z>*hLSbQdj;fki?kej5|w3W@LUk&Qy2NWR5KlS;s7`eLnV(D!wM=?awN+QCEOm07?N zmczcmR&4H^T5B*;`}}FLwtT;pXj76qzcU>&cZ6l2{T(wtu%3_31s0z6do}lgb|V(p<#$D79+nqwK`l z=lj}9l7%LdE9_jDJZW-*Dniq9!+pGd>nZ>}y+=q<-Ra-D=&n%&VhFjM1 z32P8FIxvSRz}%_S!Sytk>9VT-)UlS3Js~lW4;2G*OK)U zM7DiN^AW-WUZasU1)rsr=1uj|AVMty45-2chnHfoZ;l=Ib2YkRmAkk$15Z|>gOjv) zs;-eBmg49e;HwybnQtXI)ab-3*}KlYZ}JGcwxv|Gz4m(-Mn23}Q!o@q>r#_R_Rz$% z`M$?p42Gap`w!Kc8X(c!;>!avDCd0L`JfZ1U{!IvamGDCzP9?1ZtPt+TCn)D#;P1} z>RJPW0$TJNpa@y`CX3ypRft)5U1=B_vEl7%RTso0N=8?SgRp8t2T)ZT0m$ZvY+R9m zrOj6Ohy6B^dI5PaVm!{uT;($R*B`t?@%yNj!j^)Xs#f%2#-e%6bOVYrmZ=9&J3&S_|v# zBY8_C6$YZP+BqxZYr|#>;No@zUlEuPNs0B~MKdSvIz!P!YL(tTUa*WoK@&Fliof|T zw=;<)X~W|D++;@g%SN)PF8bU(^l+6pOSJDS025;`D8iZ|@(O*6ZA1hXS7miU$S z7#BWOUQ_BAt8GzwM$BYk0aKe=9Q!3lC*^I6{)E%Xse!0Kw_@hve*M6G4I%C>1?|e= zi4O=L8{)4}1T$rhDCFt3zt^x`ZiHu2sLDl+Jho5Dk6>`xqF{S;&jk}v{CrkMhfg|$1x(1O^OdPr*e0rQoPiD zvEl5-PviaNK<`Zd!GbB7R1I!CnzBR;mN-G6?`Y3PC13)ve}>5>2ZXPLk^4{v3JwZg2-e>Y=KfDiq2F9u0F2^XD|ArMH}(-oxx0! zvY+(BmqAbqW?e-D@<*-uj%r)tz1w^*veK~4%xB0DvmJ{!76ozNN8zQJEiq$!S`*oPFCBXaE2Snim z-aG>m6t}`vS+qV`8k|fgFKo+DyIs{XGah?-*@yXU6>6N~&npksF6>oAFoWed4mANs z&a#>^3mRJdnd1`_c$Rt@HyFprU-hRb^^icGd}D-F<%T0M0>K!Hw^9G4Nxgiqv% z!O*nISlnbOu}q@~#r9_svQn4R<};-Js^2^89>!;CW=0;*zg@z8JaHiKH2QkW6YXer zzqJrA(3vdFQB8A8je@2r2g4hOc9}%EH?2`aL?MxOVQR=-gv59%um|0nIM0FtT;!akhkVlbQ1kFVuxZ^tL+>9zTo@dD`cX zO)_Yw%I(#?#qlO<9t&~9*N|pq%5O|yk)Zbg3mOFDs`O{kA$zF^yTI|c#MSc1DdYEe zt_Gocg{GqqGFyIH`|Mi*_(Uv~1M&q2H%ml4oYJNj)z$+gT|QbzuIHg&^1x@>g4#(? zVC&n##J0?GykYxsRPviGt-);4g{1-L$It#;Q4A4qxWSnE!8L=zQ&fA&Nl^1=>`(J= z<(9S5SQHMZLhC21GGIC<)~JCHuxGmiDt$3&qO+U^O=g&0*x%Bd7!4T~bYhCf%$4$L zi;Fe5xhKN5rkau@NnSUM7Pw3J#V(dephcG9m<!cEGIZ zvQ+c4VZ7li%85)Xz&FTMG`g2Uu6gy5eU%-zX23crKB77USBJD9iVOd>W72`m`Tm8^ zREm3wh@~k=H&Ifv>iSj8T;&(v@QrWqGl>!F1#nX?Qs3|rkQ7HEV>uT_9;$Z^9mIaD@B!jA;|D$!|o|5ul1^QTJpSU@#Ol7xz$&nJ(durp7*%C|@az&vFq>b8e$}je7?5*`c!5W(${Ae7YemZk^Lz2rf z{dBS({f(cGzYYQeOioEE7+ck}yHR+&zHh7eh*XL{+MogNwT|K?sB>@jXNT|!Y$8wCYW*+w+mH8$pk?hIP1!=ibh3J9ALzx_#d^5OA;7A2(!|yc=8E zH1vRV+lyV0cIf=i_^dNdhx}|zOK7s)5Zq=mb}W-XPJ8%?peXqV(w>o>d~O>j1a>5R zw$3`2z@yPP&iX;PgQtx6g6dn5Lkp(i>gz-fF9fn)k(0WmypM{Ja~D(^^hHcLNM_k#+Jdke-CI}k_loxg4>oIdBis#w4D3tB*^Ah;9CqDN?=>p#)7sn=O)TRO{eX3gv+QnW+L$5- zN0-rt@>+Af8mAkig{9!@Az3D#w||cHo$f{)BFE>@usKDx+-*yeRF5O%rfed&9T~({ zh|aZF%!7Pe z8!w2^4rnNOkE5IArY>(joh}^KcM1-tGjK!XOYClaN;j?OT(lH%1CUxCd!WPcj`QUZXBx zd#ufYd8*{hc*QBKe&0;;L{lv-p#s6&L>OhdnAMa`awaYqnUGrK2-Rza@TsP=owY$x z&T$ahXlv}j2j1|$53Hg(?7o$1YU*lZZIEIe^tN{EG248rS+6WK0ihnJmGoPc$Ln@^ zL%b`bpI09-=qMhnP^T;evt}Sa^$Fte1{RCPaNcT`7C3^!f|GCsJVDw~q6zODCF+?W z-Iz13mo5{6qhyzy+N=g&G8Pu@!s;?BeHSd=DIM`C=rZJwHqqjZ?qf)vblZ#^LdI?F)?OKUSDu zPUHc%?tDUE06^eN@#2N!1u!tQb9SOLce1gLP+0|HvmttSbQlf?^r!owKorVY=?Cji zHgB?HfsK4uSfWicOm~WL8b8!q)mb(;BAFUQX@0!?c6==%?cM0!q5k}60OM1(y8Jn0 zB(-G`;ya|E7cPBZqsD4&WT_KH&v`qW6qyJG%X8M3ee$9#2iDcEoYKPgG18K7$YF>j zAl?--2F+DuSug?4N`%JW`fD*xGYJ~MvKR+B;deXLcyqap5advI)VYIcraaa*w7#)c zt@cw0hdLB=-?LQeGfx1x_tylKV;5dQ;Pl7Ir_ZH1i@ywV;Y3uT(G{;tB3m!}{%hmMBYK*zjwjX&Pf<|NCi0&iOaWvT! zUSxWvf=x}UaO4AV#!TVg62@>W$#~M#tp>8=M-_hvEq3B6t?=x6!bmv6%TNwxi4BBrv&BU6)yJ|B+&1X58}g+@f3(#0m++mSa)g zE14V$aVy7Z#fxU@l39M`@)Hz}l33`hh8WBGZTw*&J>Co;B7FRW0 zy}tDtD@mNX?fagJVT?&48h=T_aXd7|t z;{i;;RRuK?8kFgZ^|L*o816+3y3=_NoFl0=d*roOWcA!}6Z+U?s{Uq^mlxGexX)^fTI4OZ~g}c=u5u+q5X*u z`m5@%e9)hIv@hM?RsX;a{b2$2M^66-X6H`}`7hmn@I3#s#t)|F&({Ca{hes}-++EF zKYzC4m+tRs|KexObk7e>C)0%+Q~PRR6o7Kl4O?zQVs^ivBd3 z`uC0gnK$}l-2aM_zamlogxGwk82lFp{BhjBP%3}L_?2?^69em|BJsB|{sRH;SF~Sg zOh3_>UV`&)qWwy0`W56?de2V~-f diff --git a/ca.ecliptical.pde.ds.classpath/annotationssrc.zip b/ca.ecliptical.pde.ds.classpath/annotationssrc.zip index bde5c241712b080f205c4bd077cc80264ba98d6c..df492b461d51f95f999396ae0401a38eb6467997 100644 GIT binary patch literal 18186 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

Ow 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%0AuD@1*$);=BD6uvLdFPQNyhh4b?z5ftg1mS!o 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 literal 0 HcmV?d00001 diff --git a/ca.ecliptical.pde.ds.classpath/src/ca/ecliptical/pde/ds/internal/classpath/DSAnnotationClasspathContributor.java b/ca.ecliptical.pde.ds.classpath/src/ca/ecliptical/pde/ds/internal/classpath/DSAnnotationClasspathContributor.java index d312758..fa1c421 100644 --- a/ca.ecliptical.pde.ds.classpath/src/ca/ecliptical/pde/ds/internal/classpath/DSAnnotationClasspathContributor.java +++ b/ca.ecliptical.pde.ds.classpath/src/ca/ecliptical/pde/ds/internal/classpath/DSAnnotationClasspathContributor.java @@ -56,7 +56,7 @@ public List getInitialEntries(BundleDescription project) { if (autoClasspath) { Bundle bundle = ctx.getBundle(); try { - URL fileURL = FileLocator.toFileURL(bundle.getEntry("annotations.jar")); //$NON-NLS-1$ + URL fileURL = FileLocator.toFileURL(bundle.getEntry("org.osgi.service.component.annotations-1.3.0.jar")); //$NON-NLS-1$ if ("file".equals(fileURL.getProtocol())) { //$NON-NLS-1$ URL srcFileURL = FileLocator.toFileURL(bundle.getEntry("annotationssrc.zip")); //$NON-NLS-1$ IPath srcPath = "file".equals(srcFileURL.getProtocol()) ? new Path(srcFileURL.getPath()) : null; //$NON-NLS-1$ diff --git a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF index cedfa36..ac75af0 100644 --- a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF +++ b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF @@ -22,4 +22,4 @@ Bundle-ActivationPolicy: lazy Export-Package: ca.ecliptical.pde.internal.ds;x-internal:=true Import-Package: ca.ecliptical.pde.ds.classpath;version="[1.0.0,2.0.0)", org.osgi.service.component;version="[1.2.0,2.0.0)", - org.osgi.service.component.annotations;version="[1.2.0,2.0.0)" + org.osgi.service.component.annotations;version="[1.3.0,2.0.0)" diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index 7595f54..72db636 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -17,6 +17,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringReader; +import org.eclipse.jdt.core.dom.ParameterizedType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -33,6 +34,7 @@ import java.util.concurrent.CountDownLatch; import java.util.regex.Pattern; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.ITextFileBufferManager; @@ -47,11 +49,14 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.compiler.BuildContext; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.dom.AST; @@ -65,6 +70,7 @@ import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.IMemberValuePairBinding; import org.eclipse.jdt.core.dom.IMethodBinding; @@ -76,6 +82,7 @@ import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentRewriteSession; import org.eclipse.jface.text.DocumentRewriteSessionType; @@ -120,6 +127,7 @@ import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.component.annotations.ReferenceScope; @SuppressWarnings("restriction") public class AnnotationProcessor extends ASTRequestor { @@ -247,6 +255,8 @@ class AnnotationVisitor extends ASTVisitor { private static final String NAMESPACE_1_2 = "http://www.osgi.org/xmlns/scr/v1.2.0"; //$NON-NLS-1$ + private static final String NAMESPACE_1_3 = "http://www.osgi.org/xmlns/scr/v1.3.0"; //$NON_NLS-1$ + private static final String ATTRIBUTE_COMPONENT_CONFIGURATION_PID = "configuration-pid"; //$NON-NLS-1$ private static final String ATTRIBUTE_REFERENCE_POLICY_OPTION = "policy-option"; //$NON-NLS-1$ @@ -471,9 +481,8 @@ private void processComponent(TypeDeclaration type, ITypeBinding typeBinding, An ITextFileBuffer buffer = bufferManager.getTextFileBuffer(filePath, LocationKind.IFILE); if (buffer.isDirty()) buffer.commit(null, true); - + IDocument document = buffer.getDocument(); - final DSModel dsModel = new DSModel(document, true); dsModel.setUnderlyingResource(file); dsModel.setCharset("UTF-8"); //$NON-NLS-1$ @@ -884,7 +893,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } } - boolean requiresV12 = false; + int requiredVersion = 1; String activate = null; Annotation activateAnnotation = null; String deactivate = null; @@ -898,9 +907,31 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding HashMap refMap = new HashMap(refElements.length); for (IDSReference refElement : refElements) { - refMap.put(refElement.getReferenceBind(), refElement); + refMap.put(refElement.getName(), refElement); } + // Process the field declarations to get the field injection points. + for (FieldDeclaration field : type.getFields()) { + for (Object modifier : field.modifiers()) { + if (!(modifier instanceof Annotation)) + continue; + Annotation fieldAnnotation = (Annotation) modifier; + IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding(); + if (fieldAnnotationBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for annotation: %s", fieldAnnotation)); //$NON-NLS-1$ + continue; + } + String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName(); + if (REFERENCE_ANNOTATION.equals(annotationName)) { + requiredVersion = Math.max(requiredVersion, 3); + VariableDeclarationFragment fragment = (VariableDeclarationFragment) field.fragments().get(0); + processReference(field, fragment.getName().getIdentifier(), fieldAnnotation, fieldAnnotationBinding, + refMap, dsFactory, references, referenceNames, problems); + } + } + } + for (MethodDeclaration method : type.getMethods()) { for (Object modifier : method.modifiers()) { if (!(modifier instanceof Annotation)) @@ -921,7 +952,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding if (activate == null) { activate = method.getName().getIdentifier(); activateAnnotation = methodAnnotation; - validateLifeCycleMethod(methodAnnotation, "activate", method, problems); //$NON-NLS-1$ + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "activate", method, problems)); //$NON-NLS-1$ } else if (!errorLevel.isNone()) { reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier()); if (activateAnnotation != null) { @@ -937,7 +968,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding if (deactivate == null) { deactivate = method.getName().getIdentifier(); deactivateAnnotation = methodAnnotation; - validateLifeCycleMethod(methodAnnotation, "deactivate", method, problems); //$NON-NLS-1$ + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "deactivate", method, problems)); //$NON-NLS-1$ } else if (!errorLevel.isNone()) { reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, method.getName().getIdentifier()); if (deactivateAnnotation != null) { @@ -953,7 +984,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding if (modified == null) { modified = method.getName().getIdentifier(); modifiedAnnotation = methodAnnotation; - validateLifeCycleMethod(methodAnnotation, "modified", method, problems); //$NON-NLS-1$ + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "modified", method, problems)); //$NON-NLS-1$ } else if (!errorLevel.isNone()) { reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, method.getName().getIdentifier()); if (modifiedAnnotation != null) { @@ -971,7 +1002,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding if (debug.isDebugging()) debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ } else { - requiresV12 |= processReference(method, methodBinding, methodAnnotation, methodAnnotationBinding, refMap, dsFactory, references, referenceNames, problems); + requiredVersion = Math.max(requiredVersion, processReference(method, methodBinding, methodAnnotation, methodAnnotationBinding, refMap, dsFactory, references, referenceNames, problems)); } continue; @@ -1007,7 +1038,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding removeAttribute(component, ATTRIBUTE_COMPONENT_CONFIGURATION_PID, null); } else { component.setXMLAttribute(ATTRIBUTE_COMPONENT_CONFIGURATION_PID, configPid); - requiresV12 = true; + requiredVersion = Math.max(2, requiredVersion); } if (references.isEmpty()) { @@ -1042,12 +1073,20 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding impl.setClassName(implClass); - String xmlns = NAMESPACE_1_1; + + String neededXmlns = NAMESPACE_1_1; + if (requiredVersion > 2) { + neededXmlns = NAMESPACE_1_3; + } + else if (requiredVersion > 1) { + neededXmlns = NAMESPACE_1_2; + } + String xmlns = neededXmlns; if ((value = params.get("xmlns")) instanceof String) { //$NON-NLS-1$ xmlns = (String) value; - validateComponentXMLNS(annotation, xmlns, requiresV12, problems); - } else if (requiresV12) { - xmlns = NAMESPACE_1_2; + validateComponentXMLNS(annotation, xmlns, neededXmlns, problems); + } else { + xmlns = neededXmlns; } component.setNamespace(xmlns); @@ -1264,8 +1303,9 @@ private void validateComponentPropertyFiles(Annotation annotation, IProject proj } } - private void validateComponentXMLNS(Annotation annotation, String xmlns, boolean requiresV12, Collection problems) { - if (!errorLevel.isNone() && (requiresV12 || !NAMESPACE_1_1.equals(xmlns)) && !NAMESPACE_1_2.equals(xmlns)) + private void validateComponentXMLNS(Annotation annotation, String xmlns, String requiredNs, Collection problems) { + + if (!errorLevel.isNone() && requiredNs.compareTo(xmlns) > 0) reportProblem(annotation, "xmlns", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentDescriptorNamespace, xmlns), xmlns); //$NON-NLS-1$ } @@ -1274,18 +1314,14 @@ private void validateComponentConfigPID(Annotation annotation, String configPid, reportProblem(annotation, "configurationPid", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentConfigurationPid, configPid), configPid); //$NON-NLS-1$ } - private void validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method, Collection problems) { - if (errorLevel.isNone()) - return; - + private int validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method, Collection problems) { IMethodBinding methodBinding = method.resolveBinding(); if (methodBinding == null) { if (debug.isDebugging()) debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ - return; + return 0; } - String returnTypeName = methodBinding.getReturnType().getName(); if (!Void.TYPE.getName().equals(returnTypeName)) reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodReturnType, methodName, returnTypeName), returnTypeName); @@ -1294,13 +1330,15 @@ private void validateLifeCycleMethod(Annotation annotation, String methodName, M if (paramTypeBindings.length == 0) // no-arg method - return; + return 1; - // every argument must be either Map, ComponentContext, or BundleContext + int requiredSpecVersion = 1; + // every argument must be either Map, ComponentContext, BundleContext or component property type boolean hasMap = false; boolean hasCompCtx = false; boolean hasBundleCtx = false; boolean hasInt = false; + boolean hasComponentPropertyType = false; for (ITypeBinding paramTypeBinding : paramTypeBindings) { String paramTypeName = paramTypeBinding.getErasure().getQualifiedName(); boolean isDuplicate = false; @@ -1320,6 +1358,14 @@ private void validateLifeCycleMethod(Annotation annotation, String methodName, M isDuplicate = true; else hasBundleCtx = true; + } else if (paramTypeBinding.isAnnotation()) { + if (hasComponentPropertyType) { + isDuplicate = true; + } + else { + hasComponentPropertyType = true; + requiredSpecVersion = 3; + } } else if ("deactivate".equals(methodName) //$NON-NLS-1$ && (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) { if (hasInt) @@ -1333,6 +1379,7 @@ private void validateLifeCycleMethod(Annotation annotation, String methodName, M if (isDuplicate) reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_duplicateLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName); } + return requiredSpecVersion; } private boolean hasLifeCycleMethod(ITypeBinding componentClass, String methodName) { @@ -1360,6 +1407,13 @@ private boolean hasLifeCycleMethod(ITypeBinding componentClass, String methodNam isInvalid = true; else hasCompCtx = true; + } else if (paramTypeBinding.isAnnotation()) { + if (hasCompCtx) { + isInvalid = true; + } + else { + hasCompCtx = true; + } } else if (BundleContext.class.getName().equals(paramTypeName)) { if (hasBundleCtx) isInvalid = true; @@ -1387,11 +1441,270 @@ private boolean hasLifeCycleMethod(ITypeBinding componentClass, String methodNam return false; } - private boolean processReference(MethodDeclaration method, IMethodBinding methodBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, IDSDocumentFactory factory, Collection collector, Map names, Collection problems) { + private static Map mapAnnotationBinding(IAnnotationBinding annotationBinding) { HashMap params = new HashMap(); for (IMemberValuePairBinding pair : annotationBinding.getDeclaredMemberValuePairs()) { params.put(pair.getName(), pair.getValue()); } + return params; + } + + private static String cardinality(Map params) { + String cardinality = null; + ReferenceCardinality cardinalityLiteral = null; + Object value = params.get("cardinality"); + if (value instanceof IVariableBinding) { + IVariableBinding cardinalityBinding = (IVariableBinding) value; + cardinalityLiteral = ReferenceCardinality.valueOf(cardinalityBinding.getName()); + } + else if (value instanceof ReferenceCardinality) { + cardinalityLiteral = (ReferenceCardinality) value; + } + if (cardinalityLiteral != null) + cardinality = cardinalityLiteral.toString(); + return cardinality; + } + + private static String policy(Map params) { + String policy = null; + Object value; + if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$ + IVariableBinding policyBinding = (IVariableBinding) value; + ReferencePolicy policyLiteral = ReferencePolicy.valueOf(policyBinding.getName()); + if (policyLiteral != null) + policy = policyLiteral.toString(); + } + return policy; + } + + private static String referencePolicyOption(Map params) { + String policyOption = null; + Object value; + if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$ + IVariableBinding policyOptionBinding = (IVariableBinding) value; + ReferencePolicyOption policyOptionLiteral = ReferencePolicyOption.valueOf(policyOptionBinding.getName()); + if (policyOptionLiteral != null) { + policyOption = policyOptionLiteral.toString(); + } + } + return policyOption; + } + + private String target(Map params, Annotation annotation, Collection problems) { + String target = null; + Object value; + if ((value = params.get("target")) instanceof String) { //$NON-NLS-1$ + target = (String) value; + validateReferenceTarget(annotation, target, problems); + } + return target; + } + + private static String scope(Map params) { + String scope = null; + Object value; + if ((value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$ + IVariableBinding scopeBinding = (IVariableBinding) value; + ReferenceScope referenceScope = ReferenceScope.valueOf(scopeBinding.getName()); + if (referenceScope != null && !ReferenceScope.BUNDLE.equals(referenceScope)) { + scope = referenceScope.toString(); + } + } + return scope; + } + + /* + * Common handling of the reference processing. Does the parts that are common to both field and + * method based references and constructs a reference for it. + */ + private IDSReference reference(String defaultName, String service, + Annotation annotation, Map params, Map refMap, IDSDocumentFactory factory, Collection collector, + Map names, Collection problems) { + String name = null; + Object value; + if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$ + name = (String) value; + } + else { + name = defaultName; + } + IDSReference reference = refMap.remove(name); + if (reference == null) { + reference = factory.createReference(); + } + collector.add(reference); + if (!errorLevel.isNone()) { + if (names.containsKey(name)) { + reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ + Annotation duplicate = names.put(name, null); + if (duplicate != null) + reportProblem(duplicate, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ + } else { + names.put(name, annotation); + } + } + + if (name == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_NAME, null); + } else { + reference.setReferenceName(name); + } + + if (service == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_INTERFACE, null); + } else { + reference.setReferenceInterface(service); + } + String cardinality = cardinality(params); + String policy = policy(params); + String target = target(params, annotation, problems); + String policyOption = referencePolicyOption(params); + String scope = scope(params); + if (cardinality == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_CARDINALITY, IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_ONE); + } else { + reference.setReferenceCardinality(cardinality); + } + + if (policy == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_POLICY, IDSConstants.VALUE_REFERENCE_POLICY_STATIC); + } else { + reference.setReferencePolicy(policy); + } + + if (target == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_TARGET, null); + } else { + reference.setReferenceTarget(target); + } + + if (policyOption == null) { + removeAttribute(reference, ATTRIBUTE_REFERENCE_POLICY_OPTION, VALUE_REFERENCE_POLICY_OPTION_RELUCTANT); + } else { + reference.setXMLAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION, policyOption); + } + + if (scope != null) { + reference.setXMLAttribute("scope", scope); + } + return reference; + } + + private Type containedType(Type type) { + Type contained = null; + if (type.isParameterizedType()) { + ParameterizedType thisType = (ParameterizedType) type; + contained = (Type) thisType.typeArguments().get(0); + } + return contained; + } + + private ITypeBinding getFieldCollectionType(Type type, StringBuffer fct) { + if (type == null) return null; + ITypeBinding binding = type.resolveBinding(); + String containedName = binding.getBinaryName(); + Type serviceType = null; + String fieldCollectionType = null; + if (Map.class.getName().equals(containedName)) { + fieldCollectionType = "properties"; + } + else if (ServiceReference.class.getName().equals(containedName)) { + fieldCollectionType = "reference"; + serviceType = containedType(type); + } + else if (Map.Entry.class.getName().equals(containedName)) { + fieldCollectionType = "tuple"; + } + else if ("org.osgi.service.component.ComponentServiceObject".equals(containedName)) { + fieldCollectionType = "serviceobjects"; + serviceType = containedType(type); + } + else { + serviceType = type; + } + if (fieldCollectionType != null && fct != null) { + fct.append(fieldCollectionType); + } + return (serviceType == null) ? null : serviceType.resolveBinding(); + } + + private void processReference(FieldDeclaration field, String fieldName, + Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, + IDSDocumentFactory factory, Collection collector, Map names, + Collection problems) { + // Map the annotation properties. + Map params = mapAnnotationBinding(annotationBinding); + // Check the service type of the field. Can be either a map, collection, entry or any other object type. + Type type = field.getType(); + ITypeBinding binding = type.resolveBinding(); + // Now the processing of the type of the field. There are a couple of options, but + // the first discrimination is made between collections and "normal" objects. + ITypeBinding defaultService = null; + IType itype = (IType) binding.getJavaElement(); + // Try to determine whether the field is a collection. + boolean isCollection = false; + try { + ITypeHierarchy typeHierarchy = itype.newTypeHierarchy(new NullProgressMonitor()); + for (IType t : typeHierarchy.getSuperInterfaces(itype)) { + if (t.getFullyQualifiedName().equals(Collection.class.getName())) { + isCollection = true; + } + } + } catch (Exception exc) { + exc.printStackTrace(); + } + StringBuffer fieldCollectionType = new StringBuffer(); + if (isCollection) { + if (cardinality(params) == null) { + params.put("cardinality", ReferenceCardinality.AT_LEAST_ONE); + } + // Collection type. Check whether it is a parameterized type with one parameter containing the + // service type or one of the other options for injecting a service. + defaultService = getFieldCollectionType(containedType(type), fieldCollectionType); + } + else { + // No collection. If it is not one of the known types, like service reference, etc., + // just get the type of the field itself. + defaultService = getFieldCollectionType(type, null); + } + // Check the service specification. If the service is specified, this is the easiest solution, since the programmer + // specifies what the service is. + Object s = params.get("service"); + if (s == null && defaultService == null) { + reportProblem(annotation, "service", problems, Messages.AnnotationProcessor_unknownServiceType); + } + else { + // OK, valid. Check whether the default service type we determined matches the service + // specification. + String serviceName; + if (s == null || !(s instanceof ITypeBinding)) { + serviceName = defaultService.getBinaryName(); + } + else { + ITypeBinding serviceType = (ITypeBinding) s; + // Check the type compatibility and report a problem if not. + if (!defaultService.isAssignmentCompatible(serviceType)) { + reportProblem(annotation, "service", problems, + NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, + defaultService.getName(), serviceType.getName())); + } + serviceName = serviceType.getBinaryName(); + } + // Create the service reference. + IDSReference reference = reference(fieldName, serviceName, + annotation, params, refMap, factory, collector, names, problems); + // Add some attributes because of field injection. Note that these attributes + // are not yet known in the current eclipse version (Mars) and therefore used by hand. + reference.setXMLAttribute("field", fieldName); + if (fieldCollectionType.length() > 0) { + reference.setXMLAttribute("field-collection-type", fieldCollectionType.toString()); + } + } + } + + private int processReference(MethodDeclaration method, IMethodBinding methodBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, IDSDocumentFactory factory, Collection collector, Map names, Collection problems) { + Map params = mapAnnotationBinding(annotationBinding); ITypeBinding[] argTypes = methodBinding.getParameterTypes(); @@ -1432,9 +1745,7 @@ private boolean processReference(MethodDeclaration method, IMethodBinding method String methodName = methodBinding.getName(); String name; - if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$ - name = (String) value; - } else if (methodName.startsWith("bind")) { //$NON-NLS-1$ + if (methodName.startsWith("bind")) { //$NON-NLS-1$ name = methodName.substring("bind".length()); //$NON-NLS-1$ } else if (methodName.startsWith("set")) { //$NON-NLS-1$ name = methodName.substring("set".length()); //$NON-NLS-1$ @@ -1444,39 +1755,6 @@ private boolean processReference(MethodDeclaration method, IMethodBinding method name = methodName; } - if (!errorLevel.isNone()) { - if (names.containsKey(name)) { - reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ - Annotation duplicate = names.put(name, null); - if (duplicate != null) - reportProblem(duplicate, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ - } else { - names.put(name, annotation); - } - } - - String cardinality = null; - if ((value = params.get("cardinality")) instanceof IVariableBinding) { //$NON-NLS-1$ - IVariableBinding cardinalityBinding = (IVariableBinding) value; - ReferenceCardinality cardinalityLiteral = ReferenceCardinality.valueOf(cardinalityBinding.getName()); - if (cardinalityLiteral != null) - cardinality = cardinalityLiteral.toString(); - } - - String policy = null; - if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$ - IVariableBinding policyBinding = (IVariableBinding) value; - ReferencePolicy policyLiteral = ReferencePolicy.valueOf(policyBinding.getName()); - if (policyLiteral != null) - policy = policyLiteral.toString(); - } - - String target = null; - if ((value = params.get("target")) instanceof String) { //$NON-NLS-1$ - target = (String) value; - validateReferenceTarget(annotation, target, problems); - } - String unbind; if ((value = params.get("unbind")) instanceof String) { //$NON-NLS-1$ String unbindValue = (String) value; @@ -1507,15 +1785,6 @@ private boolean processReference(MethodDeclaration method, IMethodBinding method } } - String policyOption = null; - if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$ - IVariableBinding policyOptionBinding = (IVariableBinding) value; - ReferencePolicyOption policyOptionLiteral = ReferencePolicyOption.valueOf(policyOptionBinding.getName()); - if (policyOptionLiteral != null) { - policyOption = policyOptionLiteral.toString(); - } - } - String updated; if ((value = params.get(ATTRIBUTE_REFERENCE_UPDATED)) instanceof String) { //$NON-NLS-1$ String updatedValue = (String) value; @@ -1548,65 +1817,28 @@ private boolean processReference(MethodDeclaration method, IMethodBinding method updated = updatedMethod.getName(); } - IDSReference reference = refMap.remove(methodName); - if (reference == null) { - reference = factory.createReference(); - } - - collector.add(reference); + IDSReference reference = this.reference(name, service, annotation, params, refMap, + factory, collector, names, problems); reference.setReferenceBind(methodName); - if (name == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_NAME, null); - } else { - reference.setReferenceName(name); - } - - if (service == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_INTERFACE, null); - } else { - reference.setReferenceInterface(service); - } - - if (cardinality == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_CARDINALITY, IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_ONE); - } else { - reference.setReferenceCardinality(cardinality); - } - - if (policy == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_POLICY, IDSConstants.VALUE_REFERENCE_POLICY_STATIC); - } else { - reference.setReferencePolicy(policy); - } - - if (target == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_TARGET, null); - } else { - reference.setReferenceTarget(target); - } - if (unbind == null) { removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_UNBIND, null); } else { reference.setReferenceUnbind(unbind); } - if (policyOption == null) { - removeAttribute(reference, ATTRIBUTE_REFERENCE_POLICY_OPTION, VALUE_REFERENCE_POLICY_OPTION_RELUCTANT); - } else { - reference.setXMLAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION, policyOption); - } - if (updated == null) { removeAttribute(reference, ATTRIBUTE_REFERENCE_UPDATED, null); } else { reference.setXMLAttribute(ATTRIBUTE_REFERENCE_UPDATED, updated); } - return reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION) != null - || reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_UPDATED) != null; + if (scope(params) != null) { + return 3; + } + return (reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION) != null + || reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_UPDATED) != null) ? 2 : 1; } private ITypeBinding getObjectType(AST ast, ITypeBinding primitive) { diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java index 1a31648..288ccb9 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java @@ -84,6 +84,8 @@ public class Messages extends NLS { public static String AnnotationProcessor_noImplicitReferenceUnbind; + public static String AnnotationProcessor_unknownServiceType; + public static String AnnotationProcessor_stringOrEmpty; public static String AnnotationProcessor_unknownServiceTypeLabel; diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties index de67797..0dff162 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties @@ -35,13 +35,14 @@ AnnotationProcessor_invalidComponentPropertyFile=Component property file ''{0}'' AnnotationProcessor_invalidComponentPropertyType=Invalid component property type: {0}. Supported types are String (or empty), Long, Double, Float, Integer, Byte, Character, Boolean and Short. AnnotationProcessor_invalidComponentPropertyValue=Invalid {0} value: {1} AnnotationProcessor_invalidComponentService=Component does not extend or implement type: {0} -AnnotationProcessor_invalidLifeCycleMethodParameterType=Invalid {0} method parameter type: {1}; must be org.osgi.service.component.ComponentContext, org.osgi.framework.BundleContext, or java.util.Map. +AnnotationProcessor_invalidLifeCycleMethodParameterType=Invalid {0} method parameter type: {1}; must be org.osgi.service.component.ComponentContext, org.osgi.framework.BundleContext, java.util.Map or a component property type. AnnotationProcessor_invalidLifeCycleMethodReturnType=Invalid {0} method return type: {1}; must be void. AnnotationProcessor_invalidReferenceService=Parameter type {0} not assignable from {1}. AnnotationProcessor_invalidReferenceServiceUnknown=Unable to determine service type from method signature. Please specify service explicitly. AnnotationProcessor_invalidReferenceUnbind=No suitable unbind method named ''{0}'' found in implementation class hierarchy. AnnotationProcessor_invalidReferenceUpdated=No suitable updated method named ''{0}'' found in implementation class hierarchy. AnnotationProcessor_noImplicitReferenceUnbind=No implicit unbind method named ''{0}'' found in implementation class. +AnnotationProcessor_unknownServiceType=Cannot determine the implicit service type. 'service' is therefore required. AnnotationProcessor_stringOrEmpty=String (or empty) AnnotationProcessor_unknownServiceTypeLabel= BuildPathMarkerResolutionGenerator_additionalBundleResolution_description=Add bundle {0}, which exports DS Annotations, as an Additional Bundle dependency visible at build time but not at runtime. From 9caffeab7872b90a5f1d6f9b699daf328699f1a7 Mon Sep 17 00:00:00 2001 From: arievanwi Date: Sun, 10 Jan 2016 21:48:11 +0100 Subject: [PATCH 02/18] Removed the library stuff, since we export what we need from the DS plugin itself. --- ca.ecliptical.pde.ds.lib/.classpath | 7 - ca.ecliptical.pde.ds.lib/.project | 28 --- .../.settings/org.eclipse.jdt.core.prefs | 7 - .../.settings/org.eclipse.pde.core.prefs | 3 - ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF | 10 - .../OSGI-INF/l10n/bundle.properties | 3 - ca.ecliptical.pde.ds.lib/about.html | 67 ------ .../about_files/LICENSE-2.0.txt | 202 ------------------ ca.ecliptical.pde.ds.lib/annotations.jar | Bin 12375 -> 0 bytes ca.ecliptical.pde.ds.lib/annotationssrc.zip | Bin 12230 -> 0 bytes ca.ecliptical.pde.ds.lib/build.properties | 6 - 11 files changed, 333 deletions(-) delete mode 100644 ca.ecliptical.pde.ds.lib/.classpath delete mode 100644 ca.ecliptical.pde.ds.lib/.project delete mode 100644 ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs delete mode 100644 ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs delete mode 100644 ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF delete mode 100644 ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties delete mode 100644 ca.ecliptical.pde.ds.lib/about.html delete mode 100644 ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt delete mode 100644 ca.ecliptical.pde.ds.lib/annotations.jar delete mode 100644 ca.ecliptical.pde.ds.lib/annotationssrc.zip delete mode 100644 ca.ecliptical.pde.ds.lib/build.properties diff --git a/ca.ecliptical.pde.ds.lib/.classpath b/ca.ecliptical.pde.ds.lib/.classpath deleted file mode 100644 index 26d3d53..0000000 --- a/ca.ecliptical.pde.ds.lib/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/ca.ecliptical.pde.ds.lib/.project b/ca.ecliptical.pde.ds.lib/.project deleted file mode 100644 index f3bd272..0000000 --- a/ca.ecliptical.pde.ds.lib/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - ca.ecliptical.pde.ds.lib - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs b/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index af0f20f..0000000 --- a/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.5 diff --git a/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs b/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs deleted file mode 100644 index f29e940..0000000 --- a/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -pluginProject.extensions=false -resolve.requirebundle=false diff --git a/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF deleted file mode 100644 index 397d3f3..0000000 --- a/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF +++ /dev/null @@ -1,10 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: %Bundle-Name -Bundle-SymbolicName: ca.ecliptical.pde.ds.lib -Bundle-Version: 1.0.0.v20141223-1920 -Bundle-ClassPath: annotations.jar -Bundle-Vendor: %Bundle-Vendor -Export-Package: org.osgi.service.component.annotations;version="1.2.0" -Bundle-RequiredExecutionEnvironment: J2SE-1.5 -Import-Package: org.osgi.service.component.annotations;version="[1.2.0,1.3.0)" diff --git a/ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties b/ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties deleted file mode 100644 index f1e8f1c..0000000 --- a/ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties +++ /dev/null @@ -1,3 +0,0 @@ -#Properties file for ca.ecliptical.pde.ds.lib -Bundle-Vendor = Ecliptical Software Inc. -Bundle-Name = Annotations for Declarative Services \ No newline at end of file diff --git a/ca.ecliptical.pde.ds.lib/about.html b/ca.ecliptical.pde.ds.lib/about.html deleted file mode 100644 index cd0c528..0000000 --- a/ca.ecliptical.pde.ds.lib/about.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - -About - - -

About This Content

- -

August 26, 2014

-

License

- -

Ecliptical Software Inc. makes available all content in this plug-in ("Content"). Unless otherwise -indicated below, the Content is provided to you under the terms and conditions of the -Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available -at http://www.eclipse.org/legal/epl-v10.html. -For purposes of the EPL, "Program" will mean the Content.

- -

If you did not receive this Content directly from Ecliptical Software Inc., the Content is -being redistributed by another party ("Redistributor") and different terms and conditions may -apply to your use of any object code in the Content. Check the Redistributor's license that was -provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise -indicated below, the terms and conditions of the EPL still apply to any source code in the Content -and such source code may be obtained at http://www.eclipse.org.

- -

Third Party Content

- -

The Content includes items that have been sourced from third parties as set out below. If you -did not receive this Content directly from Ecliptical Software Inc., the following is provided -for informational purposes only, and you should look to the Redistributor’s license for -terms and conditions of use.

- -

OSGi Materials

- -

The following files (which may not be present in all cases):

- -
    -
  • annotations.jar
  • -
  • annotationssrc.zip
  • -
- -

shall be defined as the "OSGi Materials." The OSGi Materials are:

- -
-Copyright (c) 2000, 2012 -

-OSGi Alliance -Bishop Ranch 6
-2400 Camino Ramon, Suite 375
-San Ramon, CA 94583 USA -

-All Rights Reserved. -
- -

The OSGi Materials are provided to you under the terms and conditions of the Apache License, Version 2.0. A copy of the license is contained -in the file LICENSE-2.0.txt and is also available at http://www.apache.org/licenses/LICENSE-2.0.html.

- -

Implementation of certain elements of the OSGi Materials may be subject to third party intellectual property rights, including without limitation, patent rights (such a third party may -or may not be a member of the OSGi Alliance). The OSGi Alliance and its members are not responsible and shall not be held responsible in any manner for identifying or failing to identify any or all such third party -intellectual property rights.

- -OSGi™ is a trademark, registered trademark, or service mark of The OSGi Alliance in the US and other countries. Java is a trademark, -registered trademark, or service mark of Sun Microsystems, Inc. in the US and other countries. All other trademarks, registered trademarks, or -service marks used in the Content are the property of their respective owners and are hereby recognized. - - \ No newline at end of file diff --git a/ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt b/ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt deleted file mode 100644 index d645695..0000000 --- a/ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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 - - http://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. diff --git a/ca.ecliptical.pde.ds.lib/annotations.jar b/ca.ecliptical.pde.ds.lib/annotations.jar deleted file mode 100644 index 6630de342e38bdf2486be59a13b804a7c02ad158..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12375 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 diff --git a/ca.ecliptical.pde.ds.lib/annotationssrc.zip b/ca.ecliptical.pde.ds.lib/annotationssrc.zip deleted file mode 100644 index bde5c241712b080f205c4bd077cc80264ba98d6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12230 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

Ow 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 Date: Mon, 11 Jan 2016 11:04:13 +0100 Subject: [PATCH 03/18] Bugfixes and additional error/warning checking. --- .../pde/internal/ds/AnnotationProcessor.java | 119 ++++++++++++++---- .../ecliptical/pde/internal/ds/Messages.java | 6 + .../pde/internal/ds/messages.properties | 3 + 3 files changed, 103 insertions(+), 25 deletions(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index 72db636..3f93e9d 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -122,12 +123,14 @@ import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.FieldOption; import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.component.annotations.ReferenceScope; +import org.osgi.service.component.annotations.ServiceScope; @SuppressWarnings("restriction") public class AnnotationProcessor extends ASTRequestor { @@ -679,6 +682,12 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding validateComponentConfigPID(annotation, configPid, problems); } + ServiceScope scope = null; + if ((value = params.get("scope")) instanceof IVariableBinding) { + IVariableBinding scopeBinding = (IVariableBinding) value; + scope = ServiceScope.valueOf(scopeBinding.getName()); + } + IDSComponent component = model.getDSComponent(); if (enabled == null) { @@ -711,6 +720,13 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding component.setConfigurationPolicy(configPolicy); } + if (scope == null) { + removeAttribute(component, "scope", null); + } + else { + component.setXMLAttribute("scope", scope.toString()); + } + IDSDocumentFactory dsFactory = model.getFactory(); IDSProperty[] propElements = component.getPropertyElements(); @@ -911,6 +927,7 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } // Process the field declarations to get the field injection points. + // We actually should process the protected fields from super classes as well, but we don't. for (FieldDeclaration field : type.getFields()) { for (Object modifier : field.modifiers()) { if (!(modifier instanceof Annotation)) @@ -1449,8 +1466,7 @@ private static Map mapAnnotationBinding(IAnnotationBinding annot return params; } - private static String cardinality(Map params) { - String cardinality = null; + private static ReferenceCardinality _cardinality(Map params) { ReferenceCardinality cardinalityLiteral = null; Object value = params.get("cardinality"); if (value instanceof IVariableBinding) { @@ -1460,34 +1476,48 @@ private static String cardinality(Map params) { else if (value instanceof ReferenceCardinality) { cardinalityLiteral = (ReferenceCardinality) value; } + return cardinalityLiteral; + } + + private static String cardinality(Map params) { + ReferenceCardinality cardinalityLiteral = _cardinality(params); if (cardinalityLiteral != null) - cardinality = cardinalityLiteral.toString(); - return cardinality; + return cardinalityLiteral.toString(); + return null; } - private static String policy(Map params) { - String policy = null; + private static ReferencePolicy _policy(Map params) { Object value; + ReferencePolicy policyLiteral = null; if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$ IVariableBinding policyBinding = (IVariableBinding) value; - ReferencePolicy policyLiteral = ReferencePolicy.valueOf(policyBinding.getName()); - if (policyLiteral != null) - policy = policyLiteral.toString(); + policyLiteral = ReferencePolicy.valueOf(policyBinding.getName()); } - return policy; + return policyLiteral; } - private static String referencePolicyOption(Map params) { - String policyOption = null; + private static String policy(Map params) { + ReferencePolicy policyLiteral = _policy(params); + if (policyLiteral == null) return null; + return policyLiteral.toString(); + } + + private static ReferencePolicyOption _referencePolicyOption(Map params) { Object value; + ReferencePolicyOption policyOptionLiteral = null; if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$ IVariableBinding policyOptionBinding = (IVariableBinding) value; - ReferencePolicyOption policyOptionLiteral = ReferencePolicyOption.valueOf(policyOptionBinding.getName()); - if (policyOptionLiteral != null) { - policyOption = policyOptionLiteral.toString(); - } + policyOptionLiteral = ReferencePolicyOption.valueOf(policyOptionBinding.getName()); } - return policyOption; + return policyOptionLiteral; + } + + private static String referencePolicyOption(Map params) { + ReferencePolicyOption policyOptionLiteral = _referencePolicyOption(params); + if (policyOptionLiteral != null) { + return policyOptionLiteral.toString(); + } + return null; } private String target(Map params, Annotation annotation, Collection problems) { @@ -1588,14 +1618,19 @@ private IDSReference reference(String defaultName, String service, if (scope != null) { reference.setXMLAttribute("scope", scope); } + else { + removeAttribute(reference, "scope", null); + } return reference; } - private Type containedType(Type type) { + private Type containedType(Type type, int index) { Type contained = null; if (type.isParameterizedType()) { ParameterizedType thisType = (ParameterizedType) type; - contained = (Type) thisType.typeArguments().get(0); + if (thisType.typeArguments().size() > index) { + contained = (Type) thisType.typeArguments().get(index); + } } return contained; } @@ -1611,14 +1646,15 @@ private ITypeBinding getFieldCollectionType(Type type, StringBuffer fct) { } else if (ServiceReference.class.getName().equals(containedName)) { fieldCollectionType = "reference"; - serviceType = containedType(type); + serviceType = containedType(type, 0); } else if (Map.Entry.class.getName().equals(containedName)) { fieldCollectionType = "tuple"; + serviceType = containedType(type, 1); } - else if ("org.osgi.service.component.ComponentServiceObject".equals(containedName)) { + else if ("org.osgi.service.component.ComponentServiceObjects".equals(containedName)) { fieldCollectionType = "serviceobjects"; - serviceType = containedType(type); + serviceType = containedType(type, 0); } else { serviceType = type; @@ -1646,7 +1682,7 @@ private void processReference(FieldDeclaration field, String fieldName, boolean isCollection = false; try { ITypeHierarchy typeHierarchy = itype.newTypeHierarchy(new NullProgressMonitor()); - for (IType t : typeHierarchy.getSuperInterfaces(itype)) { + for (IType t : typeHierarchy.getAllInterfaces()) { if (t.getFullyQualifiedName().equals(Collection.class.getName())) { isCollection = true; } @@ -1655,18 +1691,36 @@ private void processReference(FieldDeclaration field, String fieldName, exc.printStackTrace(); } StringBuffer fieldCollectionType = new StringBuffer(); + ReferenceCardinality cardinality = _cardinality(params); if (isCollection) { - if (cardinality(params) == null) { + if (ReferenceCardinality.MANDATORY.equals(cardinality)) { + reportProblem(annotation, "cardinality", ValidationErrorLevel.warning, problems, + NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); + } + else if (cardinality == null) { params.put("cardinality", ReferenceCardinality.AT_LEAST_ONE); } // Collection type. Check whether it is a parameterized type with one parameter containing the // service type or one of the other options for injecting a service. - defaultService = getFieldCollectionType(containedType(type), fieldCollectionType); + defaultService = getFieldCollectionType(containedType(type, 0), fieldCollectionType); } else { // No collection. If it is not one of the known types, like service reference, etc., // just get the type of the field itself. defaultService = getFieldCollectionType(type, null); + if (EnumSet.of(ReferenceCardinality.AT_LEAST_ONE, ReferenceCardinality.MULTIPLE).contains(cardinality)) { + reportProblem(annotation, "cardinality", ValidationErrorLevel.error, problems, + NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); + } + } + // Dynamic fields should be marked volatile. + if (ReferencePolicy.DYNAMIC.equals(_policy(params)) && (field.getModifiers() & Modifier.VOLATILE) == 0) { + reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_dynamicShouldBeVolatile); + } + if ((field.getModifiers() & Modifier.FINAL) != 0) { + reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_referenceAndFinal); } // Check the service specification. If the service is specified, this is the easiest solution, since the programmer // specifies what the service is. @@ -1700,6 +1754,21 @@ private void processReference(FieldDeclaration field, String fieldName, if (fieldCollectionType.length() > 0) { reference.setXMLAttribute("field-collection-type", fieldCollectionType.toString()); } + else { + removeAttribute(reference, "field-collection-type", null); + } + FieldOption fieldOption = null; + Object value = params.get("fieldOption"); + if (value instanceof IVariableBinding) { + IVariableBinding scopeBinding = (IVariableBinding) value; + fieldOption = FieldOption.valueOf(scopeBinding.getName()); + } + if (fieldOption != null && FieldOption.UPDATE.equals(fieldOption)) { + reference.setXMLAttribute("field-option", fieldOption.toString()); + } + else { + removeAttribute(reference, "field-option", null); + } } } diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java index 288ccb9..ba976e5 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java @@ -89,7 +89,13 @@ public class Messages extends NLS { public static String AnnotationProcessor_stringOrEmpty; public static String AnnotationProcessor_unknownServiceTypeLabel; + + public static String AnnotationProcessor_cardinalityMismatch; + public static String AnnotationProcessor_dynamicShouldBeVolatile; + + public static String AnnotationProcessor_referenceAndFinal; + public static String BuildPathMarkerResolutionGenerator_additionalBundleResolution_description; public static String BuildPathMarkerResolutionGenerator_additionalBundleResolution_label; diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties index 0dff162..a0e5bea 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties @@ -43,6 +43,9 @@ AnnotationProcessor_invalidReferenceUnbind=No suitable unbind method named ''{0} AnnotationProcessor_invalidReferenceUpdated=No suitable updated method named ''{0}'' found in implementation class hierarchy. AnnotationProcessor_noImplicitReferenceUnbind=No implicit unbind method named ''{0}'' found in implementation class. AnnotationProcessor_unknownServiceType=Cannot determine the implicit service type. 'service' is therefore required. +AnnotationProcessor_cardinalityMismatch=Cardinality ''{0}'' does not match the field type +AnnotationProcessor_dynamicShouldBeVolatile=Dynamic fields must be marked 'volatile' +AnnotationProcessor_referenceAndFinal=Reference annotated fields cannot be final AnnotationProcessor_stringOrEmpty=String (or empty) AnnotationProcessor_unknownServiceTypeLabel= BuildPathMarkerResolutionGenerator_additionalBundleResolution_description=Add bundle {0}, which exports DS Annotations, as an Additional Bundle dependency visible at build time but not at runtime. From 81febe4d9b89c7dc2380e2c4b72090aee337b65e Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Mon, 11 Jan 2016 12:16:53 +0100 Subject: [PATCH 04/18] configurationPid can be a list of pids (who needs that?) --- .../pde/internal/ds/AnnotationProcessor.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index 3f93e9d..b7b43b8 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -677,9 +677,16 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } String configPid = null; - if ((value = params.get("configurationPid")) instanceof String) { //$NON-NLS-1$ - configPid = (String) value; - validateComponentConfigPID(annotation, configPid, problems); + if ((value = params.get("configurationPid")) instanceof String || value instanceof Object[]) { //$NON-NLS-1$ + if (value instanceof String) { + value = new Object[]{value}; + } + StringBuffer configurations = new StringBuffer(); + for (Object pid : (Object[]) value) { + validateComponentConfigPID(annotation, pid.toString(), problems); + configurations.append(pid.toString()).append(" "); + } + configPid = configurations.toString().trim(); } ServiceScope scope = null; @@ -1106,7 +1113,7 @@ else if (requiredVersion > 1) { xmlns = neededXmlns; } - component.setNamespace(xmlns); + component.setNamespace(xmlns); // Note: updates to the namespace do not work if no other changes! } private void removeChildren(IDSObject parent, Collection children) { From 01924b8575a2d5edb811c8417bafa7bc8f588eae Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Mon, 11 Jan 2016 12:54:38 +0100 Subject: [PATCH 05/18] Updated version to 1.3 Removed lib from feature. --- ca.ecliptical.pde.ds-feature/feature.xml | 82 +++++++++++------------ ca.ecliptical.pde.ds/META-INF/MANIFEST.MF | 2 +- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/ca.ecliptical.pde.ds-feature/feature.xml b/ca.ecliptical.pde.ds-feature/feature.xml index c0a86ae..81959ca 100644 --- a/ca.ecliptical.pde.ds-feature/feature.xml +++ b/ca.ecliptical.pde.ds-feature/feature.xml @@ -1,4 +1,4 @@ - + - - - - %description - - - - %copyright - - - - - - - - - - - - - - + --> + + + + %description + + + + %copyright + + + + + + + + + + + + diff --git a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF index ac75af0..512722d 100644 --- a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF +++ b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: ca.ecliptical.pde.ds;singleton:=true -Bundle-Version: 1.2.8.qualifier +Bundle-Version: 1.3.0.qualifier Bundle-Activator: ca.ecliptical.pde.internal.ds.Activator Bundle-Vendor: %Bundle-Vendor Require-Bundle: org.eclipse.ui;bundle-version="[3.105.0,4.0.0)", From 60cd52942dc89cc2aab42d70563ef2f5965ae33f Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Tue, 12 Jan 2016 09:27:37 +0100 Subject: [PATCH 06/18] Multiple pids is actually a v1.3 feature. --- .../pde/internal/ds/AnnotationProcessor.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index b7b43b8..9f4bb7c 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -17,7 +17,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringReader; -import org.eclipse.jdt.core.dom.ParameterizedType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -35,7 +34,6 @@ import java.util.concurrent.CountDownLatch; import java.util.regex.Pattern; -import org.eclipse.jdt.core.dom.Type; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.ITextFileBufferManager; @@ -55,7 +53,6 @@ import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.compiler.BuildContext; @@ -81,7 +78,9 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; +import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jface.text.BadLocationException; @@ -587,6 +586,9 @@ private void performEdit(IDocument document, TextEdit edit) throws CoreException } private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map params, String name, String implClass, Collection problems) { + // The required version of the DS specification. Defaults to 1, meaning: v1.1.0 + int requiredVersion = 1; + Object value; Collection services; if ((value = params.get("service")) instanceof Object[]) { //$NON-NLS-1$ @@ -678,15 +680,22 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding String configPid = null; if ((value = params.get("configurationPid")) instanceof String || value instanceof Object[]) { //$NON-NLS-1$ + Object[] pids; if (value instanceof String) { - value = new Object[]{value}; + pids = new Object[]{value}; + } + else { + pids = (Object[]) value; } StringBuffer configurations = new StringBuffer(); - for (Object pid : (Object[]) value) { + for (Object pid : pids) { validateComponentConfigPID(annotation, pid.toString(), problems); configurations.append(pid.toString()).append(" "); } configPid = configurations.toString().trim(); + if (pids.length > 1) { + requiredVersion = Math.max(requiredVersion, 3); + } } ServiceScope scope = null; @@ -916,7 +925,6 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } } - int requiredVersion = 1; String activate = null; Annotation activateAnnotation = null; String deactivate = null; From 7f5bf112ce722c0b6e1ce23a77cfb752df0b51f3 Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Tue, 12 Jan 2016 16:29:40 +0100 Subject: [PATCH 07/18] Also process the class hierarchy for inherited fields. --- .../pde/internal/ds/AnnotationProcessor.java | 121 +++++++++++++----- .../pde/internal/ds/DSAnnotationProblem.java | 10 +- 2 files changed, 98 insertions(+), 33 deletions(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index 9f4bb7c..d53cb03 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -59,6 +59,7 @@ import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTRequestor; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; @@ -175,16 +176,29 @@ public void acceptAST(ICompilationUnit source, CompilationUnit ast) { } if (!problems.isEmpty()) { - char[] filename = source.getResource().getFullPath().toString().toCharArray(); - for (DSAnnotationProblem problem : problems) { - problem.setOriginatingFileName(filename); - if (problem.getSourceStart() >= 0) - problem.setSourceLineNumber(ast.getLineNumber(problem.getSourceStart())); + // Remap the problems by compilation unit. + Map> remapped = new HashMap>(); + for (DSAnnotationProblem p : problems) { + List thisUnit = remapped.get(p.getUnit()); + if (thisUnit == null) { + thisUnit = new ArrayList(); + remapped.put(p.getUnit(), thisUnit); + } + thisUnit.add(p); + } + for (Map.Entry> entry : remapped.entrySet()) { + ICompilationUnit iu = (ICompilationUnit) entry.getKey().getJavaElement(); + char[] filename = iu.getResource().getFullPath().toString().toCharArray(); + List thisProblems = entry.getValue(); + for (DSAnnotationProblem problem : thisProblems) { + problem.setOriginatingFileName(filename); + if (problem.getSourceStart() >= 0) + problem.setSourceLineNumber(entry.getKey().getLineNumber(problem.getSourceStart())); + } + BuildContext buildContext = fileMap.get(iu); + if (buildContext != null) + buildContext.recordNewProblems(thisProblems.toArray(new CategorizedProblem[thisProblems.size()])); } - - BuildContext buildContext = fileMap.get(source); - if (buildContext != null) - buildContext.recordNewProblems(problems.toArray(new CategorizedProblem[problems.size()])); } } @@ -942,26 +956,8 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } // Process the field declarations to get the field injection points. - // We actually should process the protected fields from super classes as well, but we don't. - for (FieldDeclaration field : type.getFields()) { - for (Object modifier : field.modifiers()) { - if (!(modifier instanceof Annotation)) - continue; - Annotation fieldAnnotation = (Annotation) modifier; - IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding(); - if (fieldAnnotationBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for annotation: %s", fieldAnnotation)); //$NON-NLS-1$ - continue; - } - String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName(); - if (REFERENCE_ANNOTATION.equals(annotationName)) { - requiredVersion = Math.max(requiredVersion, 3); - VariableDeclarationFragment fragment = (VariableDeclarationFragment) field.fragments().get(0); - processReference(field, fragment.getName().getIdentifier(), fieldAnnotation, fieldAnnotationBinding, - refMap, dsFactory, references, referenceNames, problems); - } - } + if (processFieldReferences(type, false, refMap, dsFactory, references, referenceNames, problems).size() > 0) { + requiredVersion = Math.max(requiredVersion, 3); } for (MethodDeclaration method : type.getMethods()) { @@ -1481,6 +1477,62 @@ private static Map mapAnnotationBinding(IAnnotationBinding annot return params; } + /* + * Process the field references present for a type declaration. The annotations of the fields are processed and when + * a reference annotation is found, it is passed down the reference processing. + */ + Collection processFieldReferences(TypeDeclaration type, boolean checkAccessible, final Map refMap, + final IDSDocumentFactory factory, final Collection references, + final Map names, final Collection problems) { + final List found = new ArrayList(); + int requiredVersion = 1; + for (FieldDeclaration field : type.getFields()) { + if (checkAccessible && (field.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) continue; + for (Object modifier : field.modifiers()) { + if (!(modifier instanceof Annotation)) + continue; + Annotation fieldAnnotation = (Annotation) modifier; + IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding(); + if (fieldAnnotationBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for annotation: %s", fieldAnnotation)); //$NON-NLS-1$ + continue; + } + String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName(); + if (REFERENCE_ANNOTATION.equals(annotationName)) { + requiredVersion = Math.max(requiredVersion, 3); + VariableDeclarationFragment fragment = (VariableDeclarationFragment) field.fragments().get(0); + IDSReference goodOne = processReference(field, fragment.getName().getIdentifier(), fieldAnnotation, fieldAnnotationBinding, + refMap, factory, references, names, problems); + if (goodOne != null) { + found.add(goodOne); + } + } + } + } + // Now process the super class for any found references there (if we have a super type). + Type superT = type.getSuperclassType(); + if (superT != null) { + final IType superType = (IType) superT.resolveBinding().getJavaElement(); + ASTParser parser = ASTParser.newParser(AST.JLS4); + parser.setResolveBindings(true); + parser.setSource(superType.getCompilationUnit()); + ASTNode unitNode = parser.createAST(new NullProgressMonitor()); + unitNode.accept(new ASTVisitor() { + @Override + public boolean visit(TypeDeclaration node) { + if (node.resolveBinding().getQualifiedName().equals(superType.getFullyQualifiedName())) { + // Process the fields of this one as well. However, limit the fields to only the + // ones that are protected and public. + found.addAll(processFieldReferences(node, true, refMap, factory, references, names, problems)); + } + return false; + } + }); + } + return found; + } + private static ReferenceCardinality _cardinality(Map params) { ReferenceCardinality cardinalityLiteral = null; Object value = params.get("cardinality"); @@ -1680,7 +1732,7 @@ else if ("org.osgi.service.component.ComponentServiceObjects".equals(containedNa return (serviceType == null) ? null : serviceType.resolveBinding(); } - private void processReference(FieldDeclaration field, String fieldName, + private IDSReference processReference(FieldDeclaration field, String fieldName, Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, IDSDocumentFactory factory, Collection collector, Map names, Collection problems) { @@ -1704,6 +1756,7 @@ private void processReference(FieldDeclaration field, String fieldName, } } catch (Exception exc) { exc.printStackTrace(); + return null; } StringBuffer fieldCollectionType = new StringBuffer(); ReferenceCardinality cardinality = _cardinality(params); @@ -1740,6 +1793,7 @@ else if (cardinality == null) { // Check the service specification. If the service is specified, this is the easiest solution, since the programmer // specifies what the service is. Object s = params.get("service"); + IDSReference reference = null; if (s == null && defaultService == null) { reportProblem(annotation, "service", problems, Messages.AnnotationProcessor_unknownServiceType); } @@ -1761,7 +1815,7 @@ else if (cardinality == null) { serviceName = serviceType.getBinaryName(); } // Create the service reference. - IDSReference reference = reference(fieldName, serviceName, + reference = reference(fieldName, serviceName, annotation, params, refMap, factory, collector, names, problems); // Add some attributes because of field injection. Note that these attributes // are not yet known in the current eclipse version (Mars) and therefore used by hand. @@ -1785,6 +1839,7 @@ else if (cardinality == null) { removeAttribute(reference, "field-option", null); } } + return reference; } private int processReference(MethodDeclaration method, IMethodBinding methodBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, IDSDocumentFactory factory, Collection collector, Map names, Collection problems) { @@ -2125,10 +2180,12 @@ private void reportProblem(Annotation annotation, String member, int valueIndex, } if (start >= 0) { - DSAnnotationProblem problem = new DSAnnotationProblem(errorLevel.isError(), message, args); + DSAnnotationProblem problem = new DSAnnotationProblem((CompilationUnit) annotation.getRoot(), + errorLevel.isError(), message, args); problem.setSourceStart(start); problem.setSourceEnd(start + length - 1); problems.add(problem); } + } } \ No newline at end of file diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/DSAnnotationProblem.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/DSAnnotationProblem.java index fad8968..4eb9a2d 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/DSAnnotationProblem.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/DSAnnotationProblem.java @@ -11,6 +11,7 @@ package ca.ecliptical.pde.internal.ds; import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.core.dom.CompilationUnit; public class DSAnnotationProblem extends CategorizedProblem { @@ -28,10 +29,13 @@ public class DSAnnotationProblem extends CategorizedProblem { private int sourceLineNumber; - public DSAnnotationProblem(boolean error, String message, String... args) { + private CompilationUnit unit; + + public DSAnnotationProblem(CompilationUnit unit, boolean error, String message, String... args) { this.error = error; this.message = message; this.args = args; + this.unit = unit; } public boolean isError() { @@ -95,4 +99,8 @@ public int getSourceLineNumber() { public void setSourceLineNumber(int sourceLineNumber) { this.sourceLineNumber = sourceLineNumber; } + + public CompilationUnit getUnit() { + return unit; + } } From 29219042d64355f01bd20535eee72834a10bb374 Mon Sep 17 00:00:00 2001 From: arievanwi Date: Tue, 12 Jan 2016 21:11:01 +0100 Subject: [PATCH 08/18] Process default properties from the @interface method parameters. --- .../pde/internal/ds/AnnotationProcessor.java | 326 +++++++++++------- 1 file changed, 197 insertions(+), 129 deletions(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index d53cb03..e81eee8 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -186,6 +186,7 @@ public void acceptAST(ICompilationUnit source, CompilationUnit ast) { } thisUnit.add(p); } + // Process the problems per file. for (Map.Entry> entry : remapped.entrySet()) { ICompilationUnit iu = (ICompilationUnit) entry.getKey().getJavaElement(); char[] filename = iu.getResource().getFullPath().toString().toCharArray(); @@ -196,6 +197,7 @@ public void acceptAST(ICompilationUnit source, CompilationUnit ast) { problem.setSourceLineNumber(entry.getKey().getLineNumber(problem.getSourceStart())); } BuildContext buildContext = fileMap.get(iu); + // And show the errors/warnings. if (buildContext != null) buildContext.recordNewProblems(thisProblems.toArray(new CategorizedProblem[thisProblems.size()])); } @@ -601,6 +603,7 @@ private void performEdit(IDocument document, TextEdit edit) throws CoreException private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map params, String name, String implClass, Collection problems) { // The required version of the DS specification. Defaults to 1, meaning: v1.1.0 + // The value will be updated in case necessary. int requiredVersion = 1; Object value; @@ -759,8 +762,135 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding IDSDocumentFactory dsFactory = model.getFactory(); + String activate = null; + Annotation activateAnnotation = null; + String deactivate = null; + Annotation deactivateAnnotation = null; + String modified = null; + Annotation modifiedAnnotation = null; + + ArrayList references = new ArrayList(); + HashMap referenceNames = new HashMap(); + IDSReference[] refElements = component.getReferences(); + + HashMap refMap = new HashMap(refElements.length); + for (IDSReference refElement : refElements) { + refMap.put(refElement.getName(), refElement); + } + + // Process the field declarations to get the field injection points. + if (processFieldReferences(type, false, refMap, dsFactory, references, referenceNames, problems).size() > 0) { + requiredVersion = Math.max(requiredVersion, 3); + } + + Map defaultValues = new HashMap(); + for (MethodDeclaration method : type.getMethods()) { + for (Object modifier : method.modifiers()) { + if (!(modifier instanceof Annotation)) + continue; + + Annotation methodAnnotation = (Annotation) modifier; + IAnnotationBinding methodAnnotationBinding = methodAnnotation.resolveAnnotationBinding(); + if (methodAnnotationBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for annotation: %s", methodAnnotation)); //$NON-NLS-1$ + + continue; + } + + String annotationName = methodAnnotationBinding.getAnnotationType().getQualifiedName(); + + if (ACTIVATE_ANNOTATION.equals(annotationName)) { + if (activate == null) { + activate = method.getName().getIdentifier(); + activateAnnotation = methodAnnotation; + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "activate", method, + dsFactory, defaultValues, problems)); //$NON-NLS-1$ + } else if (!errorLevel.isNone()) { + reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier()); + if (activateAnnotation != null) { + reportProblem(activateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, activate); + activateAnnotation = null; + } + } + + continue; + } + + if (DEACTIVATE_ANNOTATION.equals(annotationName)) { + if (deactivate == null) { + deactivate = method.getName().getIdentifier(); + deactivateAnnotation = methodAnnotation; + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "deactivate", method, + dsFactory, defaultValues, problems)); //$NON-NLS-1$ + } else if (!errorLevel.isNone()) { + reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, method.getName().getIdentifier()); + if (deactivateAnnotation != null) { + reportProblem(deactivateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, deactivate); + deactivateAnnotation = null; + } + } + + continue; + } + + if (MODIFIED_ANNOTATION.equals(annotationName)) { + if (modified == null) { + modified = method.getName().getIdentifier(); + modifiedAnnotation = methodAnnotation; + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "modified", method, + dsFactory, defaultValues, problems)); //$NON-NLS-1$ + } else if (!errorLevel.isNone()) { + reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, method.getName().getIdentifier()); + if (modifiedAnnotation != null) { + reportProblem(modifiedAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, modified); + modifiedAnnotation = null; + } + } + + continue; + } + + if (REFERENCE_ANNOTATION.equals(annotationName)) { + IMethodBinding methodBinding = method.resolveBinding(); + if (methodBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ + } else { + requiredVersion = Math.max(requiredVersion, processReference(method, methodBinding, methodAnnotation, methodAnnotationBinding, refMap, dsFactory, references, referenceNames, problems)); + } + + continue; + } + } + } + + if (activate == null) { + // only remove activate="activate" if method not found + if (!"activate".equals(component.getActivateMethod()) //$NON-NLS-1$ + || !hasLifeCycleMethod(typeBinding, "activate")) //$NON-NLS-1$ + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null); //$NON-NLS-1$ + } else { + component.setActivateMethod(activate); + } + + if (deactivate == null) { + // only remove deactivate="deactivate" if method not found + if (!"deactivate".equals(component.getDeactivateMethod()) //$NON-NLS-1$ + || !hasLifeCycleMethod(typeBinding, "deactivate")) //$NON-NLS-1$ + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_DEACTIVATE, null); //$NON-NLS-1$ + } else { + component.setDeactivateMethod(deactivate); + } + + if (modified == null) { + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_MODIFIED, null); + } else { + component.setModifiedeMethod(modified); + } + IDSProperty[] propElements = component.getPropertyElements(); - if (properties.length == 0) { + if (properties.length == 0 && defaultValues.size() == 0) { removeChildren(component, Arrays.asList(propElements)); } else { // build up new property elements @@ -817,12 +947,16 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } // reconcile against existing property elements - HashMap propMap = new HashMap(propElements.length); + HashMap propMap = new HashMap(); for (IDSProperty propElement : propElements) { propMap.put(propElement.getPropertyName(), propElement); } - ArrayList propList = new ArrayList(map.values()); + // Merge the default values and overwrite them with the values in the component annotation. + Map actualProperties = new HashMap(); + actualProperties.putAll(defaultValues); + actualProperties.putAll(map); + ArrayList propList = new ArrayList(actualProperties.values()); for (ListIterator i = propList.listIterator(); i.hasNext();) { IDSProperty newProperty = i.next(); IDSProperty property = propMap.remove(newProperty.getPropertyName()); @@ -836,7 +970,8 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding property.setPropertyType(newPropertyType); String newContent = newProperty.getPropertyElemBody(); - if (newContent == null) { + // Somehow, even if we set null to the body, it still can contain an empty string. + if (newContent == null || newContent.length() == 0) { property.setPropertyValue(newProperty.getPropertyValue()); IDocumentTextNode textNode = property.getTextNode(); if (textNode != null) { @@ -939,128 +1074,6 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } } - String activate = null; - Annotation activateAnnotation = null; - String deactivate = null; - Annotation deactivateAnnotation = null; - String modified = null; - Annotation modifiedAnnotation = null; - - ArrayList references = new ArrayList(); - HashMap referenceNames = new HashMap(); - IDSReference[] refElements = component.getReferences(); - - HashMap refMap = new HashMap(refElements.length); - for (IDSReference refElement : refElements) { - refMap.put(refElement.getName(), refElement); - } - - // Process the field declarations to get the field injection points. - if (processFieldReferences(type, false, refMap, dsFactory, references, referenceNames, problems).size() > 0) { - requiredVersion = Math.max(requiredVersion, 3); - } - - for (MethodDeclaration method : type.getMethods()) { - for (Object modifier : method.modifiers()) { - if (!(modifier instanceof Annotation)) - continue; - - Annotation methodAnnotation = (Annotation) modifier; - IAnnotationBinding methodAnnotationBinding = methodAnnotation.resolveAnnotationBinding(); - if (methodAnnotationBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for annotation: %s", methodAnnotation)); //$NON-NLS-1$ - - continue; - } - - String annotationName = methodAnnotationBinding.getAnnotationType().getQualifiedName(); - - if (ACTIVATE_ANNOTATION.equals(annotationName)) { - if (activate == null) { - activate = method.getName().getIdentifier(); - activateAnnotation = methodAnnotation; - requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "activate", method, problems)); //$NON-NLS-1$ - } else if (!errorLevel.isNone()) { - reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier()); - if (activateAnnotation != null) { - reportProblem(activateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, activate); - activateAnnotation = null; - } - } - - continue; - } - - if (DEACTIVATE_ANNOTATION.equals(annotationName)) { - if (deactivate == null) { - deactivate = method.getName().getIdentifier(); - deactivateAnnotation = methodAnnotation; - requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "deactivate", method, problems)); //$NON-NLS-1$ - } else if (!errorLevel.isNone()) { - reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, method.getName().getIdentifier()); - if (deactivateAnnotation != null) { - reportProblem(deactivateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, deactivate); - deactivateAnnotation = null; - } - } - - continue; - } - - if (MODIFIED_ANNOTATION.equals(annotationName)) { - if (modified == null) { - modified = method.getName().getIdentifier(); - modifiedAnnotation = methodAnnotation; - requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "modified", method, problems)); //$NON-NLS-1$ - } else if (!errorLevel.isNone()) { - reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, method.getName().getIdentifier()); - if (modifiedAnnotation != null) { - reportProblem(modifiedAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, modified); - modifiedAnnotation = null; - } - } - - continue; - } - - if (REFERENCE_ANNOTATION.equals(annotationName)) { - IMethodBinding methodBinding = method.resolveBinding(); - if (methodBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ - } else { - requiredVersion = Math.max(requiredVersion, processReference(method, methodBinding, methodAnnotation, methodAnnotationBinding, refMap, dsFactory, references, referenceNames, problems)); - } - - continue; - } - } - } - - if (activate == null) { - // only remove activate="activate" if method not found - if (!"activate".equals(component.getActivateMethod()) //$NON-NLS-1$ - || !hasLifeCycleMethod(typeBinding, "activate")) //$NON-NLS-1$ - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null); //$NON-NLS-1$ - } else { - component.setActivateMethod(activate); - } - - if (deactivate == null) { - // only remove deactivate="deactivate" if method not found - if (!"deactivate".equals(component.getDeactivateMethod()) //$NON-NLS-1$ - || !hasLifeCycleMethod(typeBinding, "deactivate")) //$NON-NLS-1$ - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_DEACTIVATE, null); //$NON-NLS-1$ - } else { - component.setDeactivateMethod(deactivate); - } - - if (modified == null) { - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_MODIFIED, null); - } else { - component.setModifiedeMethod(modified); - } if (configPid == null) { removeAttribute(component, ATTRIBUTE_COMPONENT_CONFIGURATION_PID, null); @@ -1342,7 +1355,19 @@ private void validateComponentConfigPID(Annotation annotation, String configPid, reportProblem(annotation, "configurationPid", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentConfigurationPid, configPid), configPid); //$NON-NLS-1$ } - private int validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method, Collection problems) { + private static String getValueOfDefault(Object object) { + if (object instanceof IVariableBinding) { + IVariableBinding binding = (IVariableBinding) object; + if (binding.isEnumConstant()) { + return binding.getName(); + } + } + return object.toString(); + } + + private int validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method, + IDSDocumentFactory factory, Map defaultValues, + Collection problems) { IMethodBinding methodBinding = method.resolveBinding(); if (methodBinding == null) { if (debug.isDebugging()) @@ -1392,6 +1417,47 @@ private int validateLifeCycleMethod(Annotation annotation, String methodName, Me } else { hasComponentPropertyType = true; + // Check the default values in the property. + for (IMethodBinding m : paramTypeBinding.getDeclaredMethods()) { + // Does this property have a default value? + Object defaultValue = m.getDefaultValue(); + if (defaultValue != null) { + IDSProperty property = factory.createProperty(); + Class type = null; + property.setPropertyName(m.getName()); + // Is it an array? If so, fill the body. + if (defaultValue.getClass().isArray()) { + Object[] values = (Object[]) defaultValue; + StringBuffer v = new StringBuffer(); + for (int i = 0; i < values.length; i++) { + Object thisValue = values[i]; + if (thisValue != null) { + type = thisValue.getClass(); + if (v.length() > 0) { + v.append("\n"); + } + v.append(getValueOfDefault(thisValue)); + } + } + property.setPropertyElemBody(v.toString()); + property.setPropertyValue(null); + } + else { + // Normal, single value + type = defaultValue.getClass(); + property.setPropertyValue(getValueOfDefault(defaultValue)); + property.setPropertyElemBody(null); + } + if (type != null) { + property.setPropertyType(type.getSimpleName()); + if (!PROPERTY_TYPES.contains(property.getPropertyType())) { + property.setPropertyType(null); + } + } + System.out.println(property); + defaultValues.put(property.getPropertyName(), property); + } + } requiredSpecVersion = 3; } } else if ("deactivate".equals(methodName) //$NON-NLS-1$ @@ -1512,7 +1578,7 @@ Collection processFieldReferences(TypeDeclaration type, boolean ch } // Now process the super class for any found references there (if we have a super type). Type superT = type.getSuperclassType(); - if (superT != null) { + if (superT != null && superT.resolveBinding() != null) { final IType superType = (IType) superT.resolveBinding().getJavaElement(); ASTParser parser = ASTParser.newParser(AST.JLS4); parser.setResolveBindings(true); @@ -1521,7 +1587,8 @@ Collection processFieldReferences(TypeDeclaration type, boolean ch unitNode.accept(new ASTVisitor() { @Override public boolean visit(TypeDeclaration node) { - if (node.resolveBinding().getQualifiedName().equals(superType.getFullyQualifiedName())) { + ITypeBinding thisType = node.resolveBinding(); + if (thisType != null && thisType.getQualifiedName().equals(superType.getFullyQualifiedName())) { // Process the fields of this one as well. However, limit the fields to only the // ones that are protected and public. found.addAll(processFieldReferences(node, true, refMap, factory, references, names, problems)); @@ -1741,6 +1808,7 @@ private IDSReference processReference(FieldDeclaration field, String fieldName, // Check the service type of the field. Can be either a map, collection, entry or any other object type. Type type = field.getType(); ITypeBinding binding = type.resolveBinding(); + if (binding == null) return null; // Now the processing of the type of the field. There are a couple of options, but // the first discrimination is made between collections and "normal" objects. ITypeBinding defaultService = null; From 660c4d14828a00dede88855a9dfd548294bea3c3 Mon Sep 17 00:00:00 2001 From: arievanwi Date: Tue, 12 Jan 2016 21:14:46 +0100 Subject: [PATCH 09/18] Forgot to remove a println. --- .../src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index e81eee8..5807d2d 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -1454,7 +1454,6 @@ private int validateLifeCycleMethod(Annotation annotation, String methodName, Me property.setPropertyType(null); } } - System.out.println(property); defaultValues.put(property.getPropertyName(), property); } } From 0d3b04329b3b0cabfe039868cb5a4bd8320d8085 Mon Sep 17 00:00:00 2001 From: arievanwi Date: Tue, 12 Jan 2016 22:03:31 +0100 Subject: [PATCH 10/18] Some additional errors for dynamic handling with field options. --- .../pde/internal/ds/AnnotationProcessor.java | 81 ++++++++++++++----- .../ecliptical/pde/internal/ds/Messages.java | 4 + .../pde/internal/ds/messages.properties | 2 + 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index 5807d2d..76a24e3 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -176,7 +176,8 @@ public void acceptAST(ICompilationUnit source, CompilationUnit ast) { } if (!problems.isEmpty()) { - // Remap the problems by compilation unit. + // Remap the problems by compilation unit. We may have gotten problems from various + // sources and the problems should be shown with the correct file. Map> remapped = new HashMap>(); for (DSAnnotationProblem p : problems) { List thisUnit = remapped.get(p.getUnit()); @@ -186,7 +187,7 @@ public void acceptAST(ICompilationUnit source, CompilationUnit ast) { } thisUnit.add(p); } - // Process the problems per file. + // Process the problems per file/compilation unit. for (Map.Entry> entry : remapped.entrySet()) { ICompilationUnit iu = (ICompilationUnit) entry.getKey().getJavaElement(); char[] filename = iu.getResource().getFullPath().toString().toCharArray(); @@ -890,10 +891,12 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding } IDSProperty[] propElements = component.getPropertyElements(); + // If we don't have any properties, just remove them from the XML. if (properties.length == 0 && defaultValues.size() == 0) { removeChildren(component, Arrays.asList(propElements)); } else { - // build up new property elements + // build up new property elements. This are the property + // elements present in the @Component annotation. LinkedHashMap map = new LinkedHashMap(properties.length); for (int i = 0; i < properties.length; ++i) { String propertyStr = properties[i]; @@ -952,9 +955,12 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding propMap.put(propElement.getPropertyName(), propElement); } - // Merge the default values and overwrite them with the values in the component annotation. + // We now merge the default values from the configuration type properties with + // the values found in the component annotations. The latter overwrite the first. Map actualProperties = new HashMap(); + // Load the defaults. actualProperties.putAll(defaultValues); + // Overwrite/add the properties from the annotation. actualProperties.putAll(map); ArrayList propList = new ArrayList(actualProperties.values()); for (ListIterator i = propList.listIterator(); i.hasNext();) { @@ -970,7 +976,8 @@ private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding property.setPropertyType(newPropertyType); String newContent = newProperty.getPropertyElemBody(); - // Somehow, even if we set null to the body, it still can contain an empty string. + // Somehow, even if we set null to the body, it still can contain an empty string. Therefore, check + // on an empty string as well. if (newContent == null || newContent.length() == 0) { property.setPropertyValue(newProperty.getPropertyValue()); IDocumentTextNode textNode = property.getTextNode(); @@ -1543,32 +1550,32 @@ private static Map mapAnnotationBinding(IAnnotationBinding annot } /* - * Process the field references present for a type declaration. The annotations of the fields are processed and when - * a reference annotation is found, it is passed down the reference processing. + * Process the field references present for a type declaration. The fields are checked and if + * a reference annotation is found, it is passed down the reference annotation processing. */ Collection processFieldReferences(TypeDeclaration type, boolean checkAccessible, final Map refMap, final IDSDocumentFactory factory, final Collection references, final Map names, final Collection problems) { + // List we are going to return to our parent to show that we actually found references. final List found = new ArrayList(); - int requiredVersion = 1; + // Loop through the fields. for (FieldDeclaration field : type.getFields()) { + // If we need to check the protect/public modifiers, do it. This is enabled for superclass parsing. if (checkAccessible && (field.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) continue; for (Object modifier : field.modifiers()) { if (!(modifier instanceof Annotation)) continue; Annotation fieldAnnotation = (Annotation) modifier; IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding(); - if (fieldAnnotationBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for annotation: %s", fieldAnnotation)); //$NON-NLS-1$ - continue; - } + if (fieldAnnotationBinding == null) continue; + // Check if it is reference annotation. String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName(); if (REFERENCE_ANNOTATION.equals(annotationName)) { - requiredVersion = Math.max(requiredVersion, 3); + // Process it. VariableDeclarationFragment fragment = (VariableDeclarationFragment) field.fragments().get(0); - IDSReference goodOne = processReference(field, fragment.getName().getIdentifier(), fieldAnnotation, fieldAnnotationBinding, - refMap, factory, references, names, problems); + IDSReference goodOne = processReference(field, fragment.getName().getIdentifier(), + fieldAnnotation, fieldAnnotationBinding, refMap, factory, references, names, problems); + // If we found a valid reference, add it to the list to return. if (goodOne != null) { found.add(goodOne); } @@ -1578,6 +1585,7 @@ Collection processFieldReferences(TypeDeclaration type, boolean ch // Now process the super class for any found references there (if we have a super type). Type superT = type.getSuperclassType(); if (superT != null && superT.resolveBinding() != null) { + // We need to parse the file, so prepare it. final IType superType = (IType) superT.resolveBinding().getJavaElement(); ASTParser parser = ASTParser.newParser(AST.JLS4); parser.setResolveBindings(true); @@ -1757,6 +1765,10 @@ private IDSReference reference(String defaultName, String service, return reference; } + /* + * Get the contained type for a parameterized type. This to determine the service type from the parameterized + * indications. + */ private Type containedType(Type type, int index) { Type contained = null; if (type.isParameterizedType()) { @@ -1767,10 +1779,15 @@ private Type containedType(Type type, int index) { } return contained; } - + + /* + * Parse the field collection type for a specific field type. All according to SCR 1.3, section 112.3.3 + * Returns both the service type (as return value) and the field collection type in the passed string buffer. + */ private ITypeBinding getFieldCollectionType(Type type, StringBuffer fct) { if (type == null) return null; ITypeBinding binding = type.resolveBinding(); + if (binding == null) return null; String containedName = binding.getBinaryName(); Type serviceType = null; String fieldCollectionType = null; @@ -1798,6 +1815,11 @@ else if ("org.osgi.service.component.ComponentServiceObjects".equals(containedNa return (serviceType == null) ? null : serviceType.resolveBinding(); } + /* + * Process a field declaration with a reference annotation. As indicated in the SCR specification v1.3, + * fields can be either collection types (for multiple references) or single object types. Both types + * are handled here, generating warnings and errors on the way. + */ private IDSReference processReference(FieldDeclaration field, String fieldName, Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, IDSDocumentFactory factory, Collection collector, Map names, @@ -1828,6 +1850,9 @@ private IDSReference processReference(FieldDeclaration field, String fieldName, StringBuffer fieldCollectionType = new StringBuffer(); ReferenceCardinality cardinality = _cardinality(params); if (isCollection) { + // If the cardinality specifies a 1..1 relation, we should generate a warning (the SCR handler + // probably will fail anyway). If no cardinality is specified, we assume at least one (1..n) since + // we are processing a collection. AFAIK not in the specifications, but seems logical. if (ReferenceCardinality.MANDATORY.equals(cardinality)) { reportProblem(annotation, "cardinality", ValidationErrorLevel.warning, problems, NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); @@ -1835,14 +1860,15 @@ private IDSReference processReference(FieldDeclaration field, String fieldName, else if (cardinality == null) { params.put("cardinality", ReferenceCardinality.AT_LEAST_ONE); } - // Collection type. Check whether it is a parameterized type with one parameter containing the - // service type or one of the other options for injecting a service. + // Since it is a collection, we can check the type of the collection from the parameterized value (if present). + // This determines the collection type. defaultService = getFieldCollectionType(containedType(type, 0), fieldCollectionType); } else { // No collection. If it is not one of the known types, like service reference, etc., // just get the type of the field itself. defaultService = getFieldCollectionType(type, null); + // We cannot have a multiple relation here. if (EnumSet.of(ReferenceCardinality.AT_LEAST_ONE, ReferenceCardinality.MULTIPLE).contains(cardinality)) { reportProblem(annotation, "cardinality", ValidationErrorLevel.error, problems, NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); @@ -1853,12 +1879,14 @@ else if (cardinality == null) { reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, Messages.AnnotationProcessor_dynamicShouldBeVolatile); } + // Impossible to have a final modifier since it cannot be replaced then. if ((field.getModifiers() & Modifier.FINAL) != 0) { reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, Messages.AnnotationProcessor_referenceAndFinal); } // Check the service specification. If the service is specified, this is the easiest solution, since the programmer - // specifies what the service is. + // specifies what the service is. If this one is unavailable and we could not determine the service + // type above, report and issue. Object s = params.get("service"); IDSReference reference = null; if (s == null && defaultService == null) { @@ -1885,7 +1913,8 @@ else if (cardinality == null) { reference = reference(fieldName, serviceName, annotation, params, refMap, factory, collector, names, problems); // Add some attributes because of field injection. Note that these attributes - // are not yet known in the current eclipse version (Mars) and therefore used by hand. + // are not known by the IDS stuff in the current version, and are therefore created by hand + // in stead of convenience methods. reference.setXMLAttribute("field", fieldName); if (fieldCollectionType.length() > 0) { reference.setXMLAttribute("field-collection-type", fieldCollectionType.toString()); @@ -1893,6 +1922,7 @@ else if (cardinality == null) { else { removeAttribute(reference, "field-collection-type", null); } + // Field option: do we want the field replaced or updated? Only works for collections. FieldOption fieldOption = null; Object value = params.get("fieldOption"); if (value instanceof IVariableBinding) { @@ -1900,6 +1930,15 @@ else if (cardinality == null) { fieldOption = FieldOption.valueOf(scopeBinding.getName()); } if (fieldOption != null && FieldOption.UPDATE.equals(fieldOption)) { + if (!isCollection) { + reportProblem(annotation, "fieldOption", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_updateOnlyForCollections); + } + ReferencePolicy policy = _policy(params); + if (!ReferencePolicy.DYNAMIC.equals(policy)) { + reportProblem(annotation, "fieldOption", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_updateOnlyForDynamic); + } reference.setXMLAttribute("field-option", fieldOption.toString()); } else { diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java index ba976e5..e2de891 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/Messages.java @@ -95,6 +95,10 @@ public class Messages extends NLS { public static String AnnotationProcessor_dynamicShouldBeVolatile; public static String AnnotationProcessor_referenceAndFinal; + + public static String AnnotationProcessor_updateOnlyForCollections; + + public static String AnnotationProcessor_updateOnlyForDynamic; public static String BuildPathMarkerResolutionGenerator_additionalBundleResolution_description; diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties index a0e5bea..9c61502 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties @@ -46,6 +46,8 @@ AnnotationProcessor_unknownServiceType=Cannot determine the implicit service typ AnnotationProcessor_cardinalityMismatch=Cardinality ''{0}'' does not match the field type AnnotationProcessor_dynamicShouldBeVolatile=Dynamic fields must be marked 'volatile' AnnotationProcessor_referenceAndFinal=Reference annotated fields cannot be final +AnnotationProcessor_updateOnlyForCollections='update' field option only possible for collection types +AnnotationProcessor_updateOnlyForDynamic='update' field option only possible for dynamic references AnnotationProcessor_stringOrEmpty=String (or empty) AnnotationProcessor_unknownServiceTypeLabel= BuildPathMarkerResolutionGenerator_additionalBundleResolution_description=Add bundle {0}, which exports DS Annotations, as an Additional Bundle dependency visible at build time but not at runtime. From 2eaa1203a12e72a35554d804435449c65df5cdfc Mon Sep 17 00:00:00 2001 From: arievanwi Date: Tue, 12 Jan 2016 22:22:59 +0100 Subject: [PATCH 11/18] Additional testing. Bugfix service type handling. --- .../pde/internal/ds/AnnotationProcessor.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index 76a24e3..86ba025 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -1765,6 +1765,14 @@ private IDSReference reference(String defaultName, String service, return reference; } + private static ITypeBinding bindingExcludingObject(Type type) { + ITypeBinding b = type.resolveBinding(); + if (b == null || b.getBinaryName().equals("java.lang.Object")) { + b = null; + } + return b; + } + /* * Get the contained type for a parameterized type. This to determine the service type from the parameterized * indications. @@ -1775,6 +1783,9 @@ private Type containedType(Type type, int index) { ParameterizedType thisType = (ParameterizedType) type; if (thisType.typeArguments().size() > index) { contained = (Type) thisType.typeArguments().get(index); + if (bindingExcludingObject(contained) == null) { + contained = null; + } } } return contained; @@ -1786,7 +1797,7 @@ private Type containedType(Type type, int index) { */ private ITypeBinding getFieldCollectionType(Type type, StringBuffer fct) { if (type == null) return null; - ITypeBinding binding = type.resolveBinding(); + ITypeBinding binding = bindingExcludingObject(type); if (binding == null) return null; String containedName = binding.getBinaryName(); Type serviceType = null; @@ -1812,7 +1823,7 @@ else if ("org.osgi.service.component.ComponentServiceObjects".equals(containedNa if (fieldCollectionType != null && fct != null) { fct.append(fieldCollectionType); } - return (serviceType == null) ? null : serviceType.resolveBinding(); + return (serviceType == null) ? null : bindingExcludingObject(serviceType); } /* @@ -1902,7 +1913,7 @@ else if (cardinality == null) { else { ITypeBinding serviceType = (ITypeBinding) s; // Check the type compatibility and report a problem if not. - if (!defaultService.isAssignmentCompatible(serviceType)) { + if (defaultService != null && !defaultService.isAssignmentCompatible(serviceType)) { reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, defaultService.getName(), serviceType.getName())); From ec17d47b0470d9ae4ce22dca793d27f53c2102be Mon Sep 17 00:00:00 2001 From: arievanwi Date: Wed, 13 Jan 2016 21:43:33 +0100 Subject: [PATCH 12/18] Updated message for classpath checkbox. --- .../src/ca/ecliptical/pde/internal/ds/messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties index 9c61502..cd03f83 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/messages.properties @@ -65,7 +65,7 @@ DSAnnotationCompilationParticipant_buildpathProblemMarker_location=Build Path DSAnnotationCompilationParticipant_buildpathProblemMarker_message=DS Annotations missing from permanent build path DSAnnotationPreferenceListener_jobName=Build DSAnnotationPreferenceListener_taskName=Build -DSAnnotationPropertyPage_classpathCheckbox_text=Add DS Annotations to classpath +DSAnnotationPropertyPage_classpathCheckbox_text=Add DS Annotations (v1.3) to classpath DSAnnotationPropertyPage_enableCheckbox_text=Generate descriptors from annotated sources DSAnnotationPropertyPage_errorLevelError=Error DSAnnotationPropertyPage_errorLevelLabel_text=Annotation problem level: From 7f649e5760084e81664239e3d11093d16c209e52 Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Thu, 14 Jan 2016 14:38:12 +0100 Subject: [PATCH 13/18] Updated the readme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d66f01b..734ec26 100644 --- a/README.md +++ b/README.md @@ -23,19 +23,21 @@ Note that the descriptor files generated from source annotations are overwritten ----- * By default the plug-in makes DS Annotation types available to all PDE Plug-in projects in the workspace. However, you must make sure they are also added to your project's "permanent" build path used by external builders outside of the workbench. There are several ways to accomplish that. E.g., -*A. Additional bundle*: Add bundle _ca.ecliptical.pde.ds.lib_ under _Automated Management of Dependencies_ in your Plug-in Manifest Editor's _Dependencies_ tab. +*A. Additional bundle*: Add bundle _ca.ecliptical.pde.ds.classpath_ under _Automated Management of Dependencies_ in your Plug-in Manifest Editor's _Dependencies_ tab. *B. Extra library*: Add the following line to your Plug-in project's build.properties file: - extra.. = platform:/plugin/ca.ecliptical.pde.ds.lib/annotations.jar + extra.. = platform:/plugin/ca.ecliptical.pde.ds.classpath/org.osgi.service.component.annotations-1.3.0.jar > Or: - jars.extra.classpath = platform:/plugin/ca.ecliptical.pde.ds.lib/annotations.jar + jars.extra.classpath = platform:/plugin/ca.ecliptical.pde.ds.classpath/org.osgi.service.component.annotations-1.3.0.jar + +or any other platform:/ URL pointing to the annotations jar file. See [PDE documentation](http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.pde.doc.user%2Freference%2Fpde_feature_generating_build.htm "Feature and Plug-in Build Configuration Properties") for more details. -*C. Import package*: In your Plug-in Manifest Editor's _Dependencies_ tab import package _org.osgi.service.component.annotations_ and mark it as optional. This is the least preferred option as it unnecessarily modifies your bundle's runtime classpath (in META-INF/MANIFEST.MF). +*C. Import package*: In your Plug-in Manifest Editor's _Dependencies_ tab import package _org.osgi.service.component.annotations_ and mark it as optional. This is the least preferred option as it unnecessarily modifies your bundle's runtime classpath (in META-INF/MANIFEST.MF). Note that in this case, you need to add the correct SCR version annotation package to your target environment. ## License From cdbd28435264da2b9d50fb0a02f367f46ccbdd55 Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Mon, 8 Feb 2016 11:23:55 +0100 Subject: [PATCH 14/18] Revert "Removed the library stuff, since we export what we need from the DS plugin" This reverts commit 9caffeab7872b90a5f1d6f9b699daf328699f1a7. --- ca.ecliptical.pde.ds.lib/.classpath | 7 + ca.ecliptical.pde.ds.lib/.project | 28 +++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../.settings/org.eclipse.pde.core.prefs | 3 + ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF | 10 + .../OSGI-INF/l10n/bundle.properties | 3 + ca.ecliptical.pde.ds.lib/about.html | 67 ++++++ .../about_files/LICENSE-2.0.txt | 202 ++++++++++++++++++ ca.ecliptical.pde.ds.lib/annotations.jar | Bin 0 -> 12375 bytes ca.ecliptical.pde.ds.lib/annotationssrc.zip | Bin 0 -> 12230 bytes ca.ecliptical.pde.ds.lib/build.properties | 6 + 11 files changed, 333 insertions(+) create mode 100644 ca.ecliptical.pde.ds.lib/.classpath create mode 100644 ca.ecliptical.pde.ds.lib/.project create mode 100644 ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs create mode 100644 ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs create mode 100644 ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF create mode 100644 ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties create mode 100644 ca.ecliptical.pde.ds.lib/about.html create mode 100644 ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt create mode 100644 ca.ecliptical.pde.ds.lib/annotations.jar create mode 100644 ca.ecliptical.pde.ds.lib/annotationssrc.zip create mode 100644 ca.ecliptical.pde.ds.lib/build.properties diff --git a/ca.ecliptical.pde.ds.lib/.classpath b/ca.ecliptical.pde.ds.lib/.classpath new file mode 100644 index 0000000..26d3d53 --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ca.ecliptical.pde.ds.lib/.project b/ca.ecliptical.pde.ds.lib/.project new file mode 100644 index 0000000..f3bd272 --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/.project @@ -0,0 +1,28 @@ + + + ca.ecliptical.pde.ds.lib + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs b/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..af0f20f --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs b/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 0000000..f29e940 --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF new file mode 100644 index 0000000..397d3f3 --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Bundle-SymbolicName: ca.ecliptical.pde.ds.lib +Bundle-Version: 1.0.0.v20141223-1920 +Bundle-ClassPath: annotations.jar +Bundle-Vendor: %Bundle-Vendor +Export-Package: org.osgi.service.component.annotations;version="1.2.0" +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Import-Package: org.osgi.service.component.annotations;version="[1.2.0,1.3.0)" diff --git a/ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties b/ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties new file mode 100644 index 0000000..f1e8f1c --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/OSGI-INF/l10n/bundle.properties @@ -0,0 +1,3 @@ +#Properties file for ca.ecliptical.pde.ds.lib +Bundle-Vendor = Ecliptical Software Inc. +Bundle-Name = Annotations for Declarative Services \ No newline at end of file diff --git a/ca.ecliptical.pde.ds.lib/about.html b/ca.ecliptical.pde.ds.lib/about.html new file mode 100644 index 0000000..cd0c528 --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/about.html @@ -0,0 +1,67 @@ + + + + +About + + +

About This Content

+ +

August 26, 2014

+

License

+ +

Ecliptical Software Inc. makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from Ecliptical Software Inc., the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ +

Third Party Content

+ +

The Content includes items that have been sourced from third parties as set out below. If you +did not receive this Content directly from Ecliptical Software Inc., the following is provided +for informational purposes only, and you should look to the Redistributor’s license for +terms and conditions of use.

+ +

OSGi Materials

+ +

The following files (which may not be present in all cases):

+ +
    +
  • annotations.jar
  • +
  • annotationssrc.zip
  • +
+ +

shall be defined as the "OSGi Materials." The OSGi Materials are:

+ +
+Copyright (c) 2000, 2012 +

+OSGi Alliance +Bishop Ranch 6
+2400 Camino Ramon, Suite 375
+San Ramon, CA 94583 USA +

+All Rights Reserved. +
+ +

The OSGi Materials are provided to you under the terms and conditions of the Apache License, Version 2.0. A copy of the license is contained +in the file LICENSE-2.0.txt and is also available at http://www.apache.org/licenses/LICENSE-2.0.html.

+ +

Implementation of certain elements of the OSGi Materials may be subject to third party intellectual property rights, including without limitation, patent rights (such a third party may +or may not be a member of the OSGi Alliance). The OSGi Alliance and its members are not responsible and shall not be held responsible in any manner for identifying or failing to identify any or all such third party +intellectual property rights.

+ +OSGi™ is a trademark, registered trademark, or service mark of The OSGi Alliance in the US and other countries. Java is a trademark, +registered trademark, or service mark of Sun Microsystems, Inc. in the US and other countries. All other trademarks, registered trademarks, or +service marks used in the Content are the property of their respective owners and are hereby recognized. + + \ No newline at end of file diff --git a/ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt b/ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/ca.ecliptical.pde.ds.lib/about_files/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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. diff --git a/ca.ecliptical.pde.ds.lib/annotations.jar b/ca.ecliptical.pde.ds.lib/annotations.jar new file mode 100644 index 0000000000000000000000000000000000000000..6630de342e38bdf2486be59a13b804a7c02ad158 GIT binary patch literal 12375 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 literal 0 HcmV?d00001 diff --git a/ca.ecliptical.pde.ds.lib/annotationssrc.zip b/ca.ecliptical.pde.ds.lib/annotationssrc.zip new file mode 100644 index 0000000000000000000000000000000000000000..bde5c241712b080f205c4bd077cc80264ba98d6c GIT binary patch literal 12230 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

Ow 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 Date: Mon, 8 Feb 2016 11:40:20 +0100 Subject: [PATCH 15/18] Remove super type processing. --- ca.ecliptical.pde.ds/META-INF/MANIFEST.MF | 2 +- .../pde/internal/ds/AnnotationProcessor.java | 22 ------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF index 512722d..a341204 100644 --- a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF +++ b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: ca.ecliptical.pde.ds;singleton:=true -Bundle-Version: 1.3.0.qualifier +Bundle-Version: 1.3.1.qualifier Bundle-Activator: ca.ecliptical.pde.internal.ds.Activator Bundle-Vendor: %Bundle-Vendor Require-Bundle: org.eclipse.ui;bundle-version="[3.105.0,4.0.0)", diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index 86ba025..c79f697 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -1582,28 +1582,6 @@ Collection processFieldReferences(TypeDeclaration type, boolean ch } } } - // Now process the super class for any found references there (if we have a super type). - Type superT = type.getSuperclassType(); - if (superT != null && superT.resolveBinding() != null) { - // We need to parse the file, so prepare it. - final IType superType = (IType) superT.resolveBinding().getJavaElement(); - ASTParser parser = ASTParser.newParser(AST.JLS4); - parser.setResolveBindings(true); - parser.setSource(superType.getCompilationUnit()); - ASTNode unitNode = parser.createAST(new NullProgressMonitor()); - unitNode.accept(new ASTVisitor() { - @Override - public boolean visit(TypeDeclaration node) { - ITypeBinding thisType = node.resolveBinding(); - if (thisType != null && thisType.getQualifiedName().equals(superType.getFullyQualifiedName())) { - // Process the fields of this one as well. However, limit the fields to only the - // ones that are protected and public. - found.addAll(processFieldReferences(node, true, refMap, factory, references, names, problems)); - } - return false; - } - }); - } return found; } From 98d0d8229db21d71ce74b36a1622ac02dddee595 Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Mon, 8 Feb 2016 11:50:46 +0100 Subject: [PATCH 16/18] Updated source/annotations. --- ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF | 20 +++++++++--------- ca.ecliptical.pde.ds.lib/annotationssrc.zip | Bin 12230 -> 18186 bytes ...gi.service.component.annotations-1.3.0.jar | Bin 0 -> 35919 bytes 3 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 ca.ecliptical.pde.ds.lib/org.osgi.service.component.annotations-1.3.0.jar diff --git a/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF index 397d3f3..4bd032c 100644 --- a/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF +++ b/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF @@ -1,10 +1,10 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: %Bundle-Name -Bundle-SymbolicName: ca.ecliptical.pde.ds.lib -Bundle-Version: 1.0.0.v20141223-1920 -Bundle-ClassPath: annotations.jar -Bundle-Vendor: %Bundle-Vendor -Export-Package: org.osgi.service.component.annotations;version="1.2.0" -Bundle-RequiredExecutionEnvironment: J2SE-1.5 -Import-Package: org.osgi.service.component.annotations;version="[1.2.0,1.3.0)" +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Bundle-SymbolicName: ca.ecliptical.pde.ds.lib +Bundle-Version: 1.3.1 +Bundle-ClassPath: annotations.jar +Bundle-Vendor: %Bundle-Vendor +Export-Package: org.osgi.service.component.annotations;version="1.3.0" +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Import-Package: org.osgi.service.component.annotations;version="[1.3.0,1.4.0)" diff --git a/ca.ecliptical.pde.ds.lib/annotationssrc.zip b/ca.ecliptical.pde.ds.lib/annotationssrc.zip index bde5c241712b080f205c4bd077cc80264ba98d6c..df492b461d51f95f999396ae0401a38eb6467997 100644 GIT binary patch literal 18186 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

Ow za^ABoA-ae;A$rS4+e@TjzXzMNZXD=Y;5z^JxYw0OgxMOrm)5ML`QU!lxd?D5EUCt; zL@ivJ84y9dp$h1upPn>js+KS9fMko&7l2hjDqaCzr5DrvEB#OV($us0hQn^DEARA% zyC$jR3kR0z(pCD%!cTmeKpZ`)wog10WviQK1X=xZ^BFSi%|QW;Auu-cMg9g3V7F}& zNIBC)zdz86@Z6H0?*kU=`?ljhK(ER`GoTaD&g9?kHWoGH-q9=aN1a|{iRu1vYM0aE zhAdO%s>XGz6HS5$OG=4&2nura{=j^ooWxsq7sx$Hgi>;>cR3SF#L#+O8`944JR)C6 z!KAwMM^DfvZ8n^PsyWM zXXS0hhH|>kXPINStp6gB^T-X_^TQA}8tw;ZhAM^7_i3WFLR9!qp^vaL(43T@Y8N;0 zAFwRRlydcpk}gX_glti3eOmc{W zBt|bR%aDWdj$A1(I;204jkartXRl?N_@nfb8G^sf0-Nm-2wAb}{v5vV;W1Jvx+thr7= zX{E5GU}6Akx#S{e7J6LA9yLh^Z1N6RA*nJ(fF!%qftBcu{CyD2p=^Uq?0$|W85fO{ zwu?{pUOXw)D6D)d(=x;gowp*l$hBy(!44M?&UX&{QR@h(t-+0Gu)Mv}XRF1)seOIj zX?D?#wnSC*on5p)4(l|!`_oY1J?S16eJ6b0$TRSH55wCLr@OtKFRqY?QN+Vo{AZ={ z(-`JO_wJb?&zWm2{g48#$+zxaq1BFa*G5u7m3GvMZ?tCEH^tL!&Z4Z22bwYuYlY#T z<>!iAO+;c!wv}u~zl(-Rw3?rFMRb6&mdR7jmlgyRnscInmc^_dOUheHQl%>W!kn%?3-*>%VsBPur?a3tbrXwDGX^7QbxzIEDf3(Ml&tn=bg$Li5R^>v1>xONjoR&hfc zRs1c+&Yz(;?5Sk-7cFT+($awg($vI`6KRi<+?qf6}Uw9*g z8p~RLJ?sLtSZ9by95Y)V6GU~;@M)r29{E{OK&nv zq>UV2%;JxC!2gUNv| z{|UC%WVgwIsN~Y!s)XujgV4v^jSXMy$NLAEML+8xwWC&?VNS8@4AxJ&Bhytbj^MxnX0eLRPVzo(&3Z9G~&&buYWeTQVl!lsavoe%MB= z$lBVUkhLd*(p-Lj(e#l1Eu+0|bV=5cPN=k7jx-vmj4k1N2r;x-e@8m_w< zN$ELLL@_lx0=|HgpCIP3C(FO2>O;L~kXKZPLU*mtG%GrkbGc|N?kO&bB7IS>8xVv@ z5swiv`bFh?N1$ET!kK_8vbR=$=$VitT#C@Z4wU+5(Y`tyS~y)nO3q4(mVGz9i}^w> zN{2<`(FGP-^M!niW*$ZeGo86#_O>-%EcoYcym^b!J{3_$s|%UCe0+Hfd#gUin{IlM zs4<{12DG(Oj-Ak@;cCOmd)kczk_go^lGf@xOT1GISVJJux~l~9-wmDYs=rwRFVfw1 zBv`aOG2(L|CK^1DbIIr%G3s>PCkcngoKjN1m ziN`6drq`67?ways6mxs?>B)_xlLmRWgjGt@IQbW@ZCDB19qyA1kZEl#CVWGb=aQdIkuC4(&c@%8(#JETY*TDN-z+_&!9tn*vwEqh5D3um*E ztv6`QA`{Guz#*}C(TOU3_ZxFu_s?9}E4?cub`iSMNtZ95)YnN~$7B5{S$HT>$@9K7 z5zF(7MQPbaI@s4PD`nbelvJHRl9V2aqvtTt(YaLhMyl1(1a_8R2roHaG#kY2ayL)q z;GGCWQSW3PN#e0v+Gf?*@OvTni|bfAE_m(xfd8I@=afsbgYP+5^v=b3{}(y<$4p%5 zFDCBW>NnK?JqXRiX};8Q%~+6+YCX8c&8P$JN>Yg`qg>RI95_OKwe2pU6q{4f_iQ}z z`RTmtf=_t?cojEMtJ#&i!5zkD21pb&DZn?>>6CvVbUDj!k;EPs zu9b%Pl)S>Fh-<3H9k(c9Tp59q16y{AQDF%`acpwaJK@ z>!_M#tnJp|_Q;*P&5+n-5biqqSSr(u3I94zD?(7_jf~KcNJ>##vLa+Fhz@l;xj5-% zo~+WD?qB2Fh6aU0sj`HV(t0l1oJ-c?i!ay+i%_Hm=ExGbZ6=j1i+#Ya|JL!`8v3+q z#jaDm-vyCMm(GOM_LE1S@%yoRIs@TOorfbYDEU}9B0tApP(1SRv=jz*4>siUIrS=3 z$#_cflFMu)SV4K41)D1bLZ-A9VHHIn&R}w-vOL$@vftyL@ceGP%j@(z6jxmr7PFy@ z4EN}15kDg>7=tF5EZcSPIxl(CPMUlIZzAF>ncE(V;>IOc;Olo*x1Y?it>3KTdV;}9 zyT{x%FeBP3edxN;F4aiG((=mj>wE4-epm#$xyEF8K-cljmdaHS=i5~s(FS~p+x6E% zC?C#L+iOJ4JdYn9fv8&Jsi$YTr7BkT%QRwKn+rkf)F(V82IUSDTs^_9fUOUwzv`b} z)sK>v!kgVhpaB&;sbw*rwz(xKlV_atb@rgM3sh zQ?8B5BR1e`mz;8bSwW2j7gD8PzUA`FOn{j9%ZA|VNKx(L?5e2;&u;$RY`E>%ME%Bm zwvv59sLG}|*@TYPzX3m?$YjBIRvsaGlCg=^0A^Qs0^>`&f;_xfl~_FR4m z3VtTUMa12I5wRy^ke0N7{9#;=0gU?eXbczK<$QT${uvGGc`fpp^%+27e<@nQDbqP5 z4m>!h*AQ;3>LS`l?8~=~;g|NoeTDnIZUp-I-%7mK4V(A6A@skl8ww78UBmrbim|C? z^v{i2WY98_+s>F{BD+o$=q!F6TIyQRA^oU?G|!kQP??OTOFZFii&r{gpYjYT9dr=$ zeWljzE@AZvwGUR-wVSh1mlxYqM3bC|TvUxBWE>gFyew&q`2bSWDLRBgn@wj@Wc=_3 zFQcF_U8Kl~ZD$EaiGk(u*iHPB374)Vu;~&V3V-IJ^>OeHzF`@`<_lr6+l(eTyk$W= z2AJ=3y&Exxsh5g-vD;jYPYz;8MxReufeID1uA;D)NZyX$d$*hoU&v+m25nPf zdBhH_y98P$$T7+@F>OcQkYe=e>*tmo7jtE0AN6K`yBvPsbfIy=e&u`Vl!}1Lx8evZ zWM~naIF*?(8U8q~hawcDmc(6vzaRltJ~k;ZBFwhfmQ~HTXm_U6ld_`zM585EnpvS`JOxR7CqC84g%8DB z_94|CuM%M(aMlfnB#r|0`9BJvt$}=y%~NNi{I!oNOcwRQt0I)Lj49>C`v{%crWMVW z{akk?;g^P!DBT)W9@z-4;!7^|2Iw;H3_gb?n+MTrVs+}wMGV?`#*Zam03UK>f4463 zWnvY%zQpO_&5Kcg@zwMqj%QWt*@!z!ZyV{60l8N?P~Y}Ioy&OmB{0}dN$qxY0?PAl zk&M@(s80gn0p#xf1?|!No}lw|3mjCJ;XC687OIL(wHXTD%~zae5GXy9g{FnD9XdrB zP%uL9|LJrU`>$(LkoU7wkm7*9{{8O`St0-Hko8}VSpQY|_oKeQD;vJols{Dd*Au@# zlm7h>;P299aQ}hy|8y4cXF>eFzWjR-Zb*L|#Q(F*{4?#}_d0*qUPb+5?SI+w{F(Ca z!29pYJeYs1{QtqRCw{&2#~3l^ zykf+N=YHaqlLQ7q0f2yj05~pa6an~$fdT*mAT6xKPa`fPLMP2HBQ7GWs6;C*@-+?s z5Ur$byZ#f!TXdh-jZ3PChzuXZOs=R2#yK-DTkqoHkPWPg%*6zo5^&Cijz%AFF0S&M6WSl_&$Pcx7_J)P@)k|fxuNH}WhTSBg`P>QjsOI4$`=0OD z*Rb5Ocj;N2L+Ggt3Ix)|%^yFF5AJ`fzle*Urhw`dY^ES5Io`!NQG+Gnj9tVzu6|rN zxpZW-0e~E>mA8tYR6vP}0&+XqZpf=nh_FJTkNhwXAu-Fb$>*_Oh<>eZ&hnVgQ(X{_ za-o#1g0=4ARW==v=H0sE8n8Pjqzu+;RH!yb9MIO{x45Xy|5b`7tLx7@0JOmVL2@rw zgf#AM^OOyDlRjD$p-VlO`EYB6dNsISeOJ&MsJm(-jY=G1eErnd9b5Ned6|EmO~hnc zhg*?Up|IPi*}!`{(z*lbO_1qIm3w*dC=Nb90I_MeU{%EYYs**zs)ZJrMq)39c7Vg` z>4;LKoHb^cNs5k}0SdF&a3_+8BO+vuL)tt8cDZoRj!ExoIpK5L9uyxjZ9VB^6X#sI z%IPjke*ikoI3AzOG6%b4zi8oemBwXVFD?>wfnlcsgu`{uiTC5%ZjVi-R8^kAW!Sz` zvOAtzeJbmEj`1fcPYl44&1W1#Z2Ttzn48~cJ8U=aBb_z&7oeOZID}Am+(;4-000{( z0D#=zS2h`vv+zGGYyj{-AEd+ug=G|lw=^^zH-=GtUTRkzN)*k(O0t>Eu}{M54@0U2 z&4t=*$Pzj+xF>PBe^B0itEQ@i|&j9h=KNS82+U#>Nr+-Kip4ME?v^$62VWFy@; zmvfZra6KIICONM=?O(iI2z*Gp6gGn2JzcEqaJ8AS!hB|`);_;~YR$7a8n)D8ZI5?( z_(R{+-uP%l4xLMMcByOA+@!KrHo3VvlJiv6LfgA~d%8M4?a}yX&nl`;JbiB6hr++Y zLcUti)+kkTa{D>heDzy0K9FZXwj@duYmjo%_lkoXYmytOhR-{p)*?048#q-Us5~tz z8HVek>!9#xnzO2PVcvNXW|!GXDu++;S1FGPbOXIDM{Q)k%A{fOn7K_S8CJIMsnHqA zqEJcu)_HVb2d%k|L&-s+o$nDGvRy_keV2T8W;s%YWaEHXwKpN5xoCQfZV)Q+SsN4A zh(CrS8HR$BTqVbmibHDnVeYEa4fg`;@K@v5!h+#enL`m)L*Zj7wD&ITwiv4R{hDvA zKI%L*YcqbvDugW3GMf$B zJaB0j6>qBP%jo(=8qxjw{B%QS&Ix?1KLE~uunShl@f%>%C>a`w|4_tPFGwMfihQ1t zJ8{C7cSSK}i!Tl-ii^m~>Jz8y9MRmU&PF_gHV0>(fyr%HR*+d{^t zx{+IlMnbk?po`E=VtySMUb&!;+9#LBN6yw_c}%ZOt1r-eyNdQpxDVV`Ln{hi3rmgH z`pq%8MmR_y`8jqkg%H02TdePTG=(~MF)e!Tyi|K9DX~l)Ljg>MX?#cMcmPwsT5|B| zIcS;Ry{A`la3?mElr%$jrw@j{OwS9D6qkFF^J#Vv#5Bcz*ZuScKy_yy^&0Acaa>|s zqteLtyaUBRb11-dNjynLgTa0_dSGsB{n(mNc*{mA>@aGYqXPVzbo)SXd3eSf1JiW~ zdAR*q==+J0-KupD#3YJ_Zjs^jP2b^C!xW(cgDF#wwfov(H_<^` zd54&&6Jw6^TL!>DKMjtyqKJ7$TX7cR8J^bE)k8ZJQ5};rEA1;bB7E=IUsFJ-r`iGj zlH>p+ItN2%<@>>}q4J9e)d8HfZ21TQj@ea7O+ca|{tMEDSB1xfD&F!)gpL6tU5MYr zx)IxM)4@sU$b2N_c79@352>05#5#d-x7=o|Nro1hxv4N+9n!I*2cJ$8TE8 z0@wQ>TzI?Xh>6#LKK>{XcP7cPAI)VJpAmdZ0%YqO_(rRjVF^A>Z`J|EpU6KT2D?CV z?m?aL22dMZpYts*>JiwT!4yDDn+NSO+|yl?x%%7v|t2y(0%+rA<2(pg4BdQDxEFL!p10@p$}=N0`L zX*$jzoMb?f%Yz;({4Q&eZZ{BACxHu?74i4j4 zO%;9{(l0536YQ5rr8=UP=Ha$fqn}(N<|2o(ADCQp!o)8&i@hT(c3}R5#DnFhbtg%kRTf4z8J&?~YgF6osV-Uhy13+sGO@?(Y&0-pI@F~~Q$ z+pRosSGkLLN38qgSVai`q)36Io# z&6Y?5R33Y5KW3L2mZm=MubyBMZtMv>4cFhe;~Y%i57(0Ud()-Z>#5(UkWn>cA$gKe zpVEGuE~+;WQHW6)CAu`b=MD-CmZj2tR+pnfSFb) z;TuCCLYxAusT0hq(A~v_o@T=AQBVw){ zl`A=a-6R_1kTQ9ww;HYJ_tm^~y$_o#0$J(`?j}KoYVHOWJv1%wff~V9E^fE50=CW; zk^(@x{tn=brH_We3Bk|{X&4h)pgc`a17E#k`&#`eyQP`MEPqZJ);wRA1KB&bOXUa( zb$2qVJRF}Tvdp31W{Tm3b(P)5U_ig79bZ0UrdZTiUaro?H5YNX(3U1a0^c%S;x6u= zxKSaF8dHUBIwn+1A46uCzFLms41^r&B{>z-1G%fiT+hdfj?Yn65R+Acr(dXG_%4am z0G+_L!-mr^YL%88U7v%aO ze+9dg8ogHnv*03mg`0w?FcmEamsNqH3!;x@M0f|7Z3#uXdeNM5l?C?PK)f@`DQeHf8PLYIC{u}icDbExg(o2Aq5MAk$PC`-gNblQI{O;XI9|=FAqQ?`Uh3ru=bzUj zN1w5*_}4=tLt9%0&OMzvk(nJI8ehlRYE?dHzPdFve%EH*fIfp~Ws>A`K~%~tWYU;I zjtZD|=K#{Q=@D2&o>29AlkZ2L?-xK--5zaPT^*V=Uml+ePv_kTyxKcmpNNfGA9Y?m zo!{vKr(5?v&RxeV;AlF-SNm`{&wHQyGMhfF9bM|Wj!j1!{b06eeBXF1OHLPjtc;te zG6SGorqZ^|^Nt*Luyet&av!9FQ^&6})p8?@O(~sC)gaV@D*7kUaH#Gezz}^14r!F5}K0iHV#=+r? z117MfvERc1+EkhyIJ5T-&Tl37JlK1mpl+5MHcZ7R2%5c;{XjxY+uaCHg294@)3HyZ z4=g5JkJW|@tA{lY4n$I`*hQi+->|pbO-))eWMOD?x{%>D_Uf?*06UmV)-T9%aeV>` ztbXh4Cn2!+g+;6=uofO!kYsvXBDLicxgAR*bb|KoPF?k3Q-S*j+J}LBx*?nx59+s9 z;W23^EPp-0jsn;Q-8uxK&tTqeX;Dj5L7D~ob+$ebp`KI!;JJ=#mtA;z{dK!`(cCLA zkxkD9S}cBY_$%9_rgy_a*bPWd6l=Uj$tx^pBoFTs4#lUJrC3Bj;|h2K@5B|29C z(bheXKX}0DTN^f{6eXgyMhESeh zWiH}Jwy5QTb$TuiFqxog%oHWOeB?sg-SO_22*)fSO`H|h*auJKa1@J(Hk)6qs;Zjm zOc$7F51ozeUi=a7P9C(m20)m{Z7tnl?e(5*(KydGY2VH#IxWSACCY-iK;9DAuVDdf zo}hA(c#b!X$`S`4C=e3Pz%MY{AE-jdm#Mm@h_7Z0dzD*+AjtU@H@|<$c+k?;NWFx< zcJrka^1t5Df;wL;mT=^uBS7-q72Tm z;KIE*=sN1}vc#DwA`ERjg0rn+$Cx+Xrvra|SN;(22+R>}sp^?O;1R2+GR+8)jqgFA zq`mcjojy8xp&)uBh5*s({&?k@b=!JlNy?)aQ7U31a{e?CTwq|wTnpw)5rlY|>2mmu z*ZOYZ{fVShLaN(8o4b$qmCcBf7`-U7W7jVk_~BoQ@%k;+1AIr;P@SAnh7gct20I9Y zQ@s&`V8;iUN9{CX`}}4uzJGE3sP%yU{Wpc)*tj2J4G#dIL;a5m{W##C*%Ra+h5mQ; z1pG(JgG2l!;QuK5zm5MP?f*6KPv-v-Xsz#JY(w|&gk$`_!)+Z*X>A=%&Ht@Dy#I?l z|CNx|(b&Po+|ZcT(AL_{*2dVziB{jn#@0#S$=ue)@qaztzo5FmxZU6XuK)Pe|1}HU ze?9zfrn9rPrggKnN>WxUvRyf|-zko$u5%?eqE3{*kqn0&S7~M(oZf zSN+Mc5v&I-fVC_Uoo?(!UT=^3cK6C>V~3}%U~K0W7fzjxo;&H$_10EUZs^khKEj?>Av&Ok!`Z8G&85b$Cn9-@IPR*_JqAmHn zfX8)GbnE@yS*E~C?JHqP#?dNDMAYYIA}dVz;X^d)sDE zv|}kr&LC7+@0%_Q1zbjXZAZJx-MVZPx_tuskLtdv=lK^D?^WTzeQgcs#hoxnQ{SR~UTtTWfRb0?LX^Af-&g zjRz1;jZxug%A}xiQO0kbevhWq&fm*IE3uLvL?Nz`D<)K>rf4C=`EG8Kw?9n zc$3A1D9HLZmBVDyl*2v0w4=Uyk)jwZ?^?*3hzOO{41?A}xSs0nP-QQvMgLsScaS&w z;^NlDuHP66^A9IDDo3|^_RPzQB5a%|+h`9`1HFp%o?IcK61$?ekbe00pKnR>*cA$k ziEXtZ>ghM{`+@Etbny=vUNh@N%p0|K_Mozum{^Sd!6=C7L8s|p>Iw>$Oe(gA+;@Gy z;MRB3+X9@+nSxe{@)R|~svN@7FDG7h$#^%Out^BN<(4xTuUzxbLF=N0- zh@}eHphnz0xIzLo5Lj;t^0}*`xZNLRUg*%*7N}NUJeP}Dq*{zM-Z!E`UVoXQ8jH!L z-D(v|VcfO9Q{Pmx#t7Q7yhR?Lm8&lYK7s$W&3Yy2ci#Ou#|-|{HanHb{ZHPTz<;y8 z1pj`N_^ZIKG&F5DMp1p5eTVqqT(0=?9cRc~w5XbYDPN4%6gy#~cw@v9o3o&d9l=zW zg?xF=q+^GokZOj@=kcL;G*eG!UAWe44vcgh(z8o82v3tMia!qkJFvedC(7VBW&}lD z^7{V1RL|_4Q3VI|k*>KbB3O~C`v?&Rl;a~p=Q0Y{L;R5tb|fT&d1k~EqFD1Q!!GNJKLlMJtvdp#TBK)JkB8$JpN;Or01+P9kNRj%Yo7q;yBG zB!_fAG%F^W&~Fcg&hTn^iBF=0mA}NR_@lu!H1y$(D^FsdV2V7no`(vK&ck%;EAJho zh^2*xoE|yg@G>^wB=63Q-waG4*a_s(%)aP7B12JZcpXN_lidaGSncB7#i8Q? z!3^0n5J-4@D!WIbPEH+);wko9T@1yaY{9;LlM>1hoRyT@1K%+X$REsBt&nzn0jIRZ zRR%UWm`=8NBcMNoMKmxB)N?}ofb{rD$Drdph7l&H?&B>#e-d!R;4n}Hkq{pv`e`yq z8-fDDXa{>@0UF1Svm@(IGm-*vfyKwKLJ%M^lHZpDAT-Yc!9fO@C(F+;KL(_j)5XojQ zyjWhtPt$S)_VrztKRo|AIlZ1V#n48;%0$~(A2YyYK+b#R?fI|=;hTexi(gTTbDgbt zeZ@)@yexaNR6)z0h7~~t7EClEOH@XaAB8>=`cV|+X;>zPeqi5=Z0Z8ZsFB?Q}om zyS9-7ZJX+h9-zlCg&uUlHQHxe=|&E0oEwbPx))KvQmC7*MvwAlCS!}wtzw2s8~316 z2tmgV7omWhI-jL-4<3yq=wzLj#dyoQXd8x5GVU%7Kib2AUnVncD`t2WMZw6EZ+*)PtEoL`SC{Al1CBIup2qlPC$>(XB5HY(N4Vymqh(V?SbJm z6Tiu49Djtcg;=t?vhid3Zrn9ItJs`Hldd=lrZe|5_5p!7_uxOGq`dg zRv+6*0JBJ*-~#YSUh3ycX**RljqRlrw${Q>N?*FzX-Tnx`uXNUf68~)FL6uERy=$s zJK;_F`IG?##c~F&AENk_UNHNrH*r}ey9+J!032$996S|}D@)Czc6F@4Rb3tXK&}mI zz+|b(I%Z~GR($-wD8_zVPWF=HWO!D0n^jJwz9T7Hc@Bt+xYGjN(+`ot4dv%uNpZ_8 zybx!!tUn+0XikB?!FjVhmiz=>ov;k`qY~xz=X?QO>V4HzI@VGaA&CJ?hT_aE5m-@o zqr3&aO)Jk1e5PW`L!2VMNB0o)_VPWXU!b5jvX2xcMy)ww0X{u_(#I_n1B&feF*+cbN-%EXmWsm441M9RUY&xv_cG`46rGA6DkV5f%iN(&whe5E*sfS% zGCV&VXiQ67BwjQCvjMvuwJO$BN~l3mGYYh%TEQSRj8lXt^v(;@AXCiH_YJs0d;Ie_ zTvXy2a6hL7%nRX8#Hiw3%aHj)572%VDHf_NVZTR!VTB-?rCC~OJxxOnpsFwkdse3GO*9P?P+XxLw;HCVFG5Nub3yfC)V) zAjT4?)4-IFaMU{(TxkAVx5|+4QA45DYsCrPons-v)3#E{e&Q)%B>7jyowi9xUFf>~ zi)Bhv{5ZZ5tU}1ygNLmuYXy3m86PhIxH73@33ThC<~ofIS$S7w{ys6{cuTgA>X614 zo7{J73zDsizIxvP+*V?aPkCT- z4@aA0ll9Q@K)S*x$^LgZ>I+MS1uB*1M@iL#9_JmZmOBtq1DGr1urEfIE3XTWF3d%1 zyK;AhWBYvl9LJk(Qj(?t`vmTgF|2eSqT2+`EM z1I(fnUb*=^PbFRY=2{t!T1fQH21t2RFFpS$y+j~wlby0&gkltt;WDXzjBkd>xZJTH zBPyKZWu}&mErUxTnVD%lBDggT`P265bH9hivRbV=b893t20ED2h;Y2Z&XQYI;SR|C zJpWFt=?IWSX`$*y1hwlo@Lz{u^A)t3WGDcDQL=x8fG$PGf1P{(KBWFU{hc-Zg$({u z{eKhf|0TEohd};EAuDKWV`6UV?C>{`CTDA9Zs`6WdQ(~7aa{!AGsN)-jH`ehk_a+Cv%w#-WT!8TIjO84heK zvw%Wmlu!ROz?Xnwv5FmLY~BdqR^8H7k6~E0jUKT}USr@ek-ivoa23L>V@MNlIl)3E zhn!`x<~s*qaC+~EU%sl0Tx(uXhRTpR0R+0(`^32W@~D$1GP*CwJXD`TiUL4 zDfC4AFy~PZIaCd3uy6CC{1{bE1m^nj!H8Cs0#IwdBrx+CbCWHsvm#1xJ?A|Q{WyIG z^X&M_;p}jiU9sw1usACLepYq9_S62_F(Pdkj6=cp5AWx?Aoww_9I4bZBftjs&B>CM zGlpzw^CxVQh1gqBjxJn)1S2JI-jxtef{cWtO<6>=FjEw$u2Lh*Nh3l=3+d!&3Pg!6 z;leWFvN@}wqk0XZG)5^>)_KSiUGD{SbSx3XvyX6pu`{&|g<&hQh%onVjeLiCxx+y2 z$qouE9gxZjft2XGn?0Sx(+=_+-5RQL9(gH0w;C;HE=2g|K(@VCa0wGGG?cYuBW9NDeCEBkGRJw3Csvc~iOIFx<^})} z$$Qu6z)rXfT6 zyu&%TR^W1fMEXyEQ>lV9nNvpt%PY4(8R}&nV4NLhOT)I5vAwt}b771PxyqNJJ#^?V zrf)rrNnCqqYdeONwSV$ESs9O2@U9)Jo3OFHhI^kLF)h9b0+Ms%VoZ_y@)SLY*J9Tc zIr4}vMPzfIKkBM{i_mk-#9a(HU)JZa7X&WP*N5nyrFQ2|r&UoWvwU60ax}$j?wyXu zlaN!Nt}7awQdNMtzi@7!T^aU{Jg$l3ez_vPGk?GkwmgWPtuVG%M@^Oo?~cY@K7Qr5 zfd4$jDwI*cFZBHz+=o!ZG1LJD03iHx!|s1D`hSH-g8$9B7BbfVXMCjppZF*O`5e4V z4G5KTRt8yJ{~z#C?SJBw0KJ`uhmH^4>v>!s{ zt)qLB*Zdm*8q#G;ZA>Vf^8FcC1n5{2!GnFMBwT{Mp%BopT%lrBks|9U7s}4E0|rUG z+5?0FQxMaqeEgM%7!Xpd1(h=r7yJG8%^THIltayN<%vsbM_Qh$f8it9Klq6C{{SD+ z{u4gZMfnRK@q)>%slm~#fX~?Wk*~ab)DX5k1=L1n0@!RF&1ZvePlgU5)u26;qq{}2 zSMvmN_5|8)d#!{1g^yn4{t+J${=4{S8$rISU z&af3V&a-4n47mJUCi#A@?NYgKOu8h|AC9iuYljkYvPZy=HXOL^j<4<&G7$76vWGA- zV8#_O9C!z@SCle1gaPC6N0a}AkC-co{s|um{WmL2#N61* zNY?Hz1L|)bGdaoHc7X$BM3*&pzieNaGudftxzBCBUxlDuF){;gP;!J_oJL_|by^_! zdF!S=QDU8m>OYZTxp@bp=qJx(WHJ7O*Xv!r9GATPrvM8?w1>W!%HKhoOUp% zR)-!3sDzo%QOs#)?uS&D?yZ<_n#@c65#%g#82w&s1}-A;x#~AQOahY#Gj9c9n@BxH zrgP}n{#Mt%(|cmnC6kir6yIk7%UXG4-7c;wsnHc^^alDRp4x)UM~OMqceDNq1EKip z;G!J_bB7+J;4G#>j9$E(lu?DHLa1~O7+n;_SGWdwv0(vOcgxVU3OS+e3 z32(2|adc95Mdh_Tedt7g2&;wkh1~oOX!^umUF&|rv|-I$UFV*i?)W2OCP!N*OP6~j za-kuTXy!!qmSpyVtkeW}+++o?8VLR)3~a795RRfZq!mm1r>HYmz_Yit6)VPP^Ne3* zy9gf4Bq21f8vi)96V?@4u`X7Ie*0?*R=~2Qd#09rTYha(bf9f(*SxJqiCtcd85?YJ z!V{juWgQ%%N5i+C-h>&!OTrZhyoQE6>~r@?ouvgiQ#6lh2|~hB|CQjQIaBMmTc#CS zp?>Ox9oL(0G|YL%9WVM>-FM=d|B|%tZ%i?(K&9_{+Y7;V1Yr#td5?JX&JUIsbp z7mWrqa(?CSIdR%POC-}~#Xs)IpY}FeqNxlE8Q?uLb922t#NN1zU_!Jbj%Ul%7zjt* zRf|(1-mi{E&O6sId9cL}@W5W*!A#_4wv;VGJ^Y1N_-A~};VjX~JQlc*L{ zgncIIf~`!V76s4m8V35wm=h+<6Po;*sMjN; zXH}b)2TeFB27yX653Z13pmy#%K{b)J_g^|5$sm<-NF`Tz1?1 z`-sTXh5o5ChPfPO!6t3FJ8Jo^lEfLqy-}>%&ic1vasNj_L!u>S2 zl0V^bn;~>hk8I(-Pl@`_!{(?QNz2MGHR}E-6%47lkee|&eEfZXZo#*EQfrFkBaOz! zKm?6ee;cljML;mRNS}^QA4!rkB!I-rzyMPQ`8`Swe2rYg0H>VeqYo^v>=cCFmv!WI<4OhiVoy< zOa&YXOS1IMH`X4PX1`6hmA~Oyit5s0Plj zQ9kxDRFXmcdCYd;$cUXUZioDL&oMm5n|G@kjOlNqPLc;u%qde>WN(JtzEV0Pws(;) z?({8Kh`=O&+-G74dLX-nKBowE63JmG+SP8mA5dv$t1*!I?v+6`80v4b)Q<#2NOg8s zLTuHDY-WT}7ggD5!ylCrT4tvOD}h|!;e2uCQ?V*BAwwXd79z0>M(};Y;i-qpY#=4t^%F0YSduxbUKfQ=<9E_ zJtqQF4)ng3t*rUTD2yPXMob6?kV4vv;#}}JW`Dxs+TVW5_L=gG#7-*i#R9Kv~s3++C= z)Q<=t$O08ml)5N+!iTM-2g>{w;@5rz{~=TO?VfH+i7ovv5PLrjA>urW_8!4ri+TOl z3pd{cl-lR<7jtBXK!;YU7elH)tlOKVV^xDyUhB_#z9tVCS2N7(=JW^>V+N@ zAIlfRSxz~Irw+QryGI+>WoRVeZSs~F^oH3Igi#_{fg4z)N7!19;Y0$4BOwd#eaA20 z(hhGs6>9}NeJIvZ5_(bl`bPGYqVTL(>RNKl!?QKo3GTc!tr$TvxA;g%@WcRv&@Ze@ zjuJJfwo0-pl-*QNb5Q$dxvhp)GL}-c;|BuYYAiyppA@+x1Cc5}g82E2SNkFx2z&UPXK!_TTuAxr0 z!o)Z#l)1()`F(idBt^?S1x$Y@Sk5|C3-iQVazM|kO8`8V4r#iVL%@PVUsr zp^P2Pu9tzeExBB}Zv8WzD-Qd1Au2@S__6V-QpoX~3Ms2{ALsm9W4@Jhe5NygS3)_9 zZ-TmI5?9Uk814>f7$q8<&_L&$auXDpW!R-(1mo1OuVZ-|Xhk{sNPDGL4ydt~+#ZH_XUB2RZz3}gTP1cBXeI8uNF#{t>7N^q$-)n*Z$hm5$&&_E2Tn^2 z2$ogT*e*1hFUsu+F}b`TI=OgRIL1i#)FN=0?HXw#S?UeA@e4ys;BrIA4wrC;| z#g$4N8~3yKm^_W@s=Yf-2xY$RbyiDhzbyq+W7_Rn*GNDehw(%bgzN1}-vwN<%B!h-G7*N(6_w`exkxCIs7n|88K80zb!Rd<)&lRw@62*b7X7Qj?%@bgN%ch zB(hLmA5wBgTD}}Ht0(CAL2xW%*rG?QU04!Y{RGoC;wo8XnI(5&z`E_Muyx5edCJ4a zf{nR4*R)D`D$aq%LM=II(R7MW12DccuIl=YskG~@k-{xfx+G{g;tpwz?X}WQnikM3 z&lymzlns*FIY2j0il&S=U#SvJIB_0{yX>)QZhX{g@vxf96IXJ%QO6XAUkkyaRUfEW!XASqq^Ue#Bc=z%aNu!8wN^I`(gn1Fy{71MowaW$Z8}iyy)WGi^ z$}`kHq*@U2>qu>e(I29gw|WM=vZY3j z)xz>Gl|Nqi;wK{V(9X?!QqYoGq@QBTi?^vR8KLv&QIDAzJruPJQcya-Y0^w?hYWRGiJscebVt_grb*9g_ID&146fhEDxeX$Z+U7BfN z8f8{(*23@JhJC8{FptZX=IHFk@Xdx3J=m&$)u~P_!3N~{Qf}|~L6giD+$15XikUwq z<#l=G@gEj}C9cVR+MnBogEaq$693gAkp90t2MX#t7@6DXTbVokheuGYG-tE$=f!rL z?){2&LC$2Rt;TwKK4?w4E&rs%6GrG>BT3zCg?e?ADxKc5|AXV|;% z>{bG`*%B=Z-Rm1?Cw?Q4->?N4kEM?yczE`iF#-JEx_P(3GJ!MK3jup#KIWlkOMsk_ z!tSC<-1Yu(tz-)*;$` zfk_6XB}k-NlF)SQsCqG1&q8XoVn5tu2w;fl7=4#JU3IVxK3<b_juTj>>wi1`9cfD?O&V$)G9wc{pLs^vE^-HjXGi-^~R2qWy;|x}H#h zLR~QOd(X~hxoz9Em@Ah!(yEPJ9qVjQnU4*jG9LmlD;RF}3z}a|c{BqaD!7+ zECs$@`Pp>FL{;Q2Dt0{x|GM z5PZmj{D*sFg8i?mQSAS=8vog>v)%t=*1_}`1YH9QEs|{7cWsv|P>5?*s+Rp+p}=3c zs8`SKa&cWja2of$ne-rxJ1-KE=uKZ^wcXCdm96=02O8PVj-hOY3v&daGG+^PY+N3c z0x@v*G&O~Bdb1%v_#MJ*s-adW+%P~*1OoO}MPd<&*~lRXY!ms@HmE2T0dru-tg3=p zs?ZX{p`RcKtWXxlzD%=h5YXu=uSpDJT%bk*#4BNyO%BUQo7sO|r>(jcSuPEd3oMGm zIba?X*X;BMuPn$Okok9mbIluD`sK^JnA@5;^i-=5Sl*Vf+k|2yB zKa`8J?7nor&my6ve|yirQ31!#%UeUE-u;vp8T?wtYKjahVL#R5d3sNzqWjck9`j0xFwtB=?`sb$UM9Jj9qCIK zX>esgZ-D3RnrU#o9Y(fZPl5tEZ}uoh67#xvNmGuMuu>zXrOH1GFv3T!vbgB|HR=`> zCoy6c(q<-!PUO_ZwJHvjfdh0Tzar)lC~Oojdys|OSeDd?xV39X0&uE@S`CRe+bT~= zn7s~(SBxUg^T=l|4aPsaT}f$HC76k4N#$bI;I^a_F^IILTaR3E*!SmE&>u69ig}#+ zlo$Fk>RyB}EX8qKRoV=zw#l0et!1}3o|A30gKeis6)8b7@pZab#>Jdol;cAJd*>93 z_NTA<^EafKZ)7?!;M$debFIE-cC+u32AcN_)bNLlKh9l|dE$3CoXF|C?hJc}W++%o zN~nY&Gz}#0#eP$ZscB$+oyR8~J_k!IKLm#!{O%}X4l$|+6Q3vu{LK@ko9Db?&j~Ab z^6YB#jHk;q9NCU-f;CrI74xf}t?p7lVw-6sV_WPKYM<%YQ}_m*A-?A474{?-*4-Cx z!7W-!0rl0~rP7%Vh{lvIw@UD$y-94y4ma(I@2>-X0U!7$&*-}cL0dc;2{AL!i-Hzp z<47FuCeRRV951gy&is+u>QUZ-8HDMloU_xb2%CC&OlgNzs=d0(4!Qxj-aN}_9?svJ z-{^mFQ>zDr`SCydNBKV)ga6viW&Z!!KmNhM*izoGIbcEXZtgbNy$-}}y{9ox)J6%r zXw}Rw*H)7W>EQk{*hRB}NW4i?Oj_Dwz8%Q9gp`I&P(?>%B3fvq=NgjfsOwI>v;ZI8< zDy}st1^+InSD?ZO20pdp>sYgJ<^4IxuUdx+F1OBa9RIT@xQQC!Rx+aakLSO%@hiVM z)#l)WiDD824+|!njejoyNy0;;2L@yhYyPXrzQ$|k=H}&H&TTlK#l5G@(E6JtWBT~+ z($rwfXsaBk8ZF)0ClJ1ah zknZl5E&)NhB}BR#q)S3N75*2`xrbN3bN$cpyVKzqo1tqy?7i1qbMBc!*3swb##8RZ z8A`5*Z*-bk=swb=R1ba3vm-Cywk6ql4)&2H`!#n6Vd(tf4N2rGp5KCKDen_!>iR62 zq_``p)b6)x9S-zuAu6N#GP2(7hPRKrg-_D(2-E?mh@_bs$Y{K!$)a#DvMj|LN9o?g zOGj%%LQ9~~4z zBydonQlv$JHpq`~LI{0r^6A=}X;$hRjqA6R<>_645?Q8L3nsDLBj_H(itwfB96M|Y zmnTMF^w=;X#@9V4V*A66CO1r@5xg7f(RK;R+Km1BOHWQpk4!!21D$y$CGhI?*pB!5 zsR%J%mCWH;ObuW#S61+c!PHSW3x9>jQ2y+1Czp9xh{yO1emaM$b%1*9jJ1Y${w7z31;blw$e0x|!wI45j)cFI$&MU{e$9(t zDygVXbKNMQ`DQa_e?opax6$L3=GP0QtB%IoJ+Y^r< zwT&jiCVG~?mkDhafL_P|B-!={N&YzkBKqG-vZ8^_%Rij7WlBpnb7DwdAH2JRcfo|x zVMwH}1<3}Ec8_k*Wx++;9 zUx8^k5jswRBC6} zF9j{uC$;nVE;r5mjQf>j^NbS;Y7`Mpb5{MHz8c*iP1Jm;o_(g;E!rHVm6+QNhZV`|BK&C zH+Q-It;Mshq|#~12rW2KIN}5ff*g0@jf@%!D#P6)_=~5E6PrA;4nbHqXIQtsEN@PP zVpQ!LVZx452uD2w3w^SCa3x7szG#=Kbpvg-MR)gF&z@k1=-{)gX* zr6~SUzWN}kEdXUZFM{NW(WXzZ&d;FKmzdBcHr5`RpC<%Cg|q?br4=h^j%=Q^sI7qv zdgpGE7*9)1aV=L3>M)%Wk;ifdiB3`B0x8EQ+1v=AG7jIe+$)tEhv2# zswjw5PQ3p@bL%tf+L2cqVt@&bxngAL)Kp~B=pc~{MjO)Z(5{LyKvciJiwQ(4gu2hL z6Oz!Pu7tYJu-h3jGj3rW6IYIG|J<=3z#rI~?E`VbD_Mg*U6N)gdbM-)^AaT|V}TTD^jfRuHj0)U>)+ zw&_E)q0t09xpLcyv1Rme0Rv*GRM_%F@z;0}M8a~pv*P_mH9~YI$r1;7s6+VQ^1{F( z{9YY@P4J-@&#ujfZ%Fw_w0>7QF3+s)A=e&w&U2 za_=nl+65rvEfGgY=nA`jpdjh0A5@rwRtH11qJoU5|Ap+>$xbPp_qBOyVcaULj*BXF z!^*P`$>COO1uB-;=1!slAzLTh+w{YD(?(%^_U>q1=*(&g(lKcwa;>iwxj}byr`4b+ zZ%x-m75WSul@{4YvvX^U9)}C&zjiNvbCNrHcB8oWc)^dED?gose|&*RF&i_d%t;ld z78k08B$Su-Nt!a(cAb2>%C{AY%NO|yCblY&e7Hdfn)n54SdCh_x!Y=+s4Nj$LR2NM;Ckk_tb8M7u zBg89Lt805s4Opj3ExU)G+O|xDM@l1_3x!6;`9ok)G~!D3uGBo-IpY1!t4JBvk@t`6 zNi9RZ!Dc9a@%dV=T0Ip$ATGeVmho zYW<&x^GiJg3q2DfS~F{7oA1$4BL!`HKzXLemg)kX>!ZLqBq<)wjQT56?m<4NIOAiA z{-Cnr7$xx!D@zytgCu6(bT!A!Z7$9`$+*3x-tc5DZnZO7URGSHg1$n`44kZAps<|j`;ZKd|$bnG-uEX&-fnDUK$P=bA zmM=c-TIAP<@k2YMbe5@Px=`Id48m$PYS=tvbr)f$x2VMj*4(|Y0DIZnT<@c8EmVOx zx(Sw9nHpH%BWn1u6h$3J1Ao*iOh0ApX*$15vL{nmF_Q;^WYj*L$?_ITq7o*MPxGafq z=3@x3F-arJ3&tn0RHE)RU)LvQQ;!oK1bD#qN5t&FpAM?1;g^#7G!zzhsjzK6*1Wqq z4(QN%juSB#YAkxghOio0d}bRcs4T;2O9t0oI+@%?sPT6#JP zq@pBwEhY}7S(562LLMK;E0$12#K%XjRE0`Lo>~wROyJ(+pC8k*3e=UPDjXj39ogi| z%p;U)i zS?in5Pop>yhOaFNLFa2}mPH*w+%0AuD@1*$);=BD6uvLdFPQNyhh4b?z5ftg1mS!o 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 literal 0 HcmV?d00001 From 4c8d5c6e88381806643613afe2a4ec84c1992e27 Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Mon, 8 Feb 2016 11:53:13 +0100 Subject: [PATCH 17/18] Added qualifier to version. --- ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF index 4bd032c..b4b9457 100644 --- a/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF +++ b/ca.ecliptical.pde.ds.lib/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: ca.ecliptical.pde.ds.lib -Bundle-Version: 1.3.1 +Bundle-Version: 1.3.1.qualifier Bundle-ClassPath: annotations.jar Bundle-Vendor: %Bundle-Vendor Export-Package: org.osgi.service.component.annotations;version="1.3.0" From ba33aedc1e660d9a104a47aa9c10874cecccdbb5 Mon Sep 17 00:00:00 2001 From: Arie van Wijngaarden Date: Fri, 3 Mar 2017 14:38:11 +0100 Subject: [PATCH 18/18] scope attribute is on the service element, not component. --- ca.ecliptical.pde.ds/META-INF/MANIFEST.MF | 50 +- .../pde/internal/ds/AnnotationProcessor.java | 4568 ++++++++--------- 2 files changed, 2308 insertions(+), 2310 deletions(-) diff --git a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF index a341204..1051d99 100644 --- a/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF +++ b/ca.ecliptical.pde.ds/META-INF/MANIFEST.MF @@ -1,25 +1,25 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: %Bundle-Name -Bundle-SymbolicName: ca.ecliptical.pde.ds;singleton:=true -Bundle-Version: 1.3.1.qualifier -Bundle-Activator: ca.ecliptical.pde.internal.ds.Activator -Bundle-Vendor: %Bundle-Vendor -Require-Bundle: org.eclipse.ui;bundle-version="[3.105.0,4.0.0)", - org.eclipse.core.runtime;bundle-version="[3.9.0,4.0.0)", - org.eclipse.pde.ui;bundle-version="[3.8.0,3.9.0)", - org.eclipse.jdt.core;bundle-version="[3.9.0,4.0.0)", - org.eclipse.pde.ds.core;bundle-version="[1.0.300,1.1.0)", - org.eclipse.core.resources;bundle-version="[3.8.100,4.0.0)", - org.eclipse.jface.text;bundle-version="[3.8.100,4.0.0)", - org.eclipse.ltk.core.refactoring;bundle-version="[3.6.100,4.0.0)", - org.eclipse.core.expressions;bundle-version="[3.4.501,4.0.0)", - org.eclipse.core.filebuffers;bundle-version="[3.5.300,4.0.0)", - org.eclipse.ui.ide;bundle-version="[3.9.2,4.0.0)", - org.eclipse.jdt.ui;bundle-version="[3.9.2,4.0.0)" -Bundle-RequiredExecutionEnvironment: J2SE-1.5 -Bundle-ActivationPolicy: lazy -Export-Package: ca.ecliptical.pde.internal.ds;x-internal:=true -Import-Package: ca.ecliptical.pde.ds.classpath;version="[1.0.0,2.0.0)", - org.osgi.service.component;version="[1.2.0,2.0.0)", - org.osgi.service.component.annotations;version="[1.3.0,2.0.0)" +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %Bundle-Name +Bundle-SymbolicName: ca.ecliptical.pde.ds;singleton:=true +Bundle-Version: 1.3.2.qualifier +Bundle-Activator: ca.ecliptical.pde.internal.ds.Activator +Bundle-Vendor: %Bundle-Vendor +Require-Bundle: org.eclipse.ui;bundle-version="[3.105.0,4.0.0)", + org.eclipse.core.runtime;bundle-version="[3.9.0,4.0.0)", + org.eclipse.pde.ui;bundle-version="[3.8.0,3.9.0)", + org.eclipse.jdt.core;bundle-version="[3.9.0,4.0.0)", + org.eclipse.pde.ds.core;bundle-version="[1.0.300,1.1.0)", + org.eclipse.core.resources;bundle-version="[3.8.100,4.0.0)", + org.eclipse.jface.text;bundle-version="[3.8.100,4.0.0)", + org.eclipse.ltk.core.refactoring;bundle-version="[3.6.100,4.0.0)", + org.eclipse.core.expressions;bundle-version="[3.4.501,4.0.0)", + org.eclipse.core.filebuffers;bundle-version="[3.5.300,4.0.0)", + org.eclipse.ui.ide;bundle-version="[3.9.2,4.0.0)", + org.eclipse.jdt.ui;bundle-version="[3.9.2,4.0.0)" +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Bundle-ActivationPolicy: lazy +Export-Package: ca.ecliptical.pde.internal.ds;x-internal:=true +Import-Package: ca.ecliptical.pde.ds.classpath;version="[1.0.0,2.0.0)", + org.osgi.service.component;version="[1.2.0,2.0.0)", + org.osgi.service.component.annotations;version="[1.3.0,2.0.0)" diff --git a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java index c79f697..778baba 100644 --- a/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java +++ b/ca.ecliptical.pde.ds/src/ca/ecliptical/pde/internal/ds/AnnotationProcessor.java @@ -1,2286 +1,2284 @@ -/******************************************************************************* - * Copyright (c) 2012, 2015 Ecliptical Software Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Ecliptical Software Inc. - initial API and implementation - *******************************************************************************/ -package ca.ecliptical.pde.internal.ds; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.regex.Pattern; - -import org.eclipse.core.filebuffers.FileBuffers; -import org.eclipse.core.filebuffers.ITextFileBuffer; -import org.eclipse.core.filebuffers.ITextFileBufferManager; -import org.eclipse.core.filebuffers.LocationKind; -import org.eclipse.core.resources.ICommand; -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IProjectDescription; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.ITypeHierarchy; -import org.eclipse.jdt.core.compiler.BuildContext; -import org.eclipse.jdt.core.compiler.CategorizedProblem; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.ASTRequestor; -import org.eclipse.jdt.core.dom.ASTVisitor; -import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; -import org.eclipse.jdt.core.dom.ArrayInitializer; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.EnumDeclaration; -import org.eclipse.jdt.core.dom.Expression; -import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.IAnnotationBinding; -import org.eclipse.jdt.core.dom.IMemberValuePairBinding; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.IVariableBinding; -import org.eclipse.jdt.core.dom.MemberValuePair; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.Modifier; -import org.eclipse.jdt.core.dom.NormalAnnotation; -import org.eclipse.jdt.core.dom.ParameterizedType; -import org.eclipse.jdt.core.dom.SingleMemberAnnotation; -import org.eclipse.jdt.core.dom.Type; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.DocumentRewriteSession; -import org.eclipse.jface.text.DocumentRewriteSessionType; -import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.IDocumentExtension4; -import org.eclipse.jface.text.link.LinkedModeModel; -import org.eclipse.osgi.util.NLS; -import org.eclipse.pde.core.IModelChangedEvent; -import org.eclipse.pde.core.ModelChangedEvent; -import org.eclipse.pde.internal.core.project.PDEProject; -import org.eclipse.pde.internal.core.text.IDocumentAttributeNode; -import org.eclipse.pde.internal.core.text.IDocumentElementNode; -import org.eclipse.pde.internal.core.text.IDocumentObject; -import org.eclipse.pde.internal.core.text.IDocumentTextNode; -import org.eclipse.pde.internal.core.text.IModelTextChangeListener; -import org.eclipse.pde.internal.ds.core.IDSComponent; -import org.eclipse.pde.internal.ds.core.IDSConstants; -import org.eclipse.pde.internal.ds.core.IDSDocumentFactory; -import org.eclipse.pde.internal.ds.core.IDSImplementation; -import org.eclipse.pde.internal.ds.core.IDSModel; -import org.eclipse.pde.internal.ds.core.IDSObject; -import org.eclipse.pde.internal.ds.core.IDSProperties; -import org.eclipse.pde.internal.ds.core.IDSProperty; -import org.eclipse.pde.internal.ds.core.IDSProvide; -import org.eclipse.pde.internal.ds.core.IDSReference; -import org.eclipse.pde.internal.ds.core.IDSService; -import org.eclipse.pde.internal.ds.core.text.DSModel; -import org.eclipse.text.edits.MalformedTreeException; -import org.eclipse.text.edits.MultiTextEdit; -import org.eclipse.text.edits.ReplaceEdit; -import org.eclipse.text.edits.TextEdit; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; -import org.osgi.service.component.ComponentContext; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.ConfigurationPolicy; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.FieldOption; -import org.osgi.service.component.annotations.Modified; -import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; -import org.osgi.service.component.annotations.ReferencePolicyOption; -import org.osgi.service.component.annotations.ReferenceScope; -import org.osgi.service.component.annotations.ServiceScope; - -@SuppressWarnings("restriction") -public class AnnotationProcessor extends ASTRequestor { - - private static final String DS_BUILDER = "org.eclipse.pde.ds.core.builder"; //$NON-NLS-1$ - - static final Debug debug = Debug.getDebug("ds-annotation-builder/processor"); //$NON-NLS-1$ - - private final ProjectContext context; - - private final Map fileMap; - - private boolean hasBuilder; - - public AnnotationProcessor(ProjectContext context, Map fileMap) { - this.context = context; - this.fileMap = fileMap; - } - - @Override - public void acceptAST(ICompilationUnit source, CompilationUnit ast) { - // determine CU key - String cuKey; - IJavaElement parent = source.getParent(); - if (parent == null) - cuKey = source.getElementName(); - else - cuKey = String.format("%s/%s", parent.getElementName().replace('.', '/'), source.getElementName()); //$NON-NLS-1$ - - context.getUnprocessed().remove(cuKey); - - ProjectState state = context.getState(); - HashMap dsKeys = new HashMap(); - HashSet problems = new HashSet(); - - ast.accept(new AnnotationVisitor(this, state, dsKeys, problems)); - - // track abandoned files (may be garbage) - Collection oldDSKeys = state.updateMappings(cuKey, dsKeys); - if (oldDSKeys != null) { - oldDSKeys.removeAll(dsKeys.values()); - context.getAbandoned().addAll(oldDSKeys); - } - - if (!problems.isEmpty()) { - // Remap the problems by compilation unit. We may have gotten problems from various - // sources and the problems should be shown with the correct file. - Map> remapped = new HashMap>(); - for (DSAnnotationProblem p : problems) { - List thisUnit = remapped.get(p.getUnit()); - if (thisUnit == null) { - thisUnit = new ArrayList(); - remapped.put(p.getUnit(), thisUnit); - } - thisUnit.add(p); - } - // Process the problems per file/compilation unit. - for (Map.Entry> entry : remapped.entrySet()) { - ICompilationUnit iu = (ICompilationUnit) entry.getKey().getJavaElement(); - char[] filename = iu.getResource().getFullPath().toString().toCharArray(); - List thisProblems = entry.getValue(); - for (DSAnnotationProblem problem : thisProblems) { - problem.setOriginatingFileName(filename); - if (problem.getSourceStart() >= 0) - problem.setSourceLineNumber(entry.getKey().getLineNumber(problem.getSourceStart())); - } - BuildContext buildContext = fileMap.get(iu); - // And show the errors/warnings. - if (buildContext != null) - buildContext.recordNewProblems(thisProblems.toArray(new CategorizedProblem[thisProblems.size()])); - } - } - } - - private void ensureDSProject(IProject project) throws CoreException { - IProjectDescription description = project.getDescription(); - ICommand[] commands = description.getBuildSpec(); - - for (ICommand command : commands) { - if (DS_BUILDER.equals(command.getBuilderName())) - return; - } - - ICommand[] newCommands = new ICommand[commands.length + 1]; - System.arraycopy(commands, 0, newCommands, 0, commands.length); - ICommand command = description.newCommand(); - command.setBuilderName(DS_BUILDER); - newCommands[newCommands.length - 1] = command; - description.setBuildSpec(newCommands); - project.setDescription(description, null); - } - - private void ensureExists(IFolder folder) throws CoreException { - if (folder.exists()) - return; - - IContainer parent = folder.getParent(); - if (parent != null && parent.getType() == IResource.FOLDER) - ensureExists((IFolder) parent); - - folder.create(true, true, null); - } - - void verifyOutputLocation(IFile file) throws CoreException { - if (hasBuilder) - return; - - hasBuilder = true; - IProject project = file.getProject(); - - IPath parentPath = file.getParent().getProjectRelativePath(); - if (!parentPath.isEmpty()) { - IFolder folder = project.getFolder(parentPath); - ensureExists(folder); - } - - try { - ensureDSProject(project); - } catch (CoreException e) { - Activator.getDefault().getLog().log(e.getStatus()); - } - } -} - -@SuppressWarnings("restriction") -class AnnotationVisitor extends ASTVisitor { - - private static final String COMPONENT_ANNOTATION = DSAnnotationCompilationParticipant.COMPONENT_ANNOTATION; - - private static final String ACTIVATE_ANNOTATION = Activate.class.getName(); - - private static final String MODIFIED_ANNOTATION = Modified.class.getName(); - - private static final String DEACTIVATE_ANNOTATION = Deactivate.class.getName(); - - private static final String REFERENCE_ANNOTATION = Reference.class.getName(); - - private static final Pattern PID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*"); //$NON-NLS-1$ - - private static final String NAMESPACE_1_1 = IDSConstants.NAMESPACE; - - private static final String NAMESPACE_1_2 = "http://www.osgi.org/xmlns/scr/v1.2.0"; //$NON-NLS-1$ - - private static final String NAMESPACE_1_3 = "http://www.osgi.org/xmlns/scr/v1.3.0"; //$NON_NLS-1$ - - private static final String ATTRIBUTE_COMPONENT_CONFIGURATION_PID = "configuration-pid"; //$NON-NLS-1$ - - private static final String ATTRIBUTE_REFERENCE_POLICY_OPTION = "policy-option"; //$NON-NLS-1$ - - private static final String ATTRIBUTE_REFERENCE_UPDATED = "updated"; //$NON-NLS-1$ - - private static final String VALUE_REFERENCE_POLICY_OPTION_RELUCTANT = "reluctant"; //$NON-NLS-1$ - - private static final Set PROPERTY_TYPES = Collections.unmodifiableSet( - new HashSet( - Arrays.asList( - null, - IDSConstants.VALUE_PROPERTY_TYPE_STRING, - IDSConstants.VALUE_PROPERTY_TYPE_LONG, - IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE, - IDSConstants.VALUE_PROPERTY_TYPE_FLOAT, - IDSConstants.VALUE_PROPERTY_TYPE_INTEGER, - IDSConstants.VALUE_PROPERTY_TYPE_BYTE, - IDSConstants.VALUE_PROPERTY_TYPE_CHAR, - IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN, - IDSConstants.VALUE_PROPERTY_TYPE_SHORT))); - - private static final Comparator REF_NAME_COMPARATOR = new Comparator() { - - public int compare(IDSReference o1, IDSReference o2) { - return o1.getReferenceName().compareTo(o2.getReferenceName()); - } - }; - - private static final Debug debug = AnnotationProcessor.debug; - - private final AnnotationProcessor processor; - - private final ProjectState state; - - private final ValidationErrorLevel errorLevel; - - private final ValidationErrorLevel missingUnbindMethodLevel; - - private final Map dsKeys; - - private final Set problems; - - public AnnotationVisitor(AnnotationProcessor processor, ProjectState state, Map dsKeys, Set problems) { - this.processor = processor; - this.state = state; - this.errorLevel = state.getErrorLevel(); - this.missingUnbindMethodLevel = state.getMissingUnbindMethodLevel(); - this.dsKeys = dsKeys; - this.problems = problems; - } - - @Override - public boolean visit(TypeDeclaration type) { - if (!Modifier.isPublic(type.getModifiers())) { - // non-public types cannot be (or have nested) components - if (errorLevel.isNone()) - return false; - - Annotation annotation = findComponentAnnotation(type); - if (annotation != null) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notPublic, type.getName().getIdentifier()), type.getName().getIdentifier()); - - return true; - } - - Annotation annotation = findComponentAnnotation(type); - if (annotation != null) { - boolean isInterface = false; - boolean isAbstract = false; - boolean isNested = false; - boolean noDefaultConstructor = false; - if ((isInterface = type.isInterface()) - || (isAbstract = Modifier.isAbstract(type.getModifiers())) - || (isNested = (!type.isPackageMemberTypeDeclaration() && !isNestedPublicStatic(type))) - || (noDefaultConstructor = !hasDefaultConstructor(type))) { - // interfaces, abstract types, non-static/non-public nested types, or types with no default constructor cannot be components - if (errorLevel != ValidationErrorLevel.none) { - if (isInterface) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_interface, type.getName().getIdentifier()), type.getName().getIdentifier()); - else if (isAbstract) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_abstract, type.getName().getIdentifier()), type.getName().getIdentifier()); - else if (isNested) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notTopLevel, type.getName().getIdentifier()), type.getName().getIdentifier()); - else if (noDefaultConstructor) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_noDefaultConstructor, type.getName().getIdentifier()), type.getName().getIdentifier()); - else - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentImplementationClass, type.getName().getIdentifier()), type.getName().getIdentifier()); - } - } else { - ITypeBinding typeBinding = type.resolveBinding(); - if (typeBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for type: %s", type)); //$NON-NLS-1$ - } else { - IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding(); - if (annotationBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$ - } else { - try { - processComponent(type, typeBinding, annotation, annotationBinding, problems); - } catch (CoreException e) { - Activator.getDefault().getLog().log(e.getStatus()); - } - } - } - } - } - - return true; - } - - @Override - public boolean visit(EnumDeclaration node) { - Annotation annotation = findComponentAnnotation(node); - if (annotation != null) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_enumeration, node.getName().getIdentifier()), node.getName().getIdentifier()); - - return false; - } - - @Override - public boolean visit(AnnotationTypeDeclaration node) { - Annotation annotation = findComponentAnnotation(node); - if (annotation != null) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_annotation, node.getName().getIdentifier()), node.getName().getIdentifier()); - - return true; - } - - private Annotation findComponentAnnotation(AbstractTypeDeclaration type) { - for (Object item : type.modifiers()) { - if (!(item instanceof Annotation)) - continue; - - Annotation annotation = (Annotation) item; - IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding(); - if (annotationBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$ - - continue; - } - - if (COMPONENT_ANNOTATION.equals(annotationBinding.getAnnotationType().getQualifiedName())) - return annotation; - } - - return null; - } - - private boolean isNestedPublicStatic(AbstractTypeDeclaration type) { - if (Modifier.isStatic(type.getModifiers())) { - ASTNode parent = type.getParent(); - if (parent != null && (parent.getNodeType() == ASTNode.TYPE_DECLARATION || parent.getNodeType() == ASTNode.ANNOTATION_TYPE_DECLARATION)) { - AbstractTypeDeclaration parentType = (AbstractTypeDeclaration) parent; - if (Modifier.isPublic(parentType.getModifiers())) - return parentType.isPackageMemberTypeDeclaration() || isNestedPublicStatic(parentType); - } - } - - return false; - } - - private boolean hasDefaultConstructor(TypeDeclaration type) { - boolean hasConstructor = false; - for (MethodDeclaration method : type.getMethods()) { - if (method.isConstructor()) { - hasConstructor = true; - if (Modifier.isPublic(method.getModifiers()) && method.parameters().isEmpty()) - return true; - } - } - - return !hasConstructor; - } - - private void processComponent(TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Collection problems) throws CoreException { - // determine component name - HashMap params = new HashMap(); - for (IMemberValuePairBinding pair : annotationBinding.getDeclaredMemberValuePairs()) { - params.put(pair.getName(), pair.getValue()); - } - - String implClass = typeBinding.getBinaryName(); - - String name = implClass; - Object value; - if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$ - name = (String) value; - validateComponentName(annotation, name, problems); - } - - // set up document to edit - IPath path = new Path(state.getPath()).append(name).addFileExtension("xml"); //$NON-NLS-1$ - - String dsKey = path.toPortableString(); - dsKeys.put(implClass, dsKey); - - IProject project = typeBinding.getJavaElement().getJavaProject().getProject(); - IFile file = PDEProject.getBundleRelativeFile(project, path); - IPath filePath = file.getFullPath(); - - processor.verifyOutputLocation(file); - - // handle file move/rename - String oldPath = state.getModelFile(implClass); - if (oldPath != null && !oldPath.equals(dsKey) && !file.exists()) { - IFile oldFile = PDEProject.getBundleRelativeFile(project, Path.fromPortableString(oldPath)); - if (oldFile.exists()) { - try { - oldFile.move(file.getFullPath(), true, true, null); - } catch (CoreException e) { - Activator.getDefault().getLog().log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, String.format("Unable to move model file from '%s' to '%s'.", oldPath, file.getFullPath()), e)); //$NON-NLS-1$ - } - } - } - - ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); - bufferManager.connect(filePath, LocationKind.IFILE, null); - ITextFileBuffer buffer = bufferManager.getTextFileBuffer(filePath, LocationKind.IFILE); - if (buffer.isDirty()) - buffer.commit(null, true); - - IDocument document = buffer.getDocument(); - final DSModel dsModel = new DSModel(document, true); - dsModel.setUnderlyingResource(file); - dsModel.setCharset("UTF-8"); //$NON-NLS-1$ - dsModel.load(); - - // note: we can't use XMLTextChangeListener because it generates overlapping edits! - // thus we replace the entire content with one edit (if changed) - final IDocument fDoc = document; - dsModel.addModelChangedListener(new IModelTextChangeListener() { - - private final IDocument document = fDoc; - - private boolean changed; - - public void modelChanged(IModelChangedEvent event) { - changed = true; - } - - public TextEdit[] getTextOperations() { - if (!changed) - return new TextEdit[0]; - - String text = dsModel.getContents(); - ReplaceEdit edit = new ReplaceEdit(0, document.getLength(), text); - return new TextEdit[] { edit }; - } - - public String getReadableName(TextEdit edit) { - return null; - } - }); - - try { - processComponent(dsModel, type, typeBinding, annotation, annotationBinding, params, name, implClass, problems); - - TextEdit[] edits = dsModel.getLastTextChangeListener().getTextOperations(); - if (edits.length > 0) { - if (debug.isDebugging()) - debug.trace(String.format("Saving model: %s", file.getFullPath())); //$NON-NLS-1$ - - final MultiTextEdit edit = new MultiTextEdit(); - edit.addChildren(edits); - - if (buffer.isSynchronizationContextRequested()) { - final IDocument doc = document; - final CoreException[] ex = new CoreException[1]; - final CountDownLatch latch = new CountDownLatch(1); - bufferManager.execute(new Runnable() { - public void run() { - try { - performEdit(doc, edit); - } catch (CoreException e) { - ex[0] = e; - } - - latch.countDown(); - } - }); - - try { - latch.await(); - } catch (InterruptedException e) { - if (debug.isDebugging()) - debug.trace("Interrupted while waiting for edits to complete on display thread.", e); //$NON-NLS-1$ - } - - if (ex[0] != null) - throw ex[0]; - } else { - performEdit(document, edit); - } - - buffer.commit(null, true); - } - } finally { - dsModel.dispose(); - bufferManager.disconnect(buffer.getLocation(), LocationKind.IFILE, null); - } - } - - private void performEdit(IDocument document, TextEdit edit) throws CoreException { - DocumentRewriteSession session = null; - try { - if (document instanceof IDocumentExtension4) { - session = ((IDocumentExtension4) document).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED); - } - - LinkedModeModel.closeAllModels(document); - edit.apply(document); - } catch (MalformedTreeException e) { - throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error applying changes to component model.", e)); //$NON-NLS-1$ - } catch (BadLocationException e) { - throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error applying changes to component model.", e)); //$NON-NLS-1$ - } finally { - if (session != null) { - ((IDocumentExtension4) document).stopRewriteSession(session); - } - } - } - - private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map params, String name, String implClass, Collection problems) { - // The required version of the DS specification. Defaults to 1, meaning: v1.1.0 - // The value will be updated in case necessary. - int requiredVersion = 1; - - Object value; - Collection services; - if ((value = params.get("service")) instanceof Object[]) { //$NON-NLS-1$ - Object[] elements = (Object[]) value; - services = new LinkedHashSet(elements.length); - Map serviceDuplicates = errorLevel.isNone() ? null : new HashMap(); - for (int i = 0; i < elements.length; ++i) { - ITypeBinding serviceType = (ITypeBinding) elements[i]; - String serviceName = serviceType.getBinaryName(); - if (!errorLevel.isNone()) { - if (serviceDuplicates.containsKey(serviceName)) { - reportProblem(annotation, "service", i, problems, Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$ - Integer pos = serviceDuplicates.put(serviceName, null); - if (pos != null) - reportProblem(annotation, "service", pos.intValue(), problems, Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$ - } else { - serviceDuplicates.put(serviceName, i); - } - } - - services.add(serviceName); - validateComponentService(annotation, typeBinding, serviceType, i, problems); - } - } else { - ITypeBinding[] serviceTypes = typeBinding.getInterfaces(); - services = new ArrayList(serviceTypes.length); - for (int i = 0; i < serviceTypes.length; ++i) { - services.add(serviceTypes[i].getBinaryName()); - } - } - - String factory = null; - if ((value = params.get("factory")) instanceof String) { //$NON-NLS-1$ - factory = (String) value; - validateComponentFactory(annotation, factory, problems); - } - - Boolean serviceFactory = null; - if ((value = params.get("servicefactory")) instanceof Boolean) { //$NON-NLS-1$ - serviceFactory = (Boolean) value; - } - - Boolean enabled = null; - if ((value = params.get("enabled")) instanceof Boolean) { //$NON-NLS-1$ - enabled = (Boolean) value; - } - - Boolean immediate = null; - if ((value = params.get("immediate")) instanceof Boolean) { //$NON-NLS-1$ - immediate = (Boolean) value; - } - - String[] properties; - if ((value = params.get("property")) instanceof Object[]) { //$NON-NLS-1$ - Object[] elements = (Object[]) value; - ArrayList list = new ArrayList(elements.length); - for (int i = 0; i < elements.length; ++i) { - if (elements[i] instanceof String) - list.add((String) elements[i]); - } - - properties = list.toArray(new String[list.size()]); - } else { - properties = new String[0]; - } - - String[] propertyFiles; - if ((value = params.get("properties")) instanceof Object[]) { //$NON-NLS-1$ - Object[] elements = (Object[]) value; - ArrayList list = new ArrayList(elements.length); - for (int i = 0; i < elements.length; ++i) { - if (elements[i] instanceof String) - list.add((String) elements[i]); - } - - propertyFiles = list.toArray(new String[list.size()]); - validateComponentPropertyFiles(annotation, ((IType) typeBinding.getJavaElement()).getJavaProject().getProject(), propertyFiles, problems); - } else { - propertyFiles = new String[0]; - } - - String configPolicy = null; - if ((value = params.get("configurationPolicy")) instanceof IVariableBinding) { //$NON-NLS-1$ - IVariableBinding configPolicyBinding = (IVariableBinding) value; - ConfigurationPolicy configPolicyLiteral = ConfigurationPolicy.valueOf(configPolicyBinding.getName()); - if (configPolicyLiteral != null) - configPolicy = configPolicyLiteral.toString(); - } - - String configPid = null; - if ((value = params.get("configurationPid")) instanceof String || value instanceof Object[]) { //$NON-NLS-1$ - Object[] pids; - if (value instanceof String) { - pids = new Object[]{value}; - } - else { - pids = (Object[]) value; - } - StringBuffer configurations = new StringBuffer(); - for (Object pid : pids) { - validateComponentConfigPID(annotation, pid.toString(), problems); - configurations.append(pid.toString()).append(" "); - } - configPid = configurations.toString().trim(); - if (pids.length > 1) { - requiredVersion = Math.max(requiredVersion, 3); - } - } - - ServiceScope scope = null; - if ((value = params.get("scope")) instanceof IVariableBinding) { - IVariableBinding scopeBinding = (IVariableBinding) value; - scope = ServiceScope.valueOf(scopeBinding.getName()); - } - - IDSComponent component = model.getDSComponent(); - - if (enabled == null) { - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ENABLED, IDSConstants.VALUE_TRUE); - } else { - component.setEnabled(enabled.booleanValue()); - } - - if (name == null) { - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_NAME, null); - } else { - component.setAttributeName(name); - } - - if (factory == null) { - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_FACTORY, null); - } else { - component.setFactory(factory); - } - - if (immediate == null) { - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_IMMEDIATE, null); - } else { - component.setImmediate(immediate.booleanValue()); - } - - if (configPolicy == null) { - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_CONFIGURATION_POLICY, IDSConstants.VALUE_CONFIGURATION_POLICY_OPTIONAL); - } else { - component.setConfigurationPolicy(configPolicy); - } - - if (scope == null) { - removeAttribute(component, "scope", null); - } - else { - component.setXMLAttribute("scope", scope.toString()); - } - - IDSDocumentFactory dsFactory = model.getFactory(); - - String activate = null; - Annotation activateAnnotation = null; - String deactivate = null; - Annotation deactivateAnnotation = null; - String modified = null; - Annotation modifiedAnnotation = null; - - ArrayList references = new ArrayList(); - HashMap referenceNames = new HashMap(); - IDSReference[] refElements = component.getReferences(); - - HashMap refMap = new HashMap(refElements.length); - for (IDSReference refElement : refElements) { - refMap.put(refElement.getName(), refElement); - } - - // Process the field declarations to get the field injection points. - if (processFieldReferences(type, false, refMap, dsFactory, references, referenceNames, problems).size() > 0) { - requiredVersion = Math.max(requiredVersion, 3); - } - - Map defaultValues = new HashMap(); - for (MethodDeclaration method : type.getMethods()) { - for (Object modifier : method.modifiers()) { - if (!(modifier instanceof Annotation)) - continue; - - Annotation methodAnnotation = (Annotation) modifier; - IAnnotationBinding methodAnnotationBinding = methodAnnotation.resolveAnnotationBinding(); - if (methodAnnotationBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for annotation: %s", methodAnnotation)); //$NON-NLS-1$ - - continue; - } - - String annotationName = methodAnnotationBinding.getAnnotationType().getQualifiedName(); - - if (ACTIVATE_ANNOTATION.equals(annotationName)) { - if (activate == null) { - activate = method.getName().getIdentifier(); - activateAnnotation = methodAnnotation; - requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "activate", method, - dsFactory, defaultValues, problems)); //$NON-NLS-1$ - } else if (!errorLevel.isNone()) { - reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier()); - if (activateAnnotation != null) { - reportProblem(activateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, activate); - activateAnnotation = null; - } - } - - continue; - } - - if (DEACTIVATE_ANNOTATION.equals(annotationName)) { - if (deactivate == null) { - deactivate = method.getName().getIdentifier(); - deactivateAnnotation = methodAnnotation; - requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "deactivate", method, - dsFactory, defaultValues, problems)); //$NON-NLS-1$ - } else if (!errorLevel.isNone()) { - reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, method.getName().getIdentifier()); - if (deactivateAnnotation != null) { - reportProblem(deactivateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, deactivate); - deactivateAnnotation = null; - } - } - - continue; - } - - if (MODIFIED_ANNOTATION.equals(annotationName)) { - if (modified == null) { - modified = method.getName().getIdentifier(); - modifiedAnnotation = methodAnnotation; - requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "modified", method, - dsFactory, defaultValues, problems)); //$NON-NLS-1$ - } else if (!errorLevel.isNone()) { - reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, method.getName().getIdentifier()); - if (modifiedAnnotation != null) { - reportProblem(modifiedAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, modified); - modifiedAnnotation = null; - } - } - - continue; - } - - if (REFERENCE_ANNOTATION.equals(annotationName)) { - IMethodBinding methodBinding = method.resolveBinding(); - if (methodBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ - } else { - requiredVersion = Math.max(requiredVersion, processReference(method, methodBinding, methodAnnotation, methodAnnotationBinding, refMap, dsFactory, references, referenceNames, problems)); - } - - continue; - } - } - } - - if (activate == null) { - // only remove activate="activate" if method not found - if (!"activate".equals(component.getActivateMethod()) //$NON-NLS-1$ - || !hasLifeCycleMethod(typeBinding, "activate")) //$NON-NLS-1$ - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null); //$NON-NLS-1$ - } else { - component.setActivateMethod(activate); - } - - if (deactivate == null) { - // only remove deactivate="deactivate" if method not found - if (!"deactivate".equals(component.getDeactivateMethod()) //$NON-NLS-1$ - || !hasLifeCycleMethod(typeBinding, "deactivate")) //$NON-NLS-1$ - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_DEACTIVATE, null); //$NON-NLS-1$ - } else { - component.setDeactivateMethod(deactivate); - } - - if (modified == null) { - removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_MODIFIED, null); - } else { - component.setModifiedeMethod(modified); - } - - IDSProperty[] propElements = component.getPropertyElements(); - // If we don't have any properties, just remove them from the XML. - if (properties.length == 0 && defaultValues.size() == 0) { - removeChildren(component, Arrays.asList(propElements)); - } else { - // build up new property elements. This are the property - // elements present in the @Component annotation. - LinkedHashMap map = new LinkedHashMap(properties.length); - for (int i = 0; i < properties.length; ++i) { - String propertyStr = properties[i]; - String[] pair = propertyStr.split("=", 2); //$NON-NLS-1$ - int colon = pair[0].indexOf(':'); - String propertyName, propertyType; - if (colon == -1) { - propertyName = pair[0]; - propertyType = null; - } else { - propertyName = pair[0].substring(0, colon); - propertyType = pair[0].substring(colon + 1); - } - - String propertyValue = pair.length > 1 ? pair[1].trim() : null; - - IDSProperty property = map.get(propertyName); - if (property == null) { - // create a new property - property = dsFactory.createProperty(); - map.put(propertyName, property); - property.setPropertyName(propertyName); - if (propertyType == null) - removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_TYPE, null); // just remove the attribute completely so we can detect changes when reconciling - else - property.setPropertyType(propertyType); - - property.setPropertyValue(propertyValue); - validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i, problems); - } else { - // property is multi-valued - String content = property.getPropertyElemBody(); - if (content == null) { - content = property.getPropertyValue(); - property.setPropertyElemBody(content); - property.setPropertyValue(null); - } - - if (!errorLevel.isNone()) { - String expected = property.getPropertyType() == null || property.getPropertyType().length() == 0 || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType()) ? Messages.AnnotationProcessor_stringOrEmpty : property.getPropertyType(); - String actual = propertyType == null || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(propertyType) ? Messages.AnnotationProcessor_stringOrEmpty : propertyType; - if (!actual.equals(expected)) - reportProblem(annotation, "property", i, problems, NLS.bind(Messages.AnnotationProcessor_inconsistentComponentPropertyType, actual, expected), actual); //$NON-NLS-1$ - else - validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i, problems); - } - - if (propertyValue != null) - property.setPropertyElemBody(content + "\n" + pair[1]); //$NON-NLS-1$ - } - } - - // reconcile against existing property elements - HashMap propMap = new HashMap(); - for (IDSProperty propElement : propElements) { - propMap.put(propElement.getPropertyName(), propElement); - } - - // We now merge the default values from the configuration type properties with - // the values found in the component annotations. The latter overwrite the first. - Map actualProperties = new HashMap(); - // Load the defaults. - actualProperties.putAll(defaultValues); - // Overwrite/add the properties from the annotation. - actualProperties.putAll(map); - ArrayList propList = new ArrayList(actualProperties.values()); - for (ListIterator i = propList.listIterator(); i.hasNext();) { - IDSProperty newProperty = i.next(); - IDSProperty property = propMap.remove(newProperty.getPropertyName()); - if (property == null) - continue; - - i.set(property); - - String newPropertyType = newProperty.getPropertyType(); - if (newPropertyType != null || !IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType())) - property.setPropertyType(newPropertyType); - - String newContent = newProperty.getPropertyElemBody(); - // Somehow, even if we set null to the body, it still can contain an empty string. Therefore, check - // on an empty string as well. - if (newContent == null || newContent.length() == 0) { - property.setPropertyValue(newProperty.getPropertyValue()); - IDocumentTextNode textNode = property.getTextNode(); - if (textNode != null) { - property.removeTextNode(); - if (property.isInTheModel() && property.isEditable()) { - model.fireModelChanged(new ModelChangedEvent(model, IModelChangedEvent.REMOVE, new Object[] { textNode }, null)); - } - } - } else { - removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null); - String content = property.getPropertyElemBody(); - if (content == null || !newContent.equals(normalizePropertyElemBody(content))) { - property.setPropertyElemBody(newContent); - } - } - } - - int firstPos = propElements.length == 0 - ? 0 // insert first property element as first child of component - : component.indexOf(propElements[0]); - removeChildren(component, propMap.values()); - - addOrMoveChildren(component, propList, firstPos); - } - - IDSProperties[] propFileElements = component.getPropertiesElements(); - if (propertyFiles.length == 0) { - removeChildren(component, Arrays.asList(propFileElements)); - } else { - HashMap propFileMap = new HashMap(propFileElements.length); - for (IDSProperties propFileElement : propFileElements) { - propFileMap.put(propFileElement.getEntry(), propFileElement); - } - - ArrayList propFileList = new ArrayList(propertyFiles.length); - for (String propertyFile : propertyFiles) { - IDSProperties propertiesElement = propFileMap.remove(propertyFile); - if (propertiesElement == null) { - propertiesElement = dsFactory.createProperties(); - propertiesElement.setInTheModel(false); // note: workaround for PDE bug - propertiesElement.setEntry(propertyFile); - } - - propFileList.add(propertiesElement); - } - - int firstPos; - if (propFileElements.length == 0) { - // insert first properties element after last property or (if none) first child of component - propElements = component.getPropertyElements(); - firstPos = propElements.length == 0 ? 0 : component.indexOf(propElements[propElements.length - 1]) + 1; - } else { - firstPos = component.indexOf(propFileElements[0]); - } - - removeChildren(component, propFileMap.values()); - - addOrMoveChildren(component, propFileList, firstPos); - } - - IDSService service = component.getService(); - if (services.isEmpty()) { - if (service != null) - component.removeService(service); - } else { - if (service == null) { - service = dsFactory.createService(); - - // insert service element after last property or properties element - int firstPos = Math.max(0, indexOfLastPropertyOrProperties(component)); - component.addChildNode(service, firstPos, true); - } - - IDSProvide[] provides = service.getProvidedServices(); - HashMap provideMap = new HashMap(provides.length); - for (IDSProvide provide : provides) { - provideMap.put(provide.getInterface(), provide); - } - - ArrayList provideList = new ArrayList(services.size()); - for (String serviceName : services) { - IDSProvide provide = provideMap.remove(serviceName); - if (provide == null) { - provide = dsFactory.createProvide(); - provide.setInterface(serviceName); - } - - provideList.add(provide); - } - - int firstPos = provides.length == 0 ? -1 : service.indexOf(provides[0]); - removeChildren(service, (provideMap.values())); - - addOrMoveChildren(service, provideList, firstPos); - - if (serviceFactory == null) { - removeAttribute(service, IDSConstants.ATTRIBUTE_SERVICE_FACTORY, IDSConstants.VALUE_FALSE); - } else { - service.setServiceFactory(serviceFactory.booleanValue()); - } - } - - - if (configPid == null) { - removeAttribute(component, ATTRIBUTE_COMPONENT_CONFIGURATION_PID, null); - } else { - component.setXMLAttribute(ATTRIBUTE_COMPONENT_CONFIGURATION_PID, configPid); - requiredVersion = Math.max(2, requiredVersion); - } - - if (references.isEmpty()) { - removeChildren(component, Arrays.asList(refElements)); - } else { - // references must be declared in ascending lexicographical order of their names - Collections.sort(references, REF_NAME_COMPARATOR); - - int firstPos; - if (refElements.length == 0) { - // insert first reference element after service element, or (if not present) last property or properties - service = component.getService(); - if (service == null) { - firstPos = Math.max(0, indexOfLastPropertyOrProperties(component)); - } else { - firstPos = component.indexOf(service) + 1; - } - } else { - firstPos = component.indexOf(refElements[0]); - } - - removeChildren(component, refMap.values()); - - addOrMoveChildren(component, references, firstPos); - } - - IDSImplementation impl = component.getImplementation(); - if (impl == null) { - impl = dsFactory.createImplementation(); - component.setImplementation(impl); - } - - impl.setClassName(implClass); - - - String neededXmlns = NAMESPACE_1_1; - if (requiredVersion > 2) { - neededXmlns = NAMESPACE_1_3; - } - else if (requiredVersion > 1) { - neededXmlns = NAMESPACE_1_2; - } - String xmlns = neededXmlns; - if ((value = params.get("xmlns")) instanceof String) { //$NON-NLS-1$ - xmlns = (String) value; - validateComponentXMLNS(annotation, xmlns, neededXmlns, problems); - } else { - xmlns = neededXmlns; - } - - component.setNamespace(xmlns); // Note: updates to the namespace do not work if no other changes! - } - - private void removeChildren(IDSObject parent, Collection children) { - for (IDocumentElementNode child : children) { - parent.removeChildNode(child, true); - } - } - - private void removeAttribute(IDSObject obj, String name, String defaultValue) { - IDocumentAttributeNode attrNode = obj.getDocumentAttribute(name); - if (attrNode != null) { - // only remove if value is not default - String value = attrNode.getAttributeValue(); - if (value != null && value.equals(defaultValue)) - return; - - obj.removeDocumentAttribute(attrNode); - if (obj.isInTheModel() && obj.isEditable()) { - obj.getModel().fireModelChanged(new ModelChangedEvent(obj.getModel(), ModelChangedEvent.REMOVE, new Object[] { attrNode }, null)); - } - } - } - - private void addOrMoveChildren(IDSObject parent, List children, int firstPos) { - for (int i = 0, n = children.size(); i < n; ++i) { - IDSObject child = children.get(i); - if (child.isInTheModel()) { - int pos = parent.indexOf(child); - if (i == 0) { - if (firstPos < pos) { - // move to first place - moveChildNode(parent, child, firstPos - pos, true); - } - } else { - int prevPos = parent.indexOf(children.get(i - 1)); - if (prevPos > pos) { - // move to previous sibling's position - moveChildNode(parent, child, prevPos - pos, true); - } - } - } else { - if (i == 0) { - if (firstPos == -1) { - parent.addChildNode(child, true); - } else { - // insert into first place - parent.addChildNode(child, firstPos, true); - } - } else { - // insert after preceding sibling - parent.addChildNode(child, parent.indexOf(children.get(i - 1)) + 1, true); - } - } - } - } - - private void moveChildNode(IDocumentObject obj, IDocumentElementNode node, int newRelativeIndex, boolean fireEvent) { - if (newRelativeIndex == 1 || newRelativeIndex == -1) { - obj.moveChildNode(node, newRelativeIndex, fireEvent); - return; - } - - // workaround for PDE's busted DocumentObject.clone() method - int currentIndex = obj.indexOf(node); - if (currentIndex == -1) - return; - - int newIndex = newRelativeIndex + currentIndex; - if (newIndex < 0 || newIndex >= obj.getChildCount()) - return; - - obj.removeChildNode(node, fireEvent); - IDocumentElementNode clone = clone(obj, node); - obj.addChildNode(clone, newIndex, fireEvent); - } - - private IDocumentElementNode clone(IDocumentObject obj, IDocumentElementNode node) { - // note: same exact impl as DocumentObject.clone() - // but here the deserialized object will actually resolve successfully - // because our classloader (with DSPropery visible) will be on top of the stack - // yay for Java serialization, *sigh* - IDocumentElementNode clone = null; - try { - // Serialize - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bout); - out.writeObject(node); - out.flush(); - out.close(); - byte[] bytes = bout.toByteArray(); - // Deserialize - ByteArrayInputStream bin = new ByteArrayInputStream(bytes); - ObjectInputStream in = new ObjectInputStream(bin); - clone = (IDocumentElementNode) in.readObject(); - in.close(); - // Reconnect - clone.reconnect(obj, obj.getSharedModel()); - } catch (IOException e) { - if (debug.isDebugging()) - debug.trace("Error cloning element.", e); //$NON-NLS-1$ - } catch (ClassNotFoundException e) { - if (debug.isDebugging()) - debug.trace("Error cloning element.", e); //$NON-NLS-1$ - } - - return clone; - } - - private int indexOfLastPropertyOrProperties(IDSComponent component) { - int pos = -1; - IDSProperty[] propElements = component.getPropertyElements(); - IDSProperties[] propFileElements = component.getPropertiesElements(); - if (propElements.length > 0) - pos = component.indexOf(propElements[propElements.length - 1]) + 1; - - if (propFileElements.length > 0) { - int lastPos = component.indexOf(propFileElements[propFileElements.length - 1]) + 1; - if (lastPos > pos) - pos = lastPos; - } - - return pos; - } - - private String normalizePropertyElemBody(String content) { - StringBuilder buf = new StringBuilder(content.length()); - BufferedReader reader = new BufferedReader(new StringReader(content)); - try { - String line; - while ((line = reader.readLine()) != null) { - String trimmed = line.trim(); - if (trimmed.length() == 0) - continue; - - if (buf.length() > 0) - buf.append('\n'); - - buf.append(trimmed); - } - } catch (IOException e) { - if (debug.isDebugging()) - debug.trace("Error reading property element body.", e); //$NON-NLS-1$ - } finally { - try { - reader.close(); - } catch (IOException e) { - // ignore - } - } - - return buf.toString(); - } - - private void validateComponentName(Annotation annotation, String name, Collection problems) { - if (!errorLevel.isNone() && !PID_PATTERN.matcher(name).matches()) - reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentName, name), name); //$NON-NLS-1$ - } - - private void validateComponentService(Annotation annotation, ITypeBinding componentType, ITypeBinding serviceType, int index, Collection problems) { - if (!errorLevel.isNone() && !componentType.isAssignmentCompatible(serviceType)) - reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentService, serviceType.getName()), serviceType.getName()); //$NON-NLS-1$ - } - - private void validateComponentFactory(Annotation annotation, String factory, Collection problems) { - if (!errorLevel.isNone() && !PID_PATTERN.matcher(factory).matches()) - reportProblem(annotation, "factory", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentFactoryName, factory), factory); //$NON-NLS-1$ - } - - private void validateComponentProperty(Annotation annotation, String name, String type, String value, int index, Collection problems) { - if (errorLevel.isNone()) - return; - - if (PROPERTY_TYPES.contains(type)) { - if (name == null || name.trim().length() == 0) - reportProblem(annotation, "property", index, problems, Messages.AnnotationProcessor_invalidComponentProperty_nameRequired, name); //$NON-NLS-1$ - - if (value == null) { - reportProblem(annotation, "property", index, problems, Messages.AnnotationProcessor_invalidComponentProperty_valueRequired, name); //$NON-NLS-1$ - } else { - try { - if (IDSConstants.VALUE_PROPERTY_TYPE_LONG.equals(type)) - Long.valueOf(value); - else if (IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE.equals(type)) - Double.valueOf(value); - else if (IDSConstants.VALUE_PROPERTY_TYPE_FLOAT.equals(type)) - Float.valueOf(value); - else if (IDSConstants.VALUE_PROPERTY_TYPE_INTEGER.equals(type) || IDSConstants.VALUE_PROPERTY_TYPE_CHAR.equals(type)) - Integer.valueOf(value); - else if (IDSConstants.VALUE_PROPERTY_TYPE_BYTE.equals(type)) - Byte.valueOf(value); - else if (IDSConstants.VALUE_PROPERTY_TYPE_SHORT.equals(type)) - Short.valueOf(value); - } catch (NumberFormatException e) { - reportProblem(annotation, "property", index, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyValue, type, value), String.valueOf(value)); //$NON-NLS-1$ - } - } - } else { - reportProblem(annotation, "property", index, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyType, type), String.valueOf(type)); //$NON-NLS-1$ - } - } - - private void validateComponentPropertyFiles(Annotation annotation, IProject project, String[] files, Collection problems) { - if (errorLevel.isNone()) - return; - - for (int i = 0; i < files.length; ++i) { - String file = files[i]; - IFile wsFile = PDEProject.getBundleRelativeFile(project, new Path(file)); - if (!wsFile.exists()) - reportProblem(annotation, "properties", i, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyFile, file), file); //$NON-NLS-1$ - } - } - - private void validateComponentXMLNS(Annotation annotation, String xmlns, String requiredNs, Collection problems) { - - if (!errorLevel.isNone() && requiredNs.compareTo(xmlns) > 0) - reportProblem(annotation, "xmlns", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentDescriptorNamespace, xmlns), xmlns); //$NON-NLS-1$ - } - - private void validateComponentConfigPID(Annotation annotation, String configPid, Collection problems) { - if (!errorLevel.isNone() && !PID_PATTERN.matcher(configPid).matches()) - reportProblem(annotation, "configurationPid", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentConfigurationPid, configPid), configPid); //$NON-NLS-1$ - } - - private static String getValueOfDefault(Object object) { - if (object instanceof IVariableBinding) { - IVariableBinding binding = (IVariableBinding) object; - if (binding.isEnumConstant()) { - return binding.getName(); - } - } - return object.toString(); - } - - private int validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method, - IDSDocumentFactory factory, Map defaultValues, - Collection problems) { - IMethodBinding methodBinding = method.resolveBinding(); - if (methodBinding == null) { - if (debug.isDebugging()) - debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ - - return 0; - } - String returnTypeName = methodBinding.getReturnType().getName(); - if (!Void.TYPE.getName().equals(returnTypeName)) - reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodReturnType, methodName, returnTypeName), returnTypeName); - - ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes(); - - if (paramTypeBindings.length == 0) - // no-arg method - return 1; - - int requiredSpecVersion = 1; - // every argument must be either Map, ComponentContext, BundleContext or component property type - boolean hasMap = false; - boolean hasCompCtx = false; - boolean hasBundleCtx = false; - boolean hasInt = false; - boolean hasComponentPropertyType = false; - for (ITypeBinding paramTypeBinding : paramTypeBindings) { - String paramTypeName = paramTypeBinding.getErasure().getQualifiedName(); - boolean isDuplicate = false; - - if (Map.class.getName().equals(paramTypeName)) { - if (hasMap) - isDuplicate = true; - else - hasMap = true; - } else if (ComponentContext.class.getName().equals(paramTypeName)) { - if (hasCompCtx) - isDuplicate = true; - else - hasCompCtx = true; - } else if (BundleContext.class.getName().equals(paramTypeName)) { - if (hasBundleCtx) - isDuplicate = true; - else - hasBundleCtx = true; - } else if (paramTypeBinding.isAnnotation()) { - if (hasComponentPropertyType) { - isDuplicate = true; - } - else { - hasComponentPropertyType = true; - // Check the default values in the property. - for (IMethodBinding m : paramTypeBinding.getDeclaredMethods()) { - // Does this property have a default value? - Object defaultValue = m.getDefaultValue(); - if (defaultValue != null) { - IDSProperty property = factory.createProperty(); - Class type = null; - property.setPropertyName(m.getName()); - // Is it an array? If so, fill the body. - if (defaultValue.getClass().isArray()) { - Object[] values = (Object[]) defaultValue; - StringBuffer v = new StringBuffer(); - for (int i = 0; i < values.length; i++) { - Object thisValue = values[i]; - if (thisValue != null) { - type = thisValue.getClass(); - if (v.length() > 0) { - v.append("\n"); - } - v.append(getValueOfDefault(thisValue)); - } - } - property.setPropertyElemBody(v.toString()); - property.setPropertyValue(null); - } - else { - // Normal, single value - type = defaultValue.getClass(); - property.setPropertyValue(getValueOfDefault(defaultValue)); - property.setPropertyElemBody(null); - } - if (type != null) { - property.setPropertyType(type.getSimpleName()); - if (!PROPERTY_TYPES.contains(property.getPropertyType())) { - property.setPropertyType(null); - } - } - defaultValues.put(property.getPropertyName(), property); - } - } - requiredSpecVersion = 3; - } - } else if ("deactivate".equals(methodName) //$NON-NLS-1$ - && (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) { - if (hasInt) - isDuplicate = true; - else - hasInt = true; - } else { - reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName); - } - - if (isDuplicate) - reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_duplicateLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName); - } - return requiredSpecVersion; - } - - private boolean hasLifeCycleMethod(ITypeBinding componentClass, String methodName) { - for (IMethodBinding methodBinding : componentClass.getDeclaredMethods()) { - if (methodName.equals(methodBinding.getName()) - && Void.TYPE.getName().equals(methodBinding.getReturnType().getName())) { - ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes(); - - // every argument must be either Map, ComponentContext, or BundleContext - boolean hasMap = false; - boolean hasCompCtx = false; - boolean hasBundleCtx = false; - boolean hasInt = false; - boolean isInvalid = false; - for (ITypeBinding paramTypeBinding : paramTypeBindings) { - String paramTypeName = paramTypeBinding.getErasure().getQualifiedName(); - - if (Map.class.getName().equals(paramTypeName)) { - if (hasMap) - isInvalid = true; - else - hasMap = true; - } else if (ComponentContext.class.getName().equals(paramTypeName)) { - if (hasCompCtx) - isInvalid = true; - else - hasCompCtx = true; - } else if (paramTypeBinding.isAnnotation()) { - if (hasCompCtx) { - isInvalid = true; - } - else { - hasCompCtx = true; - } - } else if (BundleContext.class.getName().equals(paramTypeName)) { - if (hasBundleCtx) - isInvalid = true; - else - hasBundleCtx = true; - } else if ("deactivate".equals(methodName) //$NON-NLS-1$ - && (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) { - if (hasInt) - isInvalid = true; - else - hasInt = true; - } else { - isInvalid = true; - } - - if (isInvalid) - break; - } - - if (!isInvalid) - return true; - } - } - - return false; - } - - private static Map mapAnnotationBinding(IAnnotationBinding annotationBinding) { - HashMap params = new HashMap(); - for (IMemberValuePairBinding pair : annotationBinding.getDeclaredMemberValuePairs()) { - params.put(pair.getName(), pair.getValue()); - } - return params; - } - - /* - * Process the field references present for a type declaration. The fields are checked and if - * a reference annotation is found, it is passed down the reference annotation processing. - */ - Collection processFieldReferences(TypeDeclaration type, boolean checkAccessible, final Map refMap, - final IDSDocumentFactory factory, final Collection references, - final Map names, final Collection problems) { - // List we are going to return to our parent to show that we actually found references. - final List found = new ArrayList(); - // Loop through the fields. - for (FieldDeclaration field : type.getFields()) { - // If we need to check the protect/public modifiers, do it. This is enabled for superclass parsing. - if (checkAccessible && (field.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) continue; - for (Object modifier : field.modifiers()) { - if (!(modifier instanceof Annotation)) - continue; - Annotation fieldAnnotation = (Annotation) modifier; - IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding(); - if (fieldAnnotationBinding == null) continue; - // Check if it is reference annotation. - String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName(); - if (REFERENCE_ANNOTATION.equals(annotationName)) { - // Process it. - VariableDeclarationFragment fragment = (VariableDeclarationFragment) field.fragments().get(0); - IDSReference goodOne = processReference(field, fragment.getName().getIdentifier(), - fieldAnnotation, fieldAnnotationBinding, refMap, factory, references, names, problems); - // If we found a valid reference, add it to the list to return. - if (goodOne != null) { - found.add(goodOne); - } - } - } - } - return found; - } - - private static ReferenceCardinality _cardinality(Map params) { - ReferenceCardinality cardinalityLiteral = null; - Object value = params.get("cardinality"); - if (value instanceof IVariableBinding) { - IVariableBinding cardinalityBinding = (IVariableBinding) value; - cardinalityLiteral = ReferenceCardinality.valueOf(cardinalityBinding.getName()); - } - else if (value instanceof ReferenceCardinality) { - cardinalityLiteral = (ReferenceCardinality) value; - } - return cardinalityLiteral; - } - - private static String cardinality(Map params) { - ReferenceCardinality cardinalityLiteral = _cardinality(params); - if (cardinalityLiteral != null) - return cardinalityLiteral.toString(); - return null; - } - - private static ReferencePolicy _policy(Map params) { - Object value; - ReferencePolicy policyLiteral = null; - if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$ - IVariableBinding policyBinding = (IVariableBinding) value; - policyLiteral = ReferencePolicy.valueOf(policyBinding.getName()); - } - return policyLiteral; - } - - private static String policy(Map params) { - ReferencePolicy policyLiteral = _policy(params); - if (policyLiteral == null) return null; - return policyLiteral.toString(); - } - - private static ReferencePolicyOption _referencePolicyOption(Map params) { - Object value; - ReferencePolicyOption policyOptionLiteral = null; - if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$ - IVariableBinding policyOptionBinding = (IVariableBinding) value; - policyOptionLiteral = ReferencePolicyOption.valueOf(policyOptionBinding.getName()); - } - return policyOptionLiteral; - } - - private static String referencePolicyOption(Map params) { - ReferencePolicyOption policyOptionLiteral = _referencePolicyOption(params); - if (policyOptionLiteral != null) { - return policyOptionLiteral.toString(); - } - return null; - } - - private String target(Map params, Annotation annotation, Collection problems) { - String target = null; - Object value; - if ((value = params.get("target")) instanceof String) { //$NON-NLS-1$ - target = (String) value; - validateReferenceTarget(annotation, target, problems); - } - return target; - } - - private static String scope(Map params) { - String scope = null; - Object value; - if ((value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$ - IVariableBinding scopeBinding = (IVariableBinding) value; - ReferenceScope referenceScope = ReferenceScope.valueOf(scopeBinding.getName()); - if (referenceScope != null && !ReferenceScope.BUNDLE.equals(referenceScope)) { - scope = referenceScope.toString(); - } - } - return scope; - } - - /* - * Common handling of the reference processing. Does the parts that are common to both field and - * method based references and constructs a reference for it. - */ - private IDSReference reference(String defaultName, String service, - Annotation annotation, Map params, Map refMap, IDSDocumentFactory factory, Collection collector, - Map names, Collection problems) { - String name = null; - Object value; - if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$ - name = (String) value; - } - else { - name = defaultName; - } - IDSReference reference = refMap.remove(name); - if (reference == null) { - reference = factory.createReference(); - } - collector.add(reference); - if (!errorLevel.isNone()) { - if (names.containsKey(name)) { - reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ - Annotation duplicate = names.put(name, null); - if (duplicate != null) - reportProblem(duplicate, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ - } else { - names.put(name, annotation); - } - } - - if (name == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_NAME, null); - } else { - reference.setReferenceName(name); - } - - if (service == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_INTERFACE, null); - } else { - reference.setReferenceInterface(service); - } - String cardinality = cardinality(params); - String policy = policy(params); - String target = target(params, annotation, problems); - String policyOption = referencePolicyOption(params); - String scope = scope(params); - if (cardinality == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_CARDINALITY, IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_ONE); - } else { - reference.setReferenceCardinality(cardinality); - } - - if (policy == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_POLICY, IDSConstants.VALUE_REFERENCE_POLICY_STATIC); - } else { - reference.setReferencePolicy(policy); - } - - if (target == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_TARGET, null); - } else { - reference.setReferenceTarget(target); - } - - if (policyOption == null) { - removeAttribute(reference, ATTRIBUTE_REFERENCE_POLICY_OPTION, VALUE_REFERENCE_POLICY_OPTION_RELUCTANT); - } else { - reference.setXMLAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION, policyOption); - } - - if (scope != null) { - reference.setXMLAttribute("scope", scope); - } - else { - removeAttribute(reference, "scope", null); - } - return reference; - } - - private static ITypeBinding bindingExcludingObject(Type type) { - ITypeBinding b = type.resolveBinding(); - if (b == null || b.getBinaryName().equals("java.lang.Object")) { - b = null; - } - return b; - } - - /* - * Get the contained type for a parameterized type. This to determine the service type from the parameterized - * indications. - */ - private Type containedType(Type type, int index) { - Type contained = null; - if (type.isParameterizedType()) { - ParameterizedType thisType = (ParameterizedType) type; - if (thisType.typeArguments().size() > index) { - contained = (Type) thisType.typeArguments().get(index); - if (bindingExcludingObject(contained) == null) { - contained = null; - } - } - } - return contained; - } - - /* - * Parse the field collection type for a specific field type. All according to SCR 1.3, section 112.3.3 - * Returns both the service type (as return value) and the field collection type in the passed string buffer. - */ - private ITypeBinding getFieldCollectionType(Type type, StringBuffer fct) { - if (type == null) return null; - ITypeBinding binding = bindingExcludingObject(type); - if (binding == null) return null; - String containedName = binding.getBinaryName(); - Type serviceType = null; - String fieldCollectionType = null; - if (Map.class.getName().equals(containedName)) { - fieldCollectionType = "properties"; - } - else if (ServiceReference.class.getName().equals(containedName)) { - fieldCollectionType = "reference"; - serviceType = containedType(type, 0); - } - else if (Map.Entry.class.getName().equals(containedName)) { - fieldCollectionType = "tuple"; - serviceType = containedType(type, 1); - } - else if ("org.osgi.service.component.ComponentServiceObjects".equals(containedName)) { - fieldCollectionType = "serviceobjects"; - serviceType = containedType(type, 0); - } - else { - serviceType = type; - } - if (fieldCollectionType != null && fct != null) { - fct.append(fieldCollectionType); - } - return (serviceType == null) ? null : bindingExcludingObject(serviceType); - } - - /* - * Process a field declaration with a reference annotation. As indicated in the SCR specification v1.3, - * fields can be either collection types (for multiple references) or single object types. Both types - * are handled here, generating warnings and errors on the way. - */ - private IDSReference processReference(FieldDeclaration field, String fieldName, - Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, - IDSDocumentFactory factory, Collection collector, Map names, - Collection problems) { - // Map the annotation properties. - Map params = mapAnnotationBinding(annotationBinding); - // Check the service type of the field. Can be either a map, collection, entry or any other object type. - Type type = field.getType(); - ITypeBinding binding = type.resolveBinding(); - if (binding == null) return null; - // Now the processing of the type of the field. There are a couple of options, but - // the first discrimination is made between collections and "normal" objects. - ITypeBinding defaultService = null; - IType itype = (IType) binding.getJavaElement(); - // Try to determine whether the field is a collection. - boolean isCollection = false; - try { - ITypeHierarchy typeHierarchy = itype.newTypeHierarchy(new NullProgressMonitor()); - for (IType t : typeHierarchy.getAllInterfaces()) { - if (t.getFullyQualifiedName().equals(Collection.class.getName())) { - isCollection = true; - } - } - } catch (Exception exc) { - exc.printStackTrace(); - return null; - } - StringBuffer fieldCollectionType = new StringBuffer(); - ReferenceCardinality cardinality = _cardinality(params); - if (isCollection) { - // If the cardinality specifies a 1..1 relation, we should generate a warning (the SCR handler - // probably will fail anyway). If no cardinality is specified, we assume at least one (1..n) since - // we are processing a collection. AFAIK not in the specifications, but seems logical. - if (ReferenceCardinality.MANDATORY.equals(cardinality)) { - reportProblem(annotation, "cardinality", ValidationErrorLevel.warning, problems, - NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); - } - else if (cardinality == null) { - params.put("cardinality", ReferenceCardinality.AT_LEAST_ONE); - } - // Since it is a collection, we can check the type of the collection from the parameterized value (if present). - // This determines the collection type. - defaultService = getFieldCollectionType(containedType(type, 0), fieldCollectionType); - } - else { - // No collection. If it is not one of the known types, like service reference, etc., - // just get the type of the field itself. - defaultService = getFieldCollectionType(type, null); - // We cannot have a multiple relation here. - if (EnumSet.of(ReferenceCardinality.AT_LEAST_ONE, ReferenceCardinality.MULTIPLE).contains(cardinality)) { - reportProblem(annotation, "cardinality", ValidationErrorLevel.error, problems, - NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); - } - } - // Dynamic fields should be marked volatile. - if (ReferencePolicy.DYNAMIC.equals(_policy(params)) && (field.getModifiers() & Modifier.VOLATILE) == 0) { - reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, - Messages.AnnotationProcessor_dynamicShouldBeVolatile); - } - // Impossible to have a final modifier since it cannot be replaced then. - if ((field.getModifiers() & Modifier.FINAL) != 0) { - reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, - Messages.AnnotationProcessor_referenceAndFinal); - } - // Check the service specification. If the service is specified, this is the easiest solution, since the programmer - // specifies what the service is. If this one is unavailable and we could not determine the service - // type above, report and issue. - Object s = params.get("service"); - IDSReference reference = null; - if (s == null && defaultService == null) { - reportProblem(annotation, "service", problems, Messages.AnnotationProcessor_unknownServiceType); - } - else { - // OK, valid. Check whether the default service type we determined matches the service - // specification. - String serviceName; - if (s == null || !(s instanceof ITypeBinding)) { - serviceName = defaultService.getBinaryName(); - } - else { - ITypeBinding serviceType = (ITypeBinding) s; - // Check the type compatibility and report a problem if not. - if (defaultService != null && !defaultService.isAssignmentCompatible(serviceType)) { - reportProblem(annotation, "service", problems, - NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, - defaultService.getName(), serviceType.getName())); - } - serviceName = serviceType.getBinaryName(); - } - // Create the service reference. - reference = reference(fieldName, serviceName, - annotation, params, refMap, factory, collector, names, problems); - // Add some attributes because of field injection. Note that these attributes - // are not known by the IDS stuff in the current version, and are therefore created by hand - // in stead of convenience methods. - reference.setXMLAttribute("field", fieldName); - if (fieldCollectionType.length() > 0) { - reference.setXMLAttribute("field-collection-type", fieldCollectionType.toString()); - } - else { - removeAttribute(reference, "field-collection-type", null); - } - // Field option: do we want the field replaced or updated? Only works for collections. - FieldOption fieldOption = null; - Object value = params.get("fieldOption"); - if (value instanceof IVariableBinding) { - IVariableBinding scopeBinding = (IVariableBinding) value; - fieldOption = FieldOption.valueOf(scopeBinding.getName()); - } - if (fieldOption != null && FieldOption.UPDATE.equals(fieldOption)) { - if (!isCollection) { - reportProblem(annotation, "fieldOption", ValidationErrorLevel.error, problems, - Messages.AnnotationProcessor_updateOnlyForCollections); - } - ReferencePolicy policy = _policy(params); - if (!ReferencePolicy.DYNAMIC.equals(policy)) { - reportProblem(annotation, "fieldOption", ValidationErrorLevel.error, problems, - Messages.AnnotationProcessor_updateOnlyForDynamic); - } - reference.setXMLAttribute("field-option", fieldOption.toString()); - } - else { - removeAttribute(reference, "field-option", null); - } - } - return reference; - } - - private int processReference(MethodDeclaration method, IMethodBinding methodBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, IDSDocumentFactory factory, Collection collector, Map names, Collection problems) { - Map params = mapAnnotationBinding(annotationBinding); - - ITypeBinding[] argTypes = methodBinding.getParameterTypes(); - - ITypeBinding serviceType; - Object value; - if ((value = params.get("service")) instanceof ITypeBinding) { //$NON-NLS-1$ - serviceType = (ITypeBinding) value; - if (!errorLevel.isNone() && argTypes.length > 0) { - ITypeBinding[] typeArgs; - if (!(ServiceReference.class.getName().equals(argTypes[0].getErasure().getQualifiedName()) - && ((typeArgs = argTypes[0].getTypeArguments()).length == 0 || serviceType.isAssignmentCompatible(typeArgs[0]))) - && !serviceType.isAssignmentCompatible(argTypes[0])) - reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, argTypes[0].getName(), serviceType.getName()), serviceType.getName()); //$NON-NLS-1$ - } - } else if (argTypes.length > 0) { - if (ServiceReference.class.getName().equals(argTypes[0].getErasure().getQualifiedName())) { - ITypeBinding[] typeArgs = argTypes[0].getTypeArguments(); - if (typeArgs.length > 0) - serviceType = typeArgs[0]; - else - serviceType = null; - } else { - serviceType = argTypes[0].isPrimitive() ? getObjectType(method.getAST(), argTypes[0]) : argTypes[0]; - } - } else { - serviceType = null; - } - - if (serviceType == null) { - reportProblem(annotation, null, problems, Messages.AnnotationProcessor_invalidReferenceServiceUnknown); - - serviceType = method.getAST().resolveWellKnownType(Object.class.getName()); - } - - validateReferenceBindMethod(annotation, serviceType, methodBinding, problems); - - String service = serviceType == null ? null : serviceType.getBinaryName(); - - String methodName = methodBinding.getName(); - String name; - if (methodName.startsWith("bind")) { //$NON-NLS-1$ - name = methodName.substring("bind".length()); //$NON-NLS-1$ - } else if (methodName.startsWith("set")) { //$NON-NLS-1$ - name = methodName.substring("set".length()); //$NON-NLS-1$ - } else if (methodName.startsWith("add")) { //$NON-NLS-1$ - name = methodName.substring("add".length()); //$NON-NLS-1$ - } else { - name = methodName; - } - - String unbind; - if ((value = params.get("unbind")) instanceof String) { //$NON-NLS-1$ - String unbindValue = (String) value; - if ("-".equals(unbindValue)) { //$NON-NLS-1$ - unbind = null; - } else { - unbind = unbindValue; - if (!errorLevel.isNone()) { - IMethodBinding unbindMethod = findUnbindMethod(methodBinding.getDeclaringClass(), serviceType, unbind, true); - if (unbindMethod == null) - reportProblem(annotation, "unbind", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceUnbind, unbind), unbind); //$NON-NLS-1$ - } - } - } else { - String unbindCandidate; - if (methodName.startsWith("add")) { //$NON-NLS-1$ - unbindCandidate = "remove" + methodName.substring("add".length()); //$NON-NLS-1$ //$NON-NLS-2$ - } else { - unbindCandidate = "un" + methodName; //$NON-NLS-1$ - } - - IMethodBinding unbindMethod = findUnbindMethod(methodBinding.getDeclaringClass(), serviceType, unbindCandidate, false); - if (unbindMethod == null) { - unbind = null; - reportProblem(annotation, null, missingUnbindMethodLevel, problems, NLS.bind(Messages.AnnotationProcessor_noImplicitReferenceUnbind, unbindCandidate), unbindCandidate); - } else { - unbind = unbindMethod.getName(); - } - } - - String updated; - if ((value = params.get(ATTRIBUTE_REFERENCE_UPDATED)) instanceof String) { //$NON-NLS-1$ - String updatedValue = (String) value; - if ("-".equals(updatedValue)) { //$NON-NLS-1$ - updated = null; - } else { - updated = updatedValue; - if (!errorLevel.isNone()) { - IMethodBinding updatedMethod = findUpdatedMethod(methodBinding.getDeclaringClass(), updated, true); - if (updatedMethod == null) - reportProblem(annotation, ATTRIBUTE_REFERENCE_UPDATED, problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceUpdated, updated), updated); //$NON-NLS-1$ - } - } - } else { - String updatedCandidate; - if (methodName.startsWith("bind")) { //$NON-NLS-1$ - updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("bind".length()); //$NON-NLS-1$ //$NON-NLS-2$ - } else if (methodName.startsWith("set")) { //$NON-NLS-1$ - updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("set".length()); //$NON-NLS-1$ //$NON-NLS-2$ - } else if (methodName.startsWith("add")) { //$NON-NLS-1$ - updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("add".length()); //$NON-NLS-1$ //$NON-NLS-2$ - } else { - updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName; //$NON-NLS-1$ - } - - IMethodBinding updatedMethod = findUpdatedMethod(methodBinding.getDeclaringClass(), updatedCandidate, false); - if (updatedMethod == null) - updated = null; - else - updated = updatedMethod.getName(); - } - - IDSReference reference = this.reference(name, service, annotation, params, refMap, - factory, collector, names, problems); - - reference.setReferenceBind(methodName); - - if (unbind == null) { - removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_UNBIND, null); - } else { - reference.setReferenceUnbind(unbind); - } - - if (updated == null) { - removeAttribute(reference, ATTRIBUTE_REFERENCE_UPDATED, null); - } else { - reference.setXMLAttribute(ATTRIBUTE_REFERENCE_UPDATED, updated); - } - - if (scope(params) != null) { - return 3; - } - return (reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION) != null - || reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_UPDATED) != null) ? 2 : 1; - } - - private ITypeBinding getObjectType(AST ast, ITypeBinding primitive) { - if (Boolean.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Boolean.class.getName()); - - if (Byte.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Byte.class.getName()); - - if (Character.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Character.class.getName()); - - if (Double.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Double.class.getName()); - - if (Float.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Float.class.getName()); - - if (Integer.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Integer.class.getName()); - - if (Long.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Long.class.getName()); - - if (Short.TYPE.getName().equals(primitive.getName())) - return ast.resolveWellKnownType(Short.class.getName()); - - return null; - } - - private void validateReferenceBindMethod(Annotation annotation, ITypeBinding serviceType, IMethodBinding methodBinding, Collection problems) { - if (errorLevel.isNone()) - return; - - String returnTypeName = methodBinding.getReturnType().getName(); - if (!Void.TYPE.getName().equals(returnTypeName)) - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidBindMethodReturnType, returnTypeName), returnTypeName); - - ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes(); - if (!(paramTypeBindings.length == 1 && (ServiceReference.class.getName().equals(paramTypeBindings[0].getErasure().getQualifiedName()) || serviceType == null || serviceType.isAssignmentCompatible(paramTypeBindings[0]))) - && !(paramTypeBindings.length == 2 && (serviceType == null || serviceType.isAssignmentCompatible(paramTypeBindings[0])) && Map.class.getName().equals(paramTypeBindings[1].getErasure().getQualifiedName()))) { - String[] params = new String[paramTypeBindings.length]; - StringBuilder buf = new StringBuilder(64); - buf.append('('); - for (int i = 0; i < params.length; ++i) { - params[i] = paramTypeBindings[i].getName(); - if (buf.length() > 1) - buf.append(", "); //$NON-NLS-1$ - - buf.append(params[i]); - } - - buf.append(')'); - reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidBindMethodParameters, buf, serviceType == null ? Messages.AnnotationProcessor_unknownServiceTypeLabel : serviceType.getName()), params); - } - } - - private void validateReferenceTarget(Annotation annotation, String target, Collection problems) { - if (errorLevel.isNone()) - return; - - try { - FrameworkUtil.createFilter(target); - } catch (InvalidSyntaxException e) { - String msg = e.getMessage(); - String suffix = ": " + e.getFilter(); //$NON-NLS-1$ - if (msg.endsWith(suffix)) - msg = msg.substring(0, msg.length() - suffix.length()); - - reportProblem(annotation, "target", problems, msg, target); //$NON-NLS-1$ - } - } - - private IMethodBinding findUnbindMethod(ITypeBinding componentClass, ITypeBinding serviceType, String name, boolean recurse) { - ITypeBinding testedClass = componentClass; - - IMethodBinding candidate = null; - int priority = 0; - // priority: - // 0: , Map - // 1: , Map - // 2: - // 3: - do { - for (IMethodBinding declaredMethod : testedClass.getDeclaredMethods()) { - if (name.equals(declaredMethod.getName()) - && Void.TYPE.getName().equals(declaredMethod.getReturnType().getName()) - && (testedClass == componentClass - || Modifier.isPublic(declaredMethod.getModifiers()) - || Modifier.isProtected(declaredMethod.getModifiers()) - || (!Modifier.isPrivate(declaredMethod.getModifiers()) - && testedClass.getPackage().isEqualTo(componentClass.getPackage())))) { - ITypeBinding[] paramTypes = declaredMethod.getParameterTypes(); - if (paramTypes.length == 1) { - if (ServiceReference.class.getName().equals(paramTypes[0].getErasure().getQualifiedName())) - // we have the winner - return declaredMethod; - - if (priority < 3 && serviceType != null && serviceType.isEqualTo(paramTypes[0])) - priority = 3; - else if (priority < 2 && serviceType != null && serviceType.isAssignmentCompatible(paramTypes[0])) - priority = 2; - else - continue; - - // we have a (better) candidate - candidate = declaredMethod; - } else if (paramTypes.length == 2) { - if (priority < 1 - && serviceType != null && serviceType.isEqualTo(paramTypes[0]) - && Map.class.getName().equals(paramTypes[1].getErasure().getQualifiedName())) - priority = 1; - else if (candidate != null - || !(serviceType != null && serviceType.isAssignmentCompatible(paramTypes[0])) - || !Map.class.getName().equals(paramTypes[1].getErasure().getQualifiedName())) - continue; - - // we have a candidate - candidate = declaredMethod; - } - } - } - } while (recurse && (testedClass = testedClass.getSuperclass()) != null); - - return candidate; - } - - private IMethodBinding findUpdatedMethod(ITypeBinding componentClass, String name, boolean recurse) { - ITypeBinding testedClass = componentClass; - - IMethodBinding candidate = null; - do { - for (IMethodBinding declaredMethod : testedClass.getDeclaredMethods()) { - if (name.equals(declaredMethod.getName()) - && Void.TYPE.getName().equals(declaredMethod.getReturnType().getName()) - && (testedClass == componentClass - || Modifier.isPublic(declaredMethod.getModifiers()) - || Modifier.isProtected(declaredMethod.getModifiers()) - || (!Modifier.isPrivate(declaredMethod.getModifiers()) - && testedClass.getPackage().isEqualTo(componentClass.getPackage())))) { - ITypeBinding[] paramTypes = declaredMethod.getParameterTypes(); - if (paramTypes.length == 1) { - if (ServiceReference.class.getName().equals(paramTypes[0].getErasure().getQualifiedName())) - // we have the winner - return declaredMethod; - - if (candidate == null && Map.class.getName().equals(paramTypes[0].getErasure().getQualifiedName())) { - // we have a candidate - candidate = declaredMethod; - } - } - } - } - } while (recurse && (testedClass = testedClass.getSuperclass()) != null); - - return candidate; - } - - private void reportProblem(Annotation annotation, String member, Collection problems, String message, String... args) { - reportProblem(annotation, member, -1, problems, message, args); - } - - private void reportProblem(Annotation annotation, String member, ValidationErrorLevel errorLevel, Collection problems, String message, String... args) { - reportProblem(annotation, member, -1, errorLevel, problems, message, args); - } - - private void reportProblem(Annotation annotation, String member, int valueIndex, Collection problems, String message, String... args) { - reportProblem(annotation, member, valueIndex, errorLevel, problems, message, args); - } - - private void reportProblem(Annotation annotation, String member, int valueIndex, ValidationErrorLevel errorLevel, Collection problems, String message, String... args) { - if (errorLevel.isNone()) - return; - - Expression memberValue = annotation; - if (annotation.isNormalAnnotation() && member != null) { - NormalAnnotation na = (NormalAnnotation) annotation; - for (Object value : na.values()) { - MemberValuePair pair = (MemberValuePair) value; - if (member.equals(pair.getName().getIdentifier())) { - memberValue = pair.getValue(); - break; - } - } - } else if (annotation.isSingleMemberAnnotation()) { - SingleMemberAnnotation sma = (SingleMemberAnnotation) annotation; - memberValue = sma.getValue(); - } - - int start = memberValue.getStartPosition(); - int length = memberValue.getLength(); - - if (valueIndex >= 0 && memberValue instanceof ArrayInitializer) { - ArrayInitializer ai = (ArrayInitializer) memberValue; - if (valueIndex < ai.expressions().size()) { - Expression element = (Expression) ai.expressions().get(valueIndex); - start = element.getStartPosition(); - length = element.getLength(); - } - } - - if (start >= 0) { - DSAnnotationProblem problem = new DSAnnotationProblem((CompilationUnit) annotation.getRoot(), - errorLevel.isError(), message, args); - problem.setSourceStart(start); - problem.setSourceEnd(start + length - 1); - problems.add(problem); - } - - } +/******************************************************************************* + * Copyright (c) 2012, 2015 Ecliptical Software Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ecliptical Software Inc. - initial API and implementation + *******************************************************************************/ +package ca.ecliptical.pde.internal.ds; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.regex.Pattern; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filebuffers.ITextFileBufferManager; +import org.eclipse.core.filebuffers.LocationKind; +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.compiler.BuildContext; +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTRequestor; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.ArrayInitializer; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.IAnnotationBinding; +import org.eclipse.jdt.core.dom.IMemberValuePairBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.ParameterizedType; +import org.eclipse.jdt.core.dom.SingleMemberAnnotation; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentExtension4; +import org.eclipse.jface.text.link.LinkedModeModel; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.IModelChangedEvent; +import org.eclipse.pde.core.ModelChangedEvent; +import org.eclipse.pde.internal.core.project.PDEProject; +import org.eclipse.pde.internal.core.text.IDocumentAttributeNode; +import org.eclipse.pde.internal.core.text.IDocumentElementNode; +import org.eclipse.pde.internal.core.text.IDocumentObject; +import org.eclipse.pde.internal.core.text.IDocumentTextNode; +import org.eclipse.pde.internal.core.text.IModelTextChangeListener; +import org.eclipse.pde.internal.ds.core.IDSComponent; +import org.eclipse.pde.internal.ds.core.IDSConstants; +import org.eclipse.pde.internal.ds.core.IDSDocumentFactory; +import org.eclipse.pde.internal.ds.core.IDSImplementation; +import org.eclipse.pde.internal.ds.core.IDSModel; +import org.eclipse.pde.internal.ds.core.IDSObject; +import org.eclipse.pde.internal.ds.core.IDSProperties; +import org.eclipse.pde.internal.ds.core.IDSProperty; +import org.eclipse.pde.internal.ds.core.IDSProvide; +import org.eclipse.pde.internal.ds.core.IDSReference; +import org.eclipse.pde.internal.ds.core.IDSService; +import org.eclipse.pde.internal.ds.core.text.DSModel; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.FieldOption; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.component.annotations.ReferenceScope; +import org.osgi.service.component.annotations.ServiceScope; + +@SuppressWarnings("restriction") +public class AnnotationProcessor extends ASTRequestor { + + private static final String DS_BUILDER = "org.eclipse.pde.ds.core.builder"; //$NON-NLS-1$ + + static final Debug debug = Debug.getDebug("ds-annotation-builder/processor"); //$NON-NLS-1$ + + private final ProjectContext context; + + private final Map fileMap; + + private boolean hasBuilder; + + public AnnotationProcessor(ProjectContext context, Map fileMap) { + this.context = context; + this.fileMap = fileMap; + } + + @Override + public void acceptAST(ICompilationUnit source, CompilationUnit ast) { + // determine CU key + String cuKey; + IJavaElement parent = source.getParent(); + if (parent == null) + cuKey = source.getElementName(); + else + cuKey = String.format("%s/%s", parent.getElementName().replace('.', '/'), source.getElementName()); //$NON-NLS-1$ + + context.getUnprocessed().remove(cuKey); + + ProjectState state = context.getState(); + HashMap dsKeys = new HashMap(); + HashSet problems = new HashSet(); + + ast.accept(new AnnotationVisitor(this, state, dsKeys, problems)); + + // track abandoned files (may be garbage) + Collection oldDSKeys = state.updateMappings(cuKey, dsKeys); + if (oldDSKeys != null) { + oldDSKeys.removeAll(dsKeys.values()); + context.getAbandoned().addAll(oldDSKeys); + } + + if (!problems.isEmpty()) { + // Remap the problems by compilation unit. We may have gotten problems from various + // sources and the problems should be shown with the correct file. + Map> remapped = new HashMap>(); + for (DSAnnotationProblem p : problems) { + List thisUnit = remapped.get(p.getUnit()); + if (thisUnit == null) { + thisUnit = new ArrayList(); + remapped.put(p.getUnit(), thisUnit); + } + thisUnit.add(p); + } + // Process the problems per file/compilation unit. + for (Map.Entry> entry : remapped.entrySet()) { + ICompilationUnit iu = (ICompilationUnit) entry.getKey().getJavaElement(); + char[] filename = iu.getResource().getFullPath().toString().toCharArray(); + List thisProblems = entry.getValue(); + for (DSAnnotationProblem problem : thisProblems) { + problem.setOriginatingFileName(filename); + if (problem.getSourceStart() >= 0) + problem.setSourceLineNumber(entry.getKey().getLineNumber(problem.getSourceStart())); + } + BuildContext buildContext = fileMap.get(iu); + // And show the errors/warnings. + if (buildContext != null) + buildContext.recordNewProblems(thisProblems.toArray(new CategorizedProblem[thisProblems.size()])); + } + } + } + + private void ensureDSProject(IProject project) throws CoreException { + IProjectDescription description = project.getDescription(); + ICommand[] commands = description.getBuildSpec(); + + for (ICommand command : commands) { + if (DS_BUILDER.equals(command.getBuilderName())) + return; + } + + ICommand[] newCommands = new ICommand[commands.length + 1]; + System.arraycopy(commands, 0, newCommands, 0, commands.length); + ICommand command = description.newCommand(); + command.setBuilderName(DS_BUILDER); + newCommands[newCommands.length - 1] = command; + description.setBuildSpec(newCommands); + project.setDescription(description, null); + } + + private void ensureExists(IFolder folder) throws CoreException { + if (folder.exists()) + return; + + IContainer parent = folder.getParent(); + if (parent != null && parent.getType() == IResource.FOLDER) + ensureExists((IFolder) parent); + + folder.create(true, true, null); + } + + void verifyOutputLocation(IFile file) throws CoreException { + if (hasBuilder) + return; + + hasBuilder = true; + IProject project = file.getProject(); + + IPath parentPath = file.getParent().getProjectRelativePath(); + if (!parentPath.isEmpty()) { + IFolder folder = project.getFolder(parentPath); + ensureExists(folder); + } + + try { + ensureDSProject(project); + } catch (CoreException e) { + Activator.getDefault().getLog().log(e.getStatus()); + } + } +} + +@SuppressWarnings("restriction") +class AnnotationVisitor extends ASTVisitor { + + private static final String COMPONENT_ANNOTATION = DSAnnotationCompilationParticipant.COMPONENT_ANNOTATION; + + private static final String ACTIVATE_ANNOTATION = Activate.class.getName(); + + private static final String MODIFIED_ANNOTATION = Modified.class.getName(); + + private static final String DEACTIVATE_ANNOTATION = Deactivate.class.getName(); + + private static final String REFERENCE_ANNOTATION = Reference.class.getName(); + + private static final Pattern PID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*"); //$NON-NLS-1$ + + private static final String NAMESPACE_1_1 = IDSConstants.NAMESPACE; + + private static final String NAMESPACE_1_2 = "http://www.osgi.org/xmlns/scr/v1.2.0"; //$NON-NLS-1$ + + private static final String NAMESPACE_1_3 = "http://www.osgi.org/xmlns/scr/v1.3.0"; //$NON_NLS-1$ + + private static final String ATTRIBUTE_COMPONENT_CONFIGURATION_PID = "configuration-pid"; //$NON-NLS-1$ + + private static final String ATTRIBUTE_REFERENCE_POLICY_OPTION = "policy-option"; //$NON-NLS-1$ + + private static final String ATTRIBUTE_REFERENCE_UPDATED = "updated"; //$NON-NLS-1$ + + private static final String VALUE_REFERENCE_POLICY_OPTION_RELUCTANT = "reluctant"; //$NON-NLS-1$ + + private static final Set PROPERTY_TYPES = Collections.unmodifiableSet( + new HashSet( + Arrays.asList( + null, + IDSConstants.VALUE_PROPERTY_TYPE_STRING, + IDSConstants.VALUE_PROPERTY_TYPE_LONG, + IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE, + IDSConstants.VALUE_PROPERTY_TYPE_FLOAT, + IDSConstants.VALUE_PROPERTY_TYPE_INTEGER, + IDSConstants.VALUE_PROPERTY_TYPE_BYTE, + IDSConstants.VALUE_PROPERTY_TYPE_CHAR, + IDSConstants.VALUE_PROPERTY_TYPE_BOOLEAN, + IDSConstants.VALUE_PROPERTY_TYPE_SHORT))); + + private static final Comparator REF_NAME_COMPARATOR = new Comparator() { + + public int compare(IDSReference o1, IDSReference o2) { + return o1.getReferenceName().compareTo(o2.getReferenceName()); + } + }; + + private static final Debug debug = AnnotationProcessor.debug; + + private final AnnotationProcessor processor; + + private final ProjectState state; + + private final ValidationErrorLevel errorLevel; + + private final ValidationErrorLevel missingUnbindMethodLevel; + + private final Map dsKeys; + + private final Set problems; + + public AnnotationVisitor(AnnotationProcessor processor, ProjectState state, Map dsKeys, Set problems) { + this.processor = processor; + this.state = state; + this.errorLevel = state.getErrorLevel(); + this.missingUnbindMethodLevel = state.getMissingUnbindMethodLevel(); + this.dsKeys = dsKeys; + this.problems = problems; + } + + @Override + public boolean visit(TypeDeclaration type) { + if (!Modifier.isPublic(type.getModifiers())) { + // non-public types cannot be (or have nested) components + if (errorLevel.isNone()) + return false; + + Annotation annotation = findComponentAnnotation(type); + if (annotation != null) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notPublic, type.getName().getIdentifier()), type.getName().getIdentifier()); + + return true; + } + + Annotation annotation = findComponentAnnotation(type); + if (annotation != null) { + boolean isInterface = false; + boolean isAbstract = false; + boolean isNested = false; + boolean noDefaultConstructor = false; + if ((isInterface = type.isInterface()) + || (isAbstract = Modifier.isAbstract(type.getModifiers())) + || (isNested = (!type.isPackageMemberTypeDeclaration() && !isNestedPublicStatic(type))) + || (noDefaultConstructor = !hasDefaultConstructor(type))) { + // interfaces, abstract types, non-static/non-public nested types, or types with no default constructor cannot be components + if (errorLevel != ValidationErrorLevel.none) { + if (isInterface) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_interface, type.getName().getIdentifier()), type.getName().getIdentifier()); + else if (isAbstract) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_abstract, type.getName().getIdentifier()), type.getName().getIdentifier()); + else if (isNested) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_notTopLevel, type.getName().getIdentifier()), type.getName().getIdentifier()); + else if (noDefaultConstructor) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_noDefaultConstructor, type.getName().getIdentifier()), type.getName().getIdentifier()); + else + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentImplementationClass, type.getName().getIdentifier()), type.getName().getIdentifier()); + } + } else { + ITypeBinding typeBinding = type.resolveBinding(); + if (typeBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for type: %s", type)); //$NON-NLS-1$ + } else { + IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding(); + if (annotationBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$ + } else { + try { + processComponent(type, typeBinding, annotation, annotationBinding, problems); + } catch (CoreException e) { + Activator.getDefault().getLog().log(e.getStatus()); + } + } + } + } + } + + return true; + } + + @Override + public boolean visit(EnumDeclaration node) { + Annotation annotation = findComponentAnnotation(node); + if (annotation != null) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_enumeration, node.getName().getIdentifier()), node.getName().getIdentifier()); + + return false; + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + Annotation annotation = findComponentAnnotation(node); + if (annotation != null) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidCompImplClass_annotation, node.getName().getIdentifier()), node.getName().getIdentifier()); + + return true; + } + + private Annotation findComponentAnnotation(AbstractTypeDeclaration type) { + for (Object item : type.modifiers()) { + if (!(item instanceof Annotation)) + continue; + + Annotation annotation = (Annotation) item; + IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding(); + if (annotationBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for annotation: %s", annotation)); //$NON-NLS-1$ + + continue; + } + + if (COMPONENT_ANNOTATION.equals(annotationBinding.getAnnotationType().getQualifiedName())) + return annotation; + } + + return null; + } + + private boolean isNestedPublicStatic(AbstractTypeDeclaration type) { + if (Modifier.isStatic(type.getModifiers())) { + ASTNode parent = type.getParent(); + if (parent != null && (parent.getNodeType() == ASTNode.TYPE_DECLARATION || parent.getNodeType() == ASTNode.ANNOTATION_TYPE_DECLARATION)) { + AbstractTypeDeclaration parentType = (AbstractTypeDeclaration) parent; + if (Modifier.isPublic(parentType.getModifiers())) + return parentType.isPackageMemberTypeDeclaration() || isNestedPublicStatic(parentType); + } + } + + return false; + } + + private boolean hasDefaultConstructor(TypeDeclaration type) { + boolean hasConstructor = false; + for (MethodDeclaration method : type.getMethods()) { + if (method.isConstructor()) { + hasConstructor = true; + if (Modifier.isPublic(method.getModifiers()) && method.parameters().isEmpty()) + return true; + } + } + + return !hasConstructor; + } + + private void processComponent(TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Collection problems) throws CoreException { + // determine component name + HashMap params = new HashMap(); + for (IMemberValuePairBinding pair : annotationBinding.getDeclaredMemberValuePairs()) { + params.put(pair.getName(), pair.getValue()); + } + + String implClass = typeBinding.getBinaryName(); + + String name = implClass; + Object value; + if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$ + name = (String) value; + validateComponentName(annotation, name, problems); + } + + // set up document to edit + IPath path = new Path(state.getPath()).append(name).addFileExtension("xml"); //$NON-NLS-1$ + + String dsKey = path.toPortableString(); + dsKeys.put(implClass, dsKey); + + IProject project = typeBinding.getJavaElement().getJavaProject().getProject(); + IFile file = PDEProject.getBundleRelativeFile(project, path); + IPath filePath = file.getFullPath(); + + processor.verifyOutputLocation(file); + + // handle file move/rename + String oldPath = state.getModelFile(implClass); + if (oldPath != null && !oldPath.equals(dsKey) && !file.exists()) { + IFile oldFile = PDEProject.getBundleRelativeFile(project, Path.fromPortableString(oldPath)); + if (oldFile.exists()) { + try { + oldFile.move(file.getFullPath(), true, true, null); + } catch (CoreException e) { + Activator.getDefault().getLog().log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, String.format("Unable to move model file from '%s' to '%s'.", oldPath, file.getFullPath()), e)); //$NON-NLS-1$ + } + } + } + + ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager(); + bufferManager.connect(filePath, LocationKind.IFILE, null); + ITextFileBuffer buffer = bufferManager.getTextFileBuffer(filePath, LocationKind.IFILE); + if (buffer.isDirty()) + buffer.commit(null, true); + + IDocument document = buffer.getDocument(); + final DSModel dsModel = new DSModel(document, true); + dsModel.setUnderlyingResource(file); + dsModel.setCharset("UTF-8"); //$NON-NLS-1$ + dsModel.load(); + + // note: we can't use XMLTextChangeListener because it generates overlapping edits! + // thus we replace the entire content with one edit (if changed) + final IDocument fDoc = document; + dsModel.addModelChangedListener(new IModelTextChangeListener() { + + private final IDocument document = fDoc; + + private boolean changed; + + public void modelChanged(IModelChangedEvent event) { + changed = true; + } + + public TextEdit[] getTextOperations() { + if (!changed) + return new TextEdit[0]; + + String text = dsModel.getContents(); + ReplaceEdit edit = new ReplaceEdit(0, document.getLength(), text); + return new TextEdit[] { edit }; + } + + public String getReadableName(TextEdit edit) { + return null; + } + }); + + try { + processComponent(dsModel, type, typeBinding, annotation, annotationBinding, params, name, implClass, problems); + + TextEdit[] edits = dsModel.getLastTextChangeListener().getTextOperations(); + if (edits.length > 0) { + if (debug.isDebugging()) + debug.trace(String.format("Saving model: %s", file.getFullPath())); //$NON-NLS-1$ + + final MultiTextEdit edit = new MultiTextEdit(); + edit.addChildren(edits); + + if (buffer.isSynchronizationContextRequested()) { + final IDocument doc = document; + final CoreException[] ex = new CoreException[1]; + final CountDownLatch latch = new CountDownLatch(1); + bufferManager.execute(new Runnable() { + public void run() { + try { + performEdit(doc, edit); + } catch (CoreException e) { + ex[0] = e; + } + + latch.countDown(); + } + }); + + try { + latch.await(); + } catch (InterruptedException e) { + if (debug.isDebugging()) + debug.trace("Interrupted while waiting for edits to complete on display thread.", e); //$NON-NLS-1$ + } + + if (ex[0] != null) + throw ex[0]; + } else { + performEdit(document, edit); + } + + buffer.commit(null, true); + } + } finally { + dsModel.dispose(); + bufferManager.disconnect(buffer.getLocation(), LocationKind.IFILE, null); + } + } + + private void performEdit(IDocument document, TextEdit edit) throws CoreException { + DocumentRewriteSession session = null; + try { + if (document instanceof IDocumentExtension4) { + session = ((IDocumentExtension4) document).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED); + } + + LinkedModeModel.closeAllModels(document); + edit.apply(document); + } catch (MalformedTreeException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error applying changes to component model.", e)); //$NON-NLS-1$ + } catch (BadLocationException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Error applying changes to component model.", e)); //$NON-NLS-1$ + } finally { + if (session != null) { + ((IDocumentExtension4) document).stopRewriteSession(session); + } + } + } + + private void processComponent(IDSModel model, TypeDeclaration type, ITypeBinding typeBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map params, String name, String implClass, Collection problems) { + // The required version of the DS specification. Defaults to 1, meaning: v1.1.0 + // The value will be updated in case necessary. + int requiredVersion = 1; + + Object value; + Collection services; + if ((value = params.get("service")) instanceof Object[]) { //$NON-NLS-1$ + Object[] elements = (Object[]) value; + services = new LinkedHashSet(elements.length); + Map serviceDuplicates = errorLevel.isNone() ? null : new HashMap(); + for (int i = 0; i < elements.length; ++i) { + ITypeBinding serviceType = (ITypeBinding) elements[i]; + String serviceName = serviceType.getBinaryName(); + if (!errorLevel.isNone()) { + if (serviceDuplicates.containsKey(serviceName)) { + reportProblem(annotation, "service", i, problems, Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$ + Integer pos = serviceDuplicates.put(serviceName, null); + if (pos != null) + reportProblem(annotation, "service", pos.intValue(), problems, Messages.AnnotationProcessor_duplicateServiceDeclaration, serviceName); //$NON-NLS-1$ + } else { + serviceDuplicates.put(serviceName, i); + } + } + + services.add(serviceName); + validateComponentService(annotation, typeBinding, serviceType, i, problems); + } + } else { + ITypeBinding[] serviceTypes = typeBinding.getInterfaces(); + services = new ArrayList(serviceTypes.length); + for (int i = 0; i < serviceTypes.length; ++i) { + services.add(serviceTypes[i].getBinaryName()); + } + } + + String factory = null; + if ((value = params.get("factory")) instanceof String) { //$NON-NLS-1$ + factory = (String) value; + validateComponentFactory(annotation, factory, problems); + } + + Boolean serviceFactory = null; + if ((value = params.get("servicefactory")) instanceof Boolean) { //$NON-NLS-1$ + serviceFactory = (Boolean) value; + } + + Boolean enabled = null; + if ((value = params.get("enabled")) instanceof Boolean) { //$NON-NLS-1$ + enabled = (Boolean) value; + } + + Boolean immediate = null; + if ((value = params.get("immediate")) instanceof Boolean) { //$NON-NLS-1$ + immediate = (Boolean) value; + } + + String[] properties; + if ((value = params.get("property")) instanceof Object[]) { //$NON-NLS-1$ + Object[] elements = (Object[]) value; + ArrayList list = new ArrayList(elements.length); + for (int i = 0; i < elements.length; ++i) { + if (elements[i] instanceof String) + list.add((String) elements[i]); + } + + properties = list.toArray(new String[list.size()]); + } else { + properties = new String[0]; + } + + String[] propertyFiles; + if ((value = params.get("properties")) instanceof Object[]) { //$NON-NLS-1$ + Object[] elements = (Object[]) value; + ArrayList list = new ArrayList(elements.length); + for (int i = 0; i < elements.length; ++i) { + if (elements[i] instanceof String) + list.add((String) elements[i]); + } + + propertyFiles = list.toArray(new String[list.size()]); + validateComponentPropertyFiles(annotation, ((IType) typeBinding.getJavaElement()).getJavaProject().getProject(), propertyFiles, problems); + } else { + propertyFiles = new String[0]; + } + + String configPolicy = null; + if ((value = params.get("configurationPolicy")) instanceof IVariableBinding) { //$NON-NLS-1$ + IVariableBinding configPolicyBinding = (IVariableBinding) value; + ConfigurationPolicy configPolicyLiteral = ConfigurationPolicy.valueOf(configPolicyBinding.getName()); + if (configPolicyLiteral != null) + configPolicy = configPolicyLiteral.toString(); + } + + String configPid = null; + if ((value = params.get("configurationPid")) instanceof String || value instanceof Object[]) { //$NON-NLS-1$ + Object[] pids; + if (value instanceof String) { + pids = new Object[]{value}; + } + else { + pids = (Object[]) value; + } + StringBuffer configurations = new StringBuffer(); + for (Object pid : pids) { + validateComponentConfigPID(annotation, pid.toString(), problems); + configurations.append(pid.toString()).append(" "); + } + configPid = configurations.toString().trim(); + if (pids.length > 1) { + requiredVersion = Math.max(requiredVersion, 3); + } + } + + ServiceScope scope = null; + if ((value = params.get("scope")) instanceof IVariableBinding) { + IVariableBinding scopeBinding = (IVariableBinding) value; + scope = ServiceScope.valueOf(scopeBinding.getName()); + } + + IDSComponent component = model.getDSComponent(); + + if (enabled == null) { + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ENABLED, IDSConstants.VALUE_TRUE); + } else { + component.setEnabled(enabled.booleanValue()); + } + + if (name == null) { + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_NAME, null); + } else { + component.setAttributeName(name); + } + + if (factory == null) { + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_FACTORY, null); + } else { + component.setFactory(factory); + } + + if (immediate == null) { + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_IMMEDIATE, null); + } else { + component.setImmediate(immediate.booleanValue()); + } + + if (configPolicy == null) { + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_CONFIGURATION_POLICY, IDSConstants.VALUE_CONFIGURATION_POLICY_OPTIONAL); + } else { + component.setConfigurationPolicy(configPolicy); + } + + IDSDocumentFactory dsFactory = model.getFactory(); + + String activate = null; + Annotation activateAnnotation = null; + String deactivate = null; + Annotation deactivateAnnotation = null; + String modified = null; + Annotation modifiedAnnotation = null; + + ArrayList references = new ArrayList(); + HashMap referenceNames = new HashMap(); + IDSReference[] refElements = component.getReferences(); + + HashMap refMap = new HashMap(refElements.length); + for (IDSReference refElement : refElements) { + refMap.put(refElement.getName(), refElement); + } + + // Process the field declarations to get the field injection points. + if (processFieldReferences(type, false, refMap, dsFactory, references, referenceNames, problems).size() > 0) { + requiredVersion = Math.max(requiredVersion, 3); + } + + Map defaultValues = new HashMap(); + for (MethodDeclaration method : type.getMethods()) { + for (Object modifier : method.modifiers()) { + if (!(modifier instanceof Annotation)) + continue; + + Annotation methodAnnotation = (Annotation) modifier; + IAnnotationBinding methodAnnotationBinding = methodAnnotation.resolveAnnotationBinding(); + if (methodAnnotationBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for annotation: %s", methodAnnotation)); //$NON-NLS-1$ + + continue; + } + + String annotationName = methodAnnotationBinding.getAnnotationType().getQualifiedName(); + + if (ACTIVATE_ANNOTATION.equals(annotationName)) { + if (activate == null) { + activate = method.getName().getIdentifier(); + activateAnnotation = methodAnnotation; + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "activate", method, + dsFactory, defaultValues, problems)); //$NON-NLS-1$ + } else if (!errorLevel.isNone()) { + reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, method.getName().getIdentifier()); + if (activateAnnotation != null) { + reportProblem(activateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateActivateMethod, activate); + activateAnnotation = null; + } + } + + continue; + } + + if (DEACTIVATE_ANNOTATION.equals(annotationName)) { + if (deactivate == null) { + deactivate = method.getName().getIdentifier(); + deactivateAnnotation = methodAnnotation; + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "deactivate", method, + dsFactory, defaultValues, problems)); //$NON-NLS-1$ + } else if (!errorLevel.isNone()) { + reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, method.getName().getIdentifier()); + if (deactivateAnnotation != null) { + reportProblem(deactivateAnnotation, null, problems, Messages.AnnotationProcessor_duplicateDeactivateMethod, deactivate); + deactivateAnnotation = null; + } + } + + continue; + } + + if (MODIFIED_ANNOTATION.equals(annotationName)) { + if (modified == null) { + modified = method.getName().getIdentifier(); + modifiedAnnotation = methodAnnotation; + requiredVersion = Math.max(requiredVersion, validateLifeCycleMethod(methodAnnotation, "modified", method, + dsFactory, defaultValues, problems)); //$NON-NLS-1$ + } else if (!errorLevel.isNone()) { + reportProblem(methodAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, method.getName().getIdentifier()); + if (modifiedAnnotation != null) { + reportProblem(modifiedAnnotation, null, problems, Messages.AnnotationProcessor_duplicateModifiedMethod, modified); + modifiedAnnotation = null; + } + } + + continue; + } + + if (REFERENCE_ANNOTATION.equals(annotationName)) { + IMethodBinding methodBinding = method.resolveBinding(); + if (methodBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ + } else { + requiredVersion = Math.max(requiredVersion, processReference(method, methodBinding, methodAnnotation, methodAnnotationBinding, refMap, dsFactory, references, referenceNames, problems)); + } + + continue; + } + } + } + + if (activate == null) { + // only remove activate="activate" if method not found + if (!"activate".equals(component.getActivateMethod()) //$NON-NLS-1$ + || !hasLifeCycleMethod(typeBinding, "activate")) //$NON-NLS-1$ + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_ACTIVATE, null); //$NON-NLS-1$ + } else { + component.setActivateMethod(activate); + } + + if (deactivate == null) { + // only remove deactivate="deactivate" if method not found + if (!"deactivate".equals(component.getDeactivateMethod()) //$NON-NLS-1$ + || !hasLifeCycleMethod(typeBinding, "deactivate")) //$NON-NLS-1$ + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_DEACTIVATE, null); //$NON-NLS-1$ + } else { + component.setDeactivateMethod(deactivate); + } + + if (modified == null) { + removeAttribute(component, IDSConstants.ATTRIBUTE_COMPONENT_MODIFIED, null); + } else { + component.setModifiedeMethod(modified); + } + + IDSProperty[] propElements = component.getPropertyElements(); + // If we don't have any properties, just remove them from the XML. + if (properties.length == 0 && defaultValues.size() == 0) { + removeChildren(component, Arrays.asList(propElements)); + } else { + // build up new property elements. This are the property + // elements present in the @Component annotation. + LinkedHashMap map = new LinkedHashMap(properties.length); + for (int i = 0; i < properties.length; ++i) { + String propertyStr = properties[i]; + String[] pair = propertyStr.split("=", 2); //$NON-NLS-1$ + int colon = pair[0].indexOf(':'); + String propertyName, propertyType; + if (colon == -1) { + propertyName = pair[0]; + propertyType = null; + } else { + propertyName = pair[0].substring(0, colon); + propertyType = pair[0].substring(colon + 1); + } + + String propertyValue = pair.length > 1 ? pair[1].trim() : null; + + IDSProperty property = map.get(propertyName); + if (property == null) { + // create a new property + property = dsFactory.createProperty(); + map.put(propertyName, property); + property.setPropertyName(propertyName); + if (propertyType == null) + removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_TYPE, null); // just remove the attribute completely so we can detect changes when reconciling + else + property.setPropertyType(propertyType); + + property.setPropertyValue(propertyValue); + validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i, problems); + } else { + // property is multi-valued + String content = property.getPropertyElemBody(); + if (content == null) { + content = property.getPropertyValue(); + property.setPropertyElemBody(content); + property.setPropertyValue(null); + } + + if (!errorLevel.isNone()) { + String expected = property.getPropertyType() == null || property.getPropertyType().length() == 0 || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType()) ? Messages.AnnotationProcessor_stringOrEmpty : property.getPropertyType(); + String actual = propertyType == null || IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(propertyType) ? Messages.AnnotationProcessor_stringOrEmpty : propertyType; + if (!actual.equals(expected)) + reportProblem(annotation, "property", i, problems, NLS.bind(Messages.AnnotationProcessor_inconsistentComponentPropertyType, actual, expected), actual); //$NON-NLS-1$ + else + validateComponentProperty(annotation, propertyName, propertyType, propertyValue, i, problems); + } + + if (propertyValue != null) + property.setPropertyElemBody(content + "\n" + pair[1]); //$NON-NLS-1$ + } + } + + // reconcile against existing property elements + HashMap propMap = new HashMap(); + for (IDSProperty propElement : propElements) { + propMap.put(propElement.getPropertyName(), propElement); + } + + // We now merge the default values from the configuration type properties with + // the values found in the component annotations. The latter overwrite the first. + Map actualProperties = new HashMap(); + // Load the defaults. + actualProperties.putAll(defaultValues); + // Overwrite/add the properties from the annotation. + actualProperties.putAll(map); + ArrayList propList = new ArrayList(actualProperties.values()); + for (ListIterator i = propList.listIterator(); i.hasNext();) { + IDSProperty newProperty = i.next(); + IDSProperty property = propMap.remove(newProperty.getPropertyName()); + if (property == null) + continue; + + i.set(property); + + String newPropertyType = newProperty.getPropertyType(); + if (newPropertyType != null || !IDSConstants.VALUE_PROPERTY_TYPE_STRING.equals(property.getPropertyType())) + property.setPropertyType(newPropertyType); + + String newContent = newProperty.getPropertyElemBody(); + // Somehow, even if we set null to the body, it still can contain an empty string. Therefore, check + // on an empty string as well. + if (newContent == null || newContent.length() == 0) { + property.setPropertyValue(newProperty.getPropertyValue()); + IDocumentTextNode textNode = property.getTextNode(); + if (textNode != null) { + property.removeTextNode(); + if (property.isInTheModel() && property.isEditable()) { + model.fireModelChanged(new ModelChangedEvent(model, IModelChangedEvent.REMOVE, new Object[] { textNode }, null)); + } + } + } else { + removeAttribute(property, IDSConstants.ATTRIBUTE_PROPERTY_VALUE, null); + String content = property.getPropertyElemBody(); + if (content == null || !newContent.equals(normalizePropertyElemBody(content))) { + property.setPropertyElemBody(newContent); + } + } + } + + int firstPos = propElements.length == 0 + ? 0 // insert first property element as first child of component + : component.indexOf(propElements[0]); + removeChildren(component, propMap.values()); + + addOrMoveChildren(component, propList, firstPos); + } + + IDSProperties[] propFileElements = component.getPropertiesElements(); + if (propertyFiles.length == 0) { + removeChildren(component, Arrays.asList(propFileElements)); + } else { + HashMap propFileMap = new HashMap(propFileElements.length); + for (IDSProperties propFileElement : propFileElements) { + propFileMap.put(propFileElement.getEntry(), propFileElement); + } + + ArrayList propFileList = new ArrayList(propertyFiles.length); + for (String propertyFile : propertyFiles) { + IDSProperties propertiesElement = propFileMap.remove(propertyFile); + if (propertiesElement == null) { + propertiesElement = dsFactory.createProperties(); + propertiesElement.setInTheModel(false); // note: workaround for PDE bug + propertiesElement.setEntry(propertyFile); + } + + propFileList.add(propertiesElement); + } + + int firstPos; + if (propFileElements.length == 0) { + // insert first properties element after last property or (if none) first child of component + propElements = component.getPropertyElements(); + firstPos = propElements.length == 0 ? 0 : component.indexOf(propElements[propElements.length - 1]) + 1; + } else { + firstPos = component.indexOf(propFileElements[0]); + } + + removeChildren(component, propFileMap.values()); + + addOrMoveChildren(component, propFileList, firstPos); + } + + IDSService service = component.getService(); + if (services.isEmpty()) { + if (service != null) + component.removeService(service); + } else { + if (service == null) { + service = dsFactory.createService(); + + // insert service element after last property or properties element + int firstPos = Math.max(0, indexOfLastPropertyOrProperties(component)); + component.addChildNode(service, firstPos, true); + } + + IDSProvide[] provides = service.getProvidedServices(); + HashMap provideMap = new HashMap(provides.length); + for (IDSProvide provide : provides) { + provideMap.put(provide.getInterface(), provide); + } + + ArrayList provideList = new ArrayList(services.size()); + for (String serviceName : services) { + IDSProvide provide = provideMap.remove(serviceName); + if (provide == null) { + provide = dsFactory.createProvide(); + provide.setInterface(serviceName); + } + + provideList.add(provide); + } + + int firstPos = provides.length == 0 ? -1 : service.indexOf(provides[0]); + removeChildren(service, (provideMap.values())); + + addOrMoveChildren(service, provideList, firstPos); + + if (serviceFactory == null) { + removeAttribute(service, IDSConstants.ATTRIBUTE_SERVICE_FACTORY, IDSConstants.VALUE_FALSE); + } else { + service.setServiceFactory(serviceFactory.booleanValue()); + } + if (scope == null) { + removeAttribute(service, "scope", null); + } + else { + service.setXMLAttribute("scope", scope.toString()); + } + } + + + if (configPid == null) { + removeAttribute(component, ATTRIBUTE_COMPONENT_CONFIGURATION_PID, null); + } else { + component.setXMLAttribute(ATTRIBUTE_COMPONENT_CONFIGURATION_PID, configPid); + requiredVersion = Math.max(2, requiredVersion); + } + + if (references.isEmpty()) { + removeChildren(component, Arrays.asList(refElements)); + } else { + // references must be declared in ascending lexicographical order of their names + Collections.sort(references, REF_NAME_COMPARATOR); + + int firstPos; + if (refElements.length == 0) { + // insert first reference element after service element, or (if not present) last property or properties + service = component.getService(); + if (service == null) { + firstPos = Math.max(0, indexOfLastPropertyOrProperties(component)); + } else { + firstPos = component.indexOf(service) + 1; + } + } else { + firstPos = component.indexOf(refElements[0]); + } + + removeChildren(component, refMap.values()); + + addOrMoveChildren(component, references, firstPos); + } + + IDSImplementation impl = component.getImplementation(); + if (impl == null) { + impl = dsFactory.createImplementation(); + component.setImplementation(impl); + } + + impl.setClassName(implClass); + + + String neededXmlns = NAMESPACE_1_1; + if (requiredVersion > 2) { + neededXmlns = NAMESPACE_1_3; + } + else if (requiredVersion > 1) { + neededXmlns = NAMESPACE_1_2; + } + String xmlns = neededXmlns; + if ((value = params.get("xmlns")) instanceof String) { //$NON-NLS-1$ + xmlns = (String) value; + validateComponentXMLNS(annotation, xmlns, neededXmlns, problems); + } else { + xmlns = neededXmlns; + } + + component.setNamespace(xmlns); // Note: updates to the namespace do not work if no other changes! + } + + private void removeChildren(IDSObject parent, Collection children) { + for (IDocumentElementNode child : children) { + parent.removeChildNode(child, true); + } + } + + private void removeAttribute(IDSObject obj, String name, String defaultValue) { + IDocumentAttributeNode attrNode = obj.getDocumentAttribute(name); + if (attrNode != null) { + // only remove if value is not default + String value = attrNode.getAttributeValue(); + if (value != null && value.equals(defaultValue)) + return; + + obj.removeDocumentAttribute(attrNode); + if (obj.isInTheModel() && obj.isEditable()) { + obj.getModel().fireModelChanged(new ModelChangedEvent(obj.getModel(), ModelChangedEvent.REMOVE, new Object[] { attrNode }, null)); + } + } + } + + private void addOrMoveChildren(IDSObject parent, List children, int firstPos) { + for (int i = 0, n = children.size(); i < n; ++i) { + IDSObject child = children.get(i); + if (child.isInTheModel()) { + int pos = parent.indexOf(child); + if (i == 0) { + if (firstPos < pos) { + // move to first place + moveChildNode(parent, child, firstPos - pos, true); + } + } else { + int prevPos = parent.indexOf(children.get(i - 1)); + if (prevPos > pos) { + // move to previous sibling's position + moveChildNode(parent, child, prevPos - pos, true); + } + } + } else { + if (i == 0) { + if (firstPos == -1) { + parent.addChildNode(child, true); + } else { + // insert into first place + parent.addChildNode(child, firstPos, true); + } + } else { + // insert after preceding sibling + parent.addChildNode(child, parent.indexOf(children.get(i - 1)) + 1, true); + } + } + } + } + + private void moveChildNode(IDocumentObject obj, IDocumentElementNode node, int newRelativeIndex, boolean fireEvent) { + if (newRelativeIndex == 1 || newRelativeIndex == -1) { + obj.moveChildNode(node, newRelativeIndex, fireEvent); + return; + } + + // workaround for PDE's busted DocumentObject.clone() method + int currentIndex = obj.indexOf(node); + if (currentIndex == -1) + return; + + int newIndex = newRelativeIndex + currentIndex; + if (newIndex < 0 || newIndex >= obj.getChildCount()) + return; + + obj.removeChildNode(node, fireEvent); + IDocumentElementNode clone = clone(obj, node); + obj.addChildNode(clone, newIndex, fireEvent); + } + + private IDocumentElementNode clone(IDocumentObject obj, IDocumentElementNode node) { + // note: same exact impl as DocumentObject.clone() + // but here the deserialized object will actually resolve successfully + // because our classloader (with DSPropery visible) will be on top of the stack + // yay for Java serialization, *sigh* + IDocumentElementNode clone = null; + try { + // Serialize + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bout); + out.writeObject(node); + out.flush(); + out.close(); + byte[] bytes = bout.toByteArray(); + // Deserialize + ByteArrayInputStream bin = new ByteArrayInputStream(bytes); + ObjectInputStream in = new ObjectInputStream(bin); + clone = (IDocumentElementNode) in.readObject(); + in.close(); + // Reconnect + clone.reconnect(obj, obj.getSharedModel()); + } catch (IOException e) { + if (debug.isDebugging()) + debug.trace("Error cloning element.", e); //$NON-NLS-1$ + } catch (ClassNotFoundException e) { + if (debug.isDebugging()) + debug.trace("Error cloning element.", e); //$NON-NLS-1$ + } + + return clone; + } + + private int indexOfLastPropertyOrProperties(IDSComponent component) { + int pos = -1; + IDSProperty[] propElements = component.getPropertyElements(); + IDSProperties[] propFileElements = component.getPropertiesElements(); + if (propElements.length > 0) + pos = component.indexOf(propElements[propElements.length - 1]) + 1; + + if (propFileElements.length > 0) { + int lastPos = component.indexOf(propFileElements[propFileElements.length - 1]) + 1; + if (lastPos > pos) + pos = lastPos; + } + + return pos; + } + + private String normalizePropertyElemBody(String content) { + StringBuilder buf = new StringBuilder(content.length()); + BufferedReader reader = new BufferedReader(new StringReader(content)); + try { + String line; + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.length() == 0) + continue; + + if (buf.length() > 0) + buf.append('\n'); + + buf.append(trimmed); + } + } catch (IOException e) { + if (debug.isDebugging()) + debug.trace("Error reading property element body.", e); //$NON-NLS-1$ + } finally { + try { + reader.close(); + } catch (IOException e) { + // ignore + } + } + + return buf.toString(); + } + + private void validateComponentName(Annotation annotation, String name, Collection problems) { + if (!errorLevel.isNone() && !PID_PATTERN.matcher(name).matches()) + reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentName, name), name); //$NON-NLS-1$ + } + + private void validateComponentService(Annotation annotation, ITypeBinding componentType, ITypeBinding serviceType, int index, Collection problems) { + if (!errorLevel.isNone() && !componentType.isAssignmentCompatible(serviceType)) + reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentService, serviceType.getName()), serviceType.getName()); //$NON-NLS-1$ + } + + private void validateComponentFactory(Annotation annotation, String factory, Collection problems) { + if (!errorLevel.isNone() && !PID_PATTERN.matcher(factory).matches()) + reportProblem(annotation, "factory", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentFactoryName, factory), factory); //$NON-NLS-1$ + } + + private void validateComponentProperty(Annotation annotation, String name, String type, String value, int index, Collection problems) { + if (errorLevel.isNone()) + return; + + if (PROPERTY_TYPES.contains(type)) { + if (name == null || name.trim().length() == 0) + reportProblem(annotation, "property", index, problems, Messages.AnnotationProcessor_invalidComponentProperty_nameRequired, name); //$NON-NLS-1$ + + if (value == null) { + reportProblem(annotation, "property", index, problems, Messages.AnnotationProcessor_invalidComponentProperty_valueRequired, name); //$NON-NLS-1$ + } else { + try { + if (IDSConstants.VALUE_PROPERTY_TYPE_LONG.equals(type)) + Long.valueOf(value); + else if (IDSConstants.VALUE_PROPERTY_TYPE_DOUBLE.equals(type)) + Double.valueOf(value); + else if (IDSConstants.VALUE_PROPERTY_TYPE_FLOAT.equals(type)) + Float.valueOf(value); + else if (IDSConstants.VALUE_PROPERTY_TYPE_INTEGER.equals(type) || IDSConstants.VALUE_PROPERTY_TYPE_CHAR.equals(type)) + Integer.valueOf(value); + else if (IDSConstants.VALUE_PROPERTY_TYPE_BYTE.equals(type)) + Byte.valueOf(value); + else if (IDSConstants.VALUE_PROPERTY_TYPE_SHORT.equals(type)) + Short.valueOf(value); + } catch (NumberFormatException e) { + reportProblem(annotation, "property", index, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyValue, type, value), String.valueOf(value)); //$NON-NLS-1$ + } + } + } else { + reportProblem(annotation, "property", index, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyType, type), String.valueOf(type)); //$NON-NLS-1$ + } + } + + private void validateComponentPropertyFiles(Annotation annotation, IProject project, String[] files, Collection problems) { + if (errorLevel.isNone()) + return; + + for (int i = 0; i < files.length; ++i) { + String file = files[i]; + IFile wsFile = PDEProject.getBundleRelativeFile(project, new Path(file)); + if (!wsFile.exists()) + reportProblem(annotation, "properties", i, problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentPropertyFile, file), file); //$NON-NLS-1$ + } + } + + private void validateComponentXMLNS(Annotation annotation, String xmlns, String requiredNs, Collection problems) { + + if (!errorLevel.isNone() && requiredNs.compareTo(xmlns) > 0) + reportProblem(annotation, "xmlns", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentDescriptorNamespace, xmlns), xmlns); //$NON-NLS-1$ + } + + private void validateComponentConfigPID(Annotation annotation, String configPid, Collection problems) { + if (!errorLevel.isNone() && !PID_PATTERN.matcher(configPid).matches()) + reportProblem(annotation, "configurationPid", problems, NLS.bind(Messages.AnnotationProcessor_invalidComponentConfigurationPid, configPid), configPid); //$NON-NLS-1$ + } + + private static String getValueOfDefault(Object object) { + if (object instanceof IVariableBinding) { + IVariableBinding binding = (IVariableBinding) object; + if (binding.isEnumConstant()) { + return binding.getName(); + } + } + return object.toString(); + } + + private int validateLifeCycleMethod(Annotation annotation, String methodName, MethodDeclaration method, + IDSDocumentFactory factory, Map defaultValues, + Collection problems) { + IMethodBinding methodBinding = method.resolveBinding(); + if (methodBinding == null) { + if (debug.isDebugging()) + debug.trace(String.format("Unable to resolve binding for method: %s", method)); //$NON-NLS-1$ + + return 0; + } + String returnTypeName = methodBinding.getReturnType().getName(); + if (!Void.TYPE.getName().equals(returnTypeName)) + reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodReturnType, methodName, returnTypeName), returnTypeName); + + ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes(); + + if (paramTypeBindings.length == 0) + // no-arg method + return 1; + + int requiredSpecVersion = 1; + // every argument must be either Map, ComponentContext, BundleContext or component property type + boolean hasMap = false; + boolean hasCompCtx = false; + boolean hasBundleCtx = false; + boolean hasInt = false; + boolean hasComponentPropertyType = false; + for (ITypeBinding paramTypeBinding : paramTypeBindings) { + String paramTypeName = paramTypeBinding.getErasure().getQualifiedName(); + boolean isDuplicate = false; + + if (Map.class.getName().equals(paramTypeName)) { + if (hasMap) + isDuplicate = true; + else + hasMap = true; + } else if (ComponentContext.class.getName().equals(paramTypeName)) { + if (hasCompCtx) + isDuplicate = true; + else + hasCompCtx = true; + } else if (BundleContext.class.getName().equals(paramTypeName)) { + if (hasBundleCtx) + isDuplicate = true; + else + hasBundleCtx = true; + } else if (paramTypeBinding.isAnnotation()) { + if (hasComponentPropertyType) { + isDuplicate = true; + } + else { + hasComponentPropertyType = true; + // Check the default values in the property. + for (IMethodBinding m : paramTypeBinding.getDeclaredMethods()) { + // Does this property have a default value? + Object defaultValue = m.getDefaultValue(); + if (defaultValue != null) { + IDSProperty property = factory.createProperty(); + Class type = null; + property.setPropertyName(m.getName()); + // Is it an array? If so, fill the body. + if (defaultValue.getClass().isArray()) { + Object[] values = (Object[]) defaultValue; + StringBuffer v = new StringBuffer(); + for (int i = 0; i < values.length; i++) { + Object thisValue = values[i]; + if (thisValue != null) { + type = thisValue.getClass(); + if (v.length() > 0) { + v.append("\n"); + } + v.append(getValueOfDefault(thisValue)); + } + } + property.setPropertyElemBody(v.toString()); + property.setPropertyValue(null); + } + else { + // Normal, single value + type = defaultValue.getClass(); + property.setPropertyValue(getValueOfDefault(defaultValue)); + property.setPropertyElemBody(null); + } + if (type != null) { + property.setPropertyType(type.getSimpleName()); + if (!PROPERTY_TYPES.contains(property.getPropertyType())) { + property.setPropertyType(null); + } + } + defaultValues.put(property.getPropertyName(), property); + } + } + requiredSpecVersion = 3; + } + } else if ("deactivate".equals(methodName) //$NON-NLS-1$ + && (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) { + if (hasInt) + isDuplicate = true; + else + hasInt = true; + } else { + reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_invalidLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName); + } + + if (isDuplicate) + reportProblem(annotation, methodName, problems, NLS.bind(Messages.AnnotationProcessor_duplicateLifeCycleMethodParameterType, methodName, paramTypeName), paramTypeName); + } + return requiredSpecVersion; + } + + private boolean hasLifeCycleMethod(ITypeBinding componentClass, String methodName) { + for (IMethodBinding methodBinding : componentClass.getDeclaredMethods()) { + if (methodName.equals(methodBinding.getName()) + && Void.TYPE.getName().equals(methodBinding.getReturnType().getName())) { + ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes(); + + // every argument must be either Map, ComponentContext, or BundleContext + boolean hasMap = false; + boolean hasCompCtx = false; + boolean hasBundleCtx = false; + boolean hasInt = false; + boolean isInvalid = false; + for (ITypeBinding paramTypeBinding : paramTypeBindings) { + String paramTypeName = paramTypeBinding.getErasure().getQualifiedName(); + + if (Map.class.getName().equals(paramTypeName)) { + if (hasMap) + isInvalid = true; + else + hasMap = true; + } else if (ComponentContext.class.getName().equals(paramTypeName)) { + if (hasCompCtx) + isInvalid = true; + else + hasCompCtx = true; + } else if (paramTypeBinding.isAnnotation()) { + if (hasCompCtx) { + isInvalid = true; + } + else { + hasCompCtx = true; + } + } else if (BundleContext.class.getName().equals(paramTypeName)) { + if (hasBundleCtx) + isInvalid = true; + else + hasBundleCtx = true; + } else if ("deactivate".equals(methodName) //$NON-NLS-1$ + && (Integer.class.getName().equals(paramTypeName) || Integer.TYPE.getName().equals(paramTypeName))) { + if (hasInt) + isInvalid = true; + else + hasInt = true; + } else { + isInvalid = true; + } + + if (isInvalid) + break; + } + + if (!isInvalid) + return true; + } + } + + return false; + } + + private static Map mapAnnotationBinding(IAnnotationBinding annotationBinding) { + HashMap params = new HashMap(); + for (IMemberValuePairBinding pair : annotationBinding.getDeclaredMemberValuePairs()) { + params.put(pair.getName(), pair.getValue()); + } + return params; + } + + /* + * Process the field references present for a type declaration. The fields are checked and if + * a reference annotation is found, it is passed down the reference annotation processing. + */ + Collection processFieldReferences(TypeDeclaration type, boolean checkAccessible, final Map refMap, + final IDSDocumentFactory factory, final Collection references, + final Map names, final Collection problems) { + // List we are going to return to our parent to show that we actually found references. + final List found = new ArrayList(); + // Loop through the fields. + for (FieldDeclaration field : type.getFields()) { + // If we need to check the protect/public modifiers, do it. This is enabled for superclass parsing. + if (checkAccessible && (field.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) continue; + for (Object modifier : field.modifiers()) { + if (!(modifier instanceof Annotation)) + continue; + Annotation fieldAnnotation = (Annotation) modifier; + IAnnotationBinding fieldAnnotationBinding = fieldAnnotation.resolveAnnotationBinding(); + if (fieldAnnotationBinding == null) continue; + // Check if it is reference annotation. + String annotationName = fieldAnnotationBinding.getAnnotationType().getQualifiedName(); + if (REFERENCE_ANNOTATION.equals(annotationName)) { + // Process it. + VariableDeclarationFragment fragment = (VariableDeclarationFragment) field.fragments().get(0); + IDSReference goodOne = processReference(field, fragment.getName().getIdentifier(), + fieldAnnotation, fieldAnnotationBinding, refMap, factory, references, names, problems); + // If we found a valid reference, add it to the list to return. + if (goodOne != null) { + found.add(goodOne); + } + } + } + } + return found; + } + + private static ReferenceCardinality _cardinality(Map params) { + ReferenceCardinality cardinalityLiteral = null; + Object value = params.get("cardinality"); + if (value instanceof IVariableBinding) { + IVariableBinding cardinalityBinding = (IVariableBinding) value; + cardinalityLiteral = ReferenceCardinality.valueOf(cardinalityBinding.getName()); + } + else if (value instanceof ReferenceCardinality) { + cardinalityLiteral = (ReferenceCardinality) value; + } + return cardinalityLiteral; + } + + private static String cardinality(Map params) { + ReferenceCardinality cardinalityLiteral = _cardinality(params); + if (cardinalityLiteral != null) + return cardinalityLiteral.toString(); + return null; + } + + private static ReferencePolicy _policy(Map params) { + Object value; + ReferencePolicy policyLiteral = null; + if ((value = params.get("policy")) instanceof IVariableBinding) { //$NON-NLS-1$ + IVariableBinding policyBinding = (IVariableBinding) value; + policyLiteral = ReferencePolicy.valueOf(policyBinding.getName()); + } + return policyLiteral; + } + + private static String policy(Map params) { + ReferencePolicy policyLiteral = _policy(params); + if (policyLiteral == null) return null; + return policyLiteral.toString(); + } + + private static ReferencePolicyOption _referencePolicyOption(Map params) { + Object value; + ReferencePolicyOption policyOptionLiteral = null; + if ((value = params.get("policyOption")) instanceof IVariableBinding) { //$NON-NLS-1$ + IVariableBinding policyOptionBinding = (IVariableBinding) value; + policyOptionLiteral = ReferencePolicyOption.valueOf(policyOptionBinding.getName()); + } + return policyOptionLiteral; + } + + private static String referencePolicyOption(Map params) { + ReferencePolicyOption policyOptionLiteral = _referencePolicyOption(params); + if (policyOptionLiteral != null) { + return policyOptionLiteral.toString(); + } + return null; + } + + private String target(Map params, Annotation annotation, Collection problems) { + String target = null; + Object value; + if ((value = params.get("target")) instanceof String) { //$NON-NLS-1$ + target = (String) value; + validateReferenceTarget(annotation, target, problems); + } + return target; + } + + private static String scope(Map params) { + String scope = null; + Object value; + if ((value = params.get("scope")) instanceof IVariableBinding) { //$NON-NLS-1$ + IVariableBinding scopeBinding = (IVariableBinding) value; + ReferenceScope referenceScope = ReferenceScope.valueOf(scopeBinding.getName()); + if (referenceScope != null && !ReferenceScope.BUNDLE.equals(referenceScope)) { + scope = referenceScope.toString(); + } + } + return scope; + } + + /* + * Common handling of the reference processing. Does the parts that are common to both field and + * method based references and constructs a reference for it. + */ + private IDSReference reference(String defaultName, String service, + Annotation annotation, Map params, Map refMap, IDSDocumentFactory factory, Collection collector, + Map names, Collection problems) { + String name = null; + Object value; + if ((value = params.get("name")) instanceof String) { //$NON-NLS-1$ + name = (String) value; + } + else { + name = defaultName; + } + IDSReference reference = refMap.remove(name); + if (reference == null) { + reference = factory.createReference(); + } + collector.add(reference); + if (!errorLevel.isNone()) { + if (names.containsKey(name)) { + reportProblem(annotation, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ + Annotation duplicate = names.put(name, null); + if (duplicate != null) + reportProblem(duplicate, "name", problems, NLS.bind(Messages.AnnotationProcessor_duplicateReferenceName, name), name); //$NON-NLS-1$ + } else { + names.put(name, annotation); + } + } + + if (name == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_NAME, null); + } else { + reference.setReferenceName(name); + } + + if (service == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_INTERFACE, null); + } else { + reference.setReferenceInterface(service); + } + String cardinality = cardinality(params); + String policy = policy(params); + String target = target(params, annotation, problems); + String policyOption = referencePolicyOption(params); + String scope = scope(params); + if (cardinality == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_CARDINALITY, IDSConstants.VALUE_REFERENCE_CARDINALITY_ONE_ONE); + } else { + reference.setReferenceCardinality(cardinality); + } + + if (policy == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_POLICY, IDSConstants.VALUE_REFERENCE_POLICY_STATIC); + } else { + reference.setReferencePolicy(policy); + } + + if (target == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_TARGET, null); + } else { + reference.setReferenceTarget(target); + } + + if (policyOption == null) { + removeAttribute(reference, ATTRIBUTE_REFERENCE_POLICY_OPTION, VALUE_REFERENCE_POLICY_OPTION_RELUCTANT); + } else { + reference.setXMLAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION, policyOption); + } + + if (scope != null) { + reference.setXMLAttribute("scope", scope); + } + else { + removeAttribute(reference, "scope", null); + } + return reference; + } + + private static ITypeBinding bindingExcludingObject(Type type) { + ITypeBinding b = type.resolveBinding(); + if (b == null || b.getBinaryName().equals("java.lang.Object")) { + b = null; + } + return b; + } + + /* + * Get the contained type for a parameterized type. This to determine the service type from the parameterized + * indications. + */ + private Type containedType(Type type, int index) { + Type contained = null; + if (type.isParameterizedType()) { + ParameterizedType thisType = (ParameterizedType) type; + if (thisType.typeArguments().size() > index) { + contained = (Type) thisType.typeArguments().get(index); + if (bindingExcludingObject(contained) == null) { + contained = null; + } + } + } + return contained; + } + + /* + * Parse the field collection type for a specific field type. All according to SCR 1.3, section 112.3.3 + * Returns both the service type (as return value) and the field collection type in the passed string buffer. + */ + private ITypeBinding getFieldCollectionType(Type type, StringBuffer fct) { + if (type == null) return null; + ITypeBinding binding = bindingExcludingObject(type); + if (binding == null) return null; + String containedName = binding.getBinaryName(); + Type serviceType = null; + String fieldCollectionType = null; + if (Map.class.getName().equals(containedName)) { + fieldCollectionType = "properties"; + } + else if (ServiceReference.class.getName().equals(containedName)) { + fieldCollectionType = "reference"; + serviceType = containedType(type, 0); + } + else if (Map.Entry.class.getName().equals(containedName)) { + fieldCollectionType = "tuple"; + serviceType = containedType(type, 1); + } + else if ("org.osgi.service.component.ComponentServiceObjects".equals(containedName)) { + fieldCollectionType = "serviceobjects"; + serviceType = containedType(type, 0); + } + else { + serviceType = type; + } + if (fieldCollectionType != null && fct != null) { + fct.append(fieldCollectionType); + } + return (serviceType == null) ? null : bindingExcludingObject(serviceType); + } + + /* + * Process a field declaration with a reference annotation. As indicated in the SCR specification v1.3, + * fields can be either collection types (for multiple references) or single object types. Both types + * are handled here, generating warnings and errors on the way. + */ + private IDSReference processReference(FieldDeclaration field, String fieldName, + Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, + IDSDocumentFactory factory, Collection collector, Map names, + Collection problems) { + // Map the annotation properties. + Map params = mapAnnotationBinding(annotationBinding); + // Check the service type of the field. Can be either a map, collection, entry or any other object type. + Type type = field.getType(); + ITypeBinding binding = type.resolveBinding(); + if (binding == null) return null; + // Now the processing of the type of the field. There are a couple of options, but + // the first discrimination is made between collections and "normal" objects. + ITypeBinding defaultService = null; + IType itype = (IType) binding.getJavaElement(); + // Try to determine whether the field is a collection. + boolean isCollection = false; + try { + ITypeHierarchy typeHierarchy = itype.newTypeHierarchy(new NullProgressMonitor()); + for (IType t : typeHierarchy.getAllInterfaces()) { + if (t.getFullyQualifiedName().equals(Collection.class.getName())) { + isCollection = true; + } + } + } catch (Exception exc) { + exc.printStackTrace(); + return null; + } + StringBuffer fieldCollectionType = new StringBuffer(); + ReferenceCardinality cardinality = _cardinality(params); + if (isCollection) { + // If the cardinality specifies a 1..1 relation, we should generate a warning (the SCR handler + // probably will fail anyway). If no cardinality is specified, we assume at least one (1..n) since + // we are processing a collection. AFAIK not in the specifications, but seems logical. + if (ReferenceCardinality.MANDATORY.equals(cardinality)) { + reportProblem(annotation, "cardinality", ValidationErrorLevel.warning, problems, + NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); + } + else if (cardinality == null) { + params.put("cardinality", ReferenceCardinality.AT_LEAST_ONE); + } + // Since it is a collection, we can check the type of the collection from the parameterized value (if present). + // This determines the collection type. + defaultService = getFieldCollectionType(containedType(type, 0), fieldCollectionType); + } + else { + // No collection. If it is not one of the known types, like service reference, etc., + // just get the type of the field itself. + defaultService = getFieldCollectionType(type, null); + // We cannot have a multiple relation here. + if (EnumSet.of(ReferenceCardinality.AT_LEAST_ONE, ReferenceCardinality.MULTIPLE).contains(cardinality)) { + reportProblem(annotation, "cardinality", ValidationErrorLevel.error, problems, + NLS.bind(Messages.AnnotationProcessor_cardinalityMismatch, cardinality.toString())); + } + } + // Dynamic fields should be marked volatile. + if (ReferencePolicy.DYNAMIC.equals(_policy(params)) && (field.getModifiers() & Modifier.VOLATILE) == 0) { + reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_dynamicShouldBeVolatile); + } + // Impossible to have a final modifier since it cannot be replaced then. + if ((field.getModifiers() & Modifier.FINAL) != 0) { + reportProblem(annotation, "policy", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_referenceAndFinal); + } + // Check the service specification. If the service is specified, this is the easiest solution, since the programmer + // specifies what the service is. If this one is unavailable and we could not determine the service + // type above, report and issue. + Object s = params.get("service"); + IDSReference reference = null; + if (s == null && defaultService == null) { + reportProblem(annotation, "service", problems, Messages.AnnotationProcessor_unknownServiceType); + } + else { + // OK, valid. Check whether the default service type we determined matches the service + // specification. + String serviceName; + if (s == null || !(s instanceof ITypeBinding)) { + serviceName = defaultService.getBinaryName(); + } + else { + ITypeBinding serviceType = (ITypeBinding) s; + // Check the type compatibility and report a problem if not. + if (defaultService != null && !defaultService.isAssignmentCompatible(serviceType)) { + reportProblem(annotation, "service", problems, + NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, + defaultService.getName(), serviceType.getName())); + } + serviceName = serviceType.getBinaryName(); + } + // Create the service reference. + reference = reference(fieldName, serviceName, + annotation, params, refMap, factory, collector, names, problems); + // Add some attributes because of field injection. Note that these attributes + // are not known by the IDS stuff in the current version, and are therefore created by hand + // in stead of convenience methods. + reference.setXMLAttribute("field", fieldName); + if (fieldCollectionType.length() > 0) { + reference.setXMLAttribute("field-collection-type", fieldCollectionType.toString()); + } + else { + removeAttribute(reference, "field-collection-type", null); + } + // Field option: do we want the field replaced or updated? Only works for collections. + FieldOption fieldOption = null; + Object value = params.get("fieldOption"); + if (value instanceof IVariableBinding) { + IVariableBinding scopeBinding = (IVariableBinding) value; + fieldOption = FieldOption.valueOf(scopeBinding.getName()); + } + if (fieldOption != null && FieldOption.UPDATE.equals(fieldOption)) { + if (!isCollection) { + reportProblem(annotation, "fieldOption", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_updateOnlyForCollections); + } + ReferencePolicy policy = _policy(params); + if (!ReferencePolicy.DYNAMIC.equals(policy)) { + reportProblem(annotation, "fieldOption", ValidationErrorLevel.error, problems, + Messages.AnnotationProcessor_updateOnlyForDynamic); + } + reference.setXMLAttribute("field-option", fieldOption.toString()); + } + else { + removeAttribute(reference, "field-option", null); + } + } + return reference; + } + + private int processReference(MethodDeclaration method, IMethodBinding methodBinding, Annotation annotation, IAnnotationBinding annotationBinding, Map refMap, IDSDocumentFactory factory, Collection collector, Map names, Collection problems) { + Map params = mapAnnotationBinding(annotationBinding); + + ITypeBinding[] argTypes = methodBinding.getParameterTypes(); + + ITypeBinding serviceType; + Object value; + if ((value = params.get("service")) instanceof ITypeBinding) { //$NON-NLS-1$ + serviceType = (ITypeBinding) value; + if (!errorLevel.isNone() && argTypes.length > 0) { + ITypeBinding[] typeArgs; + if (!(ServiceReference.class.getName().equals(argTypes[0].getErasure().getQualifiedName()) + && ((typeArgs = argTypes[0].getTypeArguments()).length == 0 || serviceType.isAssignmentCompatible(typeArgs[0]))) + && !serviceType.isAssignmentCompatible(argTypes[0])) + reportProblem(annotation, "service", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceService, argTypes[0].getName(), serviceType.getName()), serviceType.getName()); //$NON-NLS-1$ + } + } else if (argTypes.length > 0) { + if (ServiceReference.class.getName().equals(argTypes[0].getErasure().getQualifiedName())) { + ITypeBinding[] typeArgs = argTypes[0].getTypeArguments(); + if (typeArgs.length > 0) + serviceType = typeArgs[0]; + else + serviceType = null; + } else { + serviceType = argTypes[0].isPrimitive() ? getObjectType(method.getAST(), argTypes[0]) : argTypes[0]; + } + } else { + serviceType = null; + } + + if (serviceType == null) { + reportProblem(annotation, null, problems, Messages.AnnotationProcessor_invalidReferenceServiceUnknown); + + serviceType = method.getAST().resolveWellKnownType(Object.class.getName()); + } + + validateReferenceBindMethod(annotation, serviceType, methodBinding, problems); + + String service = serviceType == null ? null : serviceType.getBinaryName(); + + String methodName = methodBinding.getName(); + String name; + if (methodName.startsWith("bind")) { //$NON-NLS-1$ + name = methodName.substring("bind".length()); //$NON-NLS-1$ + } else if (methodName.startsWith("set")) { //$NON-NLS-1$ + name = methodName.substring("set".length()); //$NON-NLS-1$ + } else if (methodName.startsWith("add")) { //$NON-NLS-1$ + name = methodName.substring("add".length()); //$NON-NLS-1$ + } else { + name = methodName; + } + + String unbind; + if ((value = params.get("unbind")) instanceof String) { //$NON-NLS-1$ + String unbindValue = (String) value; + if ("-".equals(unbindValue)) { //$NON-NLS-1$ + unbind = null; + } else { + unbind = unbindValue; + if (!errorLevel.isNone()) { + IMethodBinding unbindMethod = findUnbindMethod(methodBinding.getDeclaringClass(), serviceType, unbind, true); + if (unbindMethod == null) + reportProblem(annotation, "unbind", problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceUnbind, unbind), unbind); //$NON-NLS-1$ + } + } + } else { + String unbindCandidate; + if (methodName.startsWith("add")) { //$NON-NLS-1$ + unbindCandidate = "remove" + methodName.substring("add".length()); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + unbindCandidate = "un" + methodName; //$NON-NLS-1$ + } + + IMethodBinding unbindMethod = findUnbindMethod(methodBinding.getDeclaringClass(), serviceType, unbindCandidate, false); + if (unbindMethod == null) { + unbind = null; + reportProblem(annotation, null, missingUnbindMethodLevel, problems, NLS.bind(Messages.AnnotationProcessor_noImplicitReferenceUnbind, unbindCandidate), unbindCandidate); + } else { + unbind = unbindMethod.getName(); + } + } + + String updated; + if ((value = params.get(ATTRIBUTE_REFERENCE_UPDATED)) instanceof String) { //$NON-NLS-1$ + String updatedValue = (String) value; + if ("-".equals(updatedValue)) { //$NON-NLS-1$ + updated = null; + } else { + updated = updatedValue; + if (!errorLevel.isNone()) { + IMethodBinding updatedMethod = findUpdatedMethod(methodBinding.getDeclaringClass(), updated, true); + if (updatedMethod == null) + reportProblem(annotation, ATTRIBUTE_REFERENCE_UPDATED, problems, NLS.bind(Messages.AnnotationProcessor_invalidReferenceUpdated, updated), updated); //$NON-NLS-1$ + } + } + } else { + String updatedCandidate; + if (methodName.startsWith("bind")) { //$NON-NLS-1$ + updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("bind".length()); //$NON-NLS-1$ //$NON-NLS-2$ + } else if (methodName.startsWith("set")) { //$NON-NLS-1$ + updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("set".length()); //$NON-NLS-1$ //$NON-NLS-2$ + } else if (methodName.startsWith("add")) { //$NON-NLS-1$ + updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName.substring("add".length()); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + updatedCandidate = ATTRIBUTE_REFERENCE_UPDATED + methodName; //$NON-NLS-1$ + } + + IMethodBinding updatedMethod = findUpdatedMethod(methodBinding.getDeclaringClass(), updatedCandidate, false); + if (updatedMethod == null) + updated = null; + else + updated = updatedMethod.getName(); + } + + IDSReference reference = this.reference(name, service, annotation, params, refMap, + factory, collector, names, problems); + + reference.setReferenceBind(methodName); + + if (unbind == null) { + removeAttribute(reference, IDSConstants.ATTRIBUTE_REFERENCE_UNBIND, null); + } else { + reference.setReferenceUnbind(unbind); + } + + if (updated == null) { + removeAttribute(reference, ATTRIBUTE_REFERENCE_UPDATED, null); + } else { + reference.setXMLAttribute(ATTRIBUTE_REFERENCE_UPDATED, updated); + } + + if (scope(params) != null) { + return 3; + } + return (reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_POLICY_OPTION) != null + || reference.getDocumentAttribute(ATTRIBUTE_REFERENCE_UPDATED) != null) ? 2 : 1; + } + + private ITypeBinding getObjectType(AST ast, ITypeBinding primitive) { + if (Boolean.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Boolean.class.getName()); + + if (Byte.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Byte.class.getName()); + + if (Character.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Character.class.getName()); + + if (Double.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Double.class.getName()); + + if (Float.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Float.class.getName()); + + if (Integer.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Integer.class.getName()); + + if (Long.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Long.class.getName()); + + if (Short.TYPE.getName().equals(primitive.getName())) + return ast.resolveWellKnownType(Short.class.getName()); + + return null; + } + + private void validateReferenceBindMethod(Annotation annotation, ITypeBinding serviceType, IMethodBinding methodBinding, Collection problems) { + if (errorLevel.isNone()) + return; + + String returnTypeName = methodBinding.getReturnType().getName(); + if (!Void.TYPE.getName().equals(returnTypeName)) + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidBindMethodReturnType, returnTypeName), returnTypeName); + + ITypeBinding[] paramTypeBindings = methodBinding.getParameterTypes(); + if (!(paramTypeBindings.length == 1 && (ServiceReference.class.getName().equals(paramTypeBindings[0].getErasure().getQualifiedName()) || serviceType == null || serviceType.isAssignmentCompatible(paramTypeBindings[0]))) + && !(paramTypeBindings.length == 2 && (serviceType == null || serviceType.isAssignmentCompatible(paramTypeBindings[0])) && Map.class.getName().equals(paramTypeBindings[1].getErasure().getQualifiedName()))) { + String[] params = new String[paramTypeBindings.length]; + StringBuilder buf = new StringBuilder(64); + buf.append('('); + for (int i = 0; i < params.length; ++i) { + params[i] = paramTypeBindings[i].getName(); + if (buf.length() > 1) + buf.append(", "); //$NON-NLS-1$ + + buf.append(params[i]); + } + + buf.append(')'); + reportProblem(annotation, null, problems, NLS.bind(Messages.AnnotationProcessor_invalidBindMethodParameters, buf, serviceType == null ? Messages.AnnotationProcessor_unknownServiceTypeLabel : serviceType.getName()), params); + } + } + + private void validateReferenceTarget(Annotation annotation, String target, Collection problems) { + if (errorLevel.isNone()) + return; + + try { + FrameworkUtil.createFilter(target); + } catch (InvalidSyntaxException e) { + String msg = e.getMessage(); + String suffix = ": " + e.getFilter(); //$NON-NLS-1$ + if (msg.endsWith(suffix)) + msg = msg.substring(0, msg.length() - suffix.length()); + + reportProblem(annotation, "target", problems, msg, target); //$NON-NLS-1$ + } + } + + private IMethodBinding findUnbindMethod(ITypeBinding componentClass, ITypeBinding serviceType, String name, boolean recurse) { + ITypeBinding testedClass = componentClass; + + IMethodBinding candidate = null; + int priority = 0; + // priority: + // 0: , Map + // 1: , Map + // 2: + // 3: + do { + for (IMethodBinding declaredMethod : testedClass.getDeclaredMethods()) { + if (name.equals(declaredMethod.getName()) + && Void.TYPE.getName().equals(declaredMethod.getReturnType().getName()) + && (testedClass == componentClass + || Modifier.isPublic(declaredMethod.getModifiers()) + || Modifier.isProtected(declaredMethod.getModifiers()) + || (!Modifier.isPrivate(declaredMethod.getModifiers()) + && testedClass.getPackage().isEqualTo(componentClass.getPackage())))) { + ITypeBinding[] paramTypes = declaredMethod.getParameterTypes(); + if (paramTypes.length == 1) { + if (ServiceReference.class.getName().equals(paramTypes[0].getErasure().getQualifiedName())) + // we have the winner + return declaredMethod; + + if (priority < 3 && serviceType != null && serviceType.isEqualTo(paramTypes[0])) + priority = 3; + else if (priority < 2 && serviceType != null && serviceType.isAssignmentCompatible(paramTypes[0])) + priority = 2; + else + continue; + + // we have a (better) candidate + candidate = declaredMethod; + } else if (paramTypes.length == 2) { + if (priority < 1 + && serviceType != null && serviceType.isEqualTo(paramTypes[0]) + && Map.class.getName().equals(paramTypes[1].getErasure().getQualifiedName())) + priority = 1; + else if (candidate != null + || !(serviceType != null && serviceType.isAssignmentCompatible(paramTypes[0])) + || !Map.class.getName().equals(paramTypes[1].getErasure().getQualifiedName())) + continue; + + // we have a candidate + candidate = declaredMethod; + } + } + } + } while (recurse && (testedClass = testedClass.getSuperclass()) != null); + + return candidate; + } + + private IMethodBinding findUpdatedMethod(ITypeBinding componentClass, String name, boolean recurse) { + ITypeBinding testedClass = componentClass; + + IMethodBinding candidate = null; + do { + for (IMethodBinding declaredMethod : testedClass.getDeclaredMethods()) { + if (name.equals(declaredMethod.getName()) + && Void.TYPE.getName().equals(declaredMethod.getReturnType().getName()) + && (testedClass == componentClass + || Modifier.isPublic(declaredMethod.getModifiers()) + || Modifier.isProtected(declaredMethod.getModifiers()) + || (!Modifier.isPrivate(declaredMethod.getModifiers()) + && testedClass.getPackage().isEqualTo(componentClass.getPackage())))) { + ITypeBinding[] paramTypes = declaredMethod.getParameterTypes(); + if (paramTypes.length == 1) { + if (ServiceReference.class.getName().equals(paramTypes[0].getErasure().getQualifiedName())) + // we have the winner + return declaredMethod; + + if (candidate == null && Map.class.getName().equals(paramTypes[0].getErasure().getQualifiedName())) { + // we have a candidate + candidate = declaredMethod; + } + } + } + } + } while (recurse && (testedClass = testedClass.getSuperclass()) != null); + + return candidate; + } + + private void reportProblem(Annotation annotation, String member, Collection problems, String message, String... args) { + reportProblem(annotation, member, -1, problems, message, args); + } + + private void reportProblem(Annotation annotation, String member, ValidationErrorLevel errorLevel, Collection problems, String message, String... args) { + reportProblem(annotation, member, -1, errorLevel, problems, message, args); + } + + private void reportProblem(Annotation annotation, String member, int valueIndex, Collection problems, String message, String... args) { + reportProblem(annotation, member, valueIndex, errorLevel, problems, message, args); + } + + private void reportProblem(Annotation annotation, String member, int valueIndex, ValidationErrorLevel errorLevel, Collection problems, String message, String... args) { + if (errorLevel.isNone()) + return; + + Expression memberValue = annotation; + if (annotation.isNormalAnnotation() && member != null) { + NormalAnnotation na = (NormalAnnotation) annotation; + for (Object value : na.values()) { + MemberValuePair pair = (MemberValuePair) value; + if (member.equals(pair.getName().getIdentifier())) { + memberValue = pair.getValue(); + break; + } + } + } else if (annotation.isSingleMemberAnnotation()) { + SingleMemberAnnotation sma = (SingleMemberAnnotation) annotation; + memberValue = sma.getValue(); + } + + int start = memberValue.getStartPosition(); + int length = memberValue.getLength(); + + if (valueIndex >= 0 && memberValue instanceof ArrayInitializer) { + ArrayInitializer ai = (ArrayInitializer) memberValue; + if (valueIndex < ai.expressions().size()) { + Expression element = (Expression) ai.expressions().get(valueIndex); + start = element.getStartPosition(); + length = element.getLength(); + } + } + + if (start >= 0) { + DSAnnotationProblem problem = new DSAnnotationProblem((CompilationUnit) annotation.getRoot(), + errorLevel.isError(), message, args); + problem.setSourceStart(start); + problem.setSourceEnd(start + length - 1); + problems.add(problem); + } + + } } \ No newline at end of file