From b88ba518c1980045e8774eb29482d0723dcece1c Mon Sep 17 00:00:00 2001 From: lempinen Date: Mon, 15 Oct 2012 12:31:49 +0000 Subject: [PATCH] Better issues. Added new issue types and remove issue sources from module types. (refs #3017) git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@26014 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.sysdyn.ontology/graph.tg | Bin 68708 -> 70441 bytes .../graph/Validation.pgraph | 50 ++- .../org/simantics/sysdyn/SysdynResource.java | 30 +- .../handlers/imports/ImportModelHandler.java | 40 +- .../widgets/expressions/ExpressionField.java | 65 +-- .../expressions/WithLookupExpression.java | 3 +- .../widgets/functions/FunctionCodeWidget.java | 5 +- .../sysdyn/ui/utils/ArrayVariableUtils.java | 225 ++++++---- .../sysdyn/ui/utils/ExpressionUtils.java | 408 +++++++++++------- .../sysdyn/ui/utils/SyntaxError.java | 223 ++++++++++ .../ui/validation/DependencyFunction.java | 224 ++++++---- .../validation/ExpressionIssueFunction.java | 4 +- .../sysdyn/ui/validation/Functions.java | 10 +- .../ui/validation/IssueWithStringContext.java | 107 +++++ .../sysdyn/ui/validation/References.java | 51 +++ .../sysdyn/ui/validation/ValidationUtils.java | 67 +-- 16 files changed, 1047 insertions(+), 465 deletions(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/SyntaxError.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/IssueWithStringContext.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/References.java diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index 0475c6b5589ce60083775c487e276ca8a6701b57..6e0ebb774c87dc8294e41ef075f0f858092d68f2 100644 GIT binary patch literal 70441 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 V)xeUYSi;xEae8dz$&@Mee*pd^W_ADo literal 68708 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

75&ZIqUl#F8BYsK5H%I*9h+h=( z3nP9(#LtiTc@blc(R}f|A$^XKKg07=(Y`^HU*=h#U!Lh$^T~fJRqQ_sU3`R}9r+_$ ze`M`tHRbA$%$TTG2loQJpOS9_UxeSNdx7Z-^6bZuZ^S;9_iu3WWR%^3YcCJ% z?R7t#ay_R0#jwX1OBY1E&~ex=F9surK_BCS9O}#ZqwU(>bfx1A>uyIMu8(z`qJI2d zI)A56iS)rif3=ebefZ{e@W0Hn&!76#@tU6D<$nAs2Y-l_J}LJn{eKh6mY}|8JKGe@ z?}_u@FQcyTrTCqq{H=~N+&4PiAM0h5W$hQS8TSy3EJa`1JiRc|6J2<50H5=Zp0NhkQX-_)`4V zd_f-a^#?E4{IHF*S6*0T$=72|`29d_f-k(f2>)@g!f5Mw#Y|y5Nv6=nC6>K_2q;doRBj`PTVLdu8U!u;lAeD0?LG z(d~Yx;6nSsVZJe++!LxkZNUY7aL{Kyp&RtQZOEUGS;!akk7Ap@N96G&U)-~5z8DJ} z@&#RCn=i;izJBTDnx6#`Ghg)Q<&v+5nf5Us;6nSsDfZQ+F84cqsL^LWzy*D9(D$|> zKk7sMdEG*OAUld}zW%|;k4G%BzyGAOp1pp8wVr=t6Z}!1tolvQR{z0}D_;irjV8ap zb{zT#b)`JqZ{WMYs2e%l&tg`6+n@Be!>_-ni}INZPHLwq>%x5cNloj-6z}lZf(4PkT`&H>T;Oio;<(%FFa`ID=pFvzd z$g}nr`};typFG2}>X$r!6gv2H+}pACkMRD;p6u;qev;MxG}wcKJ$YYbPqy}CwLjJA z2YYfgvL{=6vf7{G^n*Qll5&6kfooyi4={g}YyMC2toa!SPeAVVC4XcapRDmucKRVc zd2eJ-w)SMTU*q(HJ-Gt;#eTit1->@o2ZFV~zXJK+8UCW@ufiTalCOFG68OHjhHZKc z%5D3|TAzD)x%%gLf;`lZ^7}y_dotN?QU+GJ=u)orzrT}*`3Mp91E=7tQK<2r2EHod zE5WwDWX&h_$!gE~g0(&!WiQwEk*|Qh^+&e;$krb?_`9dKSAXQop|AO%u4UE*toBkj z%c`&WVt+%f`QhK{75J~}}8 zGWgn!c$&}mdwwd$8`mesqFnRE`36@19b~jQw1@W_@{|4eqAqiC8~j~_-yCa}Dc_mf z^Zg)M>&L%MOfP|4>&Nwwto7r4hphFxhhvS$|JOu{dr_zlbjd2Gt;lVDAP@2X1Ucqi zAwJp1m-UqF$6LD2>8pP^6QEq{cOvA773u?-oFQm_JZnCszn~xL^GE1!hrQ1S_vpfQ zyd%#VkFu0`SO1=aJ@Z3-%`fvww(TKndpVvj%=INc*~Y&B?AuG-6ddAv)_8J1q`r+$ zw(-d}{x%~o!S_~U&+$Rl@u6~CKghK{9Vd8w?H}@%Tuxu)kpJI14*lolh5n-c$1vVU z@cR$Y-<;P^+Dq2auJIQTox+p9nF+0efj`Q|wJg=bxVXiK>=>t-2k zE%5mfBi98v`i`viVc!j$~_7ud%O?CTSl^*syq)&60=$u>V^n;)>+cO2{WH9zDt zp>Oj;)_l+wtoDqRjzMnYlWlymjSp7)j$^#N#wU+L-^M3veAa{+++WDnA2|41;qBEQxgYu$oB2R~GS;FyoUX`~SvMNDdD2-$0HrqW7DQ<({98_A@s7hhs?dPx*T2 zd$~LxkZr!nKHqXb2M7CQ-d^)fMvQ_#vh_!{{#Yli&r)x%{>W>gul@J0jzfEY74fgZ z_s-+X{e`UY-|gjg{G68SOZ&;H|1{{6BmGd{C0?%eC7%j?9Z#?=FzO;_;=aACYk^hY zwio_Fdw=2d1OF0y3gT(}#h$f4KjJvlm-3U1{vyv-AM&72c^UdzAND;s#Q!csruxcso=P(S7ma;_EZ)3hhz8zX*r#E*{naS`7HJ_i1OfcSk*M%M8r?;pS^ z=0Wx&_d8wf>drRsY=s=^|0&0X`a@o*zh&0nGV5=d^|#FWTW0;SX6yJ!>yCB5$A9Z4 zU18b>TXLa&;Ltwb*1%q-{iQk3_R0GYzu&VT z&!S8DN8P7`^#iLv;rWnh{a7Ee)`xAfe34_dmuDl$15+P;Sf~$K>qA|#)~Cm@+INsK zx3oXf61XF0>7NX&?d5tvR{zu|+xZI){+Ub4H9i>_GzE`?2f$RQqcOMUfE{tM>k2hg8bJLPXdz^X5GH!QYb?XQ?sU)z)5en$T~-}rCE z6!_o4f5u1adl|0(WcX>g{v$pn|H8h#eBUgr_Rn;z^_TybBJH)k91DLi>(PfJ#`;8g zXdi7Yd;K6+{fpgvl=f0z`902F>mzlAJmlj$P9FHX;NRop=L6UK0y7@uVLkQoz+N8W zGyn8w>+?IX=8O6MZRF1~{ecVhv8;0SkNcVEE3^I35AFLm@Ne+3@qZn~w@m-wLVU|A z*LaLixiaH}L;P=pu_r6U|78^4GW~-K@hz)d<1s$v%8U;V@xKMe9x2GN{s})BI(J~+ z-6vwqNqI)T4daVzVa(M3IX+MK>oL|R;irPJw-?`(YrZ)@z&ai~xb{*0QTGx48Lq8A zvijqEBU^vq;E!u3)>pAtf8?LwWBrk>KeF`)4*s|nQ?CBV zKgP%UBdfp5yxjVOJow}KOMUf6<`}rxtOsP@zoKiIb+OD`SY|FQv#-D!U;56n*AH^< zFR+gnn05aVKDPa2Z9n^)tnI@kS?$@q$q(_d_GD{M_V)7sngy%9@DCz;vb86x{a2hikHp2M+Nl=a^7A$J76B{&E!Wf11Bw{4;-Kn?JJ6-{-+Le`L+yZ*fi5 z{5;cf$RFjOHS*8l`qTJ$e|%q`dauJIQZk`*BUy`p_5I)(0Hw!@UsY z>Yw~RBj^1NT&NFO^;K@`b1USwK4h&AZOOJi;7}jlKPgxLW}^G4YdEVL)@ud-uJ63;k?|^;a??m!%NBqr*xprCm&qvI;X62kamO0)n zbF5qDShmcuW|@6$`PPV;E6BrsDQ2%9|V)XX~xPFi0@I1h^8to0t`-Nq!vjyh8#L9jBf<5nLR?hmq3m=XD4qU%8 z;#=nD&r|cwYj)6(6e?Id@lXiaab?N&%?+1BU^uD>kl0K;n_g!b^Ref7a!}7 ztp3;@vh@cJ{(j`;>W};!e5^mR^+&e;z`@@Sy}kM)KN}zGk8J&stv_(^hi3z^SAXPZ z;bZ-gtv|B$2M+%3@b>DD{1kkwKeF{lw*J7u-}k+}`Xk?j&y(HrzxcMyx>;u1Ei<>4 z*>{#XHY{`Ofwevydj6U%dy?ZYA3qoI=Og|C_=)&v zJ~Yv}Qvn@uBl7;W57fBO<$H7%QEZokN9YP=>H!Kul4Nzemgnh+5EkB@?fLi#5Hx)-wn7v zWcVsiAqaNRb%6xS`otRG`&eh)Yf{muLB_4sJ~MUJ&UrLK?%W?e0_u9jI> z%dD$q*3~lW2G+mNm0TLmZZFE^-%_^n&w79U@3FH_gJe+YaQt{>o7^|KFn-U0bGT+`QHe5}8U@pm<3Wa|&C`r?nQ{^;xPayj3Z zNgdSxHJ-6fcbt#wYdxzyZP^CZzaOqy2g_H1b^Kk4>nkGOWArb_H8{f_PX0Z(mwCn> zu44l363lNXx{E*e2eT;U-9Q?i2$ph2&e51eCvt3Up*Y)IBT%U)J z#(NsB&o#UO*XI~!d%z(cbVLds& z$ntNeLto|cZ?Dt-Hsq82z`9vx-7K?iwAcEvK4AU(Z~3?2M~s~LB-?zFZ9a#MKJ&?a zf%<1Y2Msfy;80)c4j4J}*>9Nn>@&=K_8MmW!NDJ6VI2zje3xU*m$U_PW%;+*Ex*Ux zYd%>w`cr1zEVFL3*Y>hL;E>Pvg4Y>2^GUY(Bx^p8!}Y!KQGe7YTYa+XAB*c#@lk!| z|CESNHq8E5W0?B)inwf;`u8-<{sV`6(6{9>_yl~kKF8ylZL++|F#CI@VfOd(h?f~= z|1XJnv0>&P9O}oqFEHcbwGqG0v5p7HHS`0sZwzOzkN6D{ztM4s&$cqYjvuzwGTUmI zZMDp{fOk;$0E%jCe=H7e~A$V&4C3{dm8# zjC);y`y=j)826Tf{>F&8K3e}=uPt+Jw#>EJGS^Sb<%my=nCrdO=lW@x>x*Sv7kGZe zKr82Wn+p7gh<_jPZzKM7#J`OA7ZKkX@sA?@VZ=X(_>PD_5b^sWes{#t`X*<~CVz64 zY?*}&F6ny<1qw(@9x%3jaPqxr{seW5>aZzPBNrC)!OM`C|GjPifPC(ZAFqRV^8 z-_65+lKI=Fs?*gv7R_v<6m)N{G|O8KRGKJC|4E8Ew@fu=C-JCzoZ!4xt-gN>X-yyD zEH=#^u1(b{O|0bUgPp|{m8pZ&cy1?yocE4r^5S0l1wjIiDKv`vKf1_DzADDLH8_H*%Q*Kx4YY(4}yLLL0(YgCcUnh=NURAE`D^J!A z)+Vcy<-JGBH&mMqsJ>C8$J`Gzx5Cv?MboQX!Ot{6Ru&fvw$*2+tIbNg)~LhpHW4Ge z$%P_5@xp2y(y8+PW@Y9;Z30&ljp><29VzCG=n~PIzojwllfGUGx_8y8n)GC^6fNB~ zRXMV^GI1RoApK2RPl{ym_Eu}QT0T^1*3l6tPk-XPUecJHovNZBdrcSXC8hY?(OIKK zSF3ukifkTPFj;L)G;1?#L&r>|eE=4(<}#^2or{{)7Gh3Rw=`z!ZAo)a^>DjdZ^5M1 zO+9hGkXJp(b-i6{*QzZEYdvLM>d85otnRDTq1vc(*y>M5$L>a>U9jt@SEj2HqoaLf z#$9!7Llv6!%2Yvf-b|&5_~Jje>e?yIbGn^f+v~YyXHTnLY0Id06&BNZ2P;#v|8WaC zMNQg`+8^gzz2k?4jVzbW@|C4eMCCd&SI<%oxo30?C6Ri!6 z=6)1*Qj~T}x3BNpz#I5H7In@amrml)n9qWF%eGc$s&#b7#F0HsjGZbsJ6#mb?-X-f zEf&*&ONfgXTvTa|`{4mzErr^ijFk55s2r&_dr<-I7#6ZD>)zCARhw=uB*#h-J#DSD zD|^suP+1^_J=^QZd3~a~b05kUN!dau?rK&iF-h9g?2uCQ)Bz4swrQ!9bVG0F-Un4D zuplMfQZ(O%rOpZ|Sj?dLpho#1K70~c>fb6J<9U&1_88^8@a>l%gjQ?UsagdoiaIkCNL@6%}TA_p68}* zH%4h~veIre7w)aJsu#@GCtN3Gue5XL|K>R|?0=$_syNS2@Jenk>XXc}?j%n&xic}H z!GhzGs0Y}s(rc9ZpT9BpWu6u1zD%EmX70<3UNq<2mpQq338u&9!Z`3Vd=Ug&!r(z| zWI8RLk77T6=bHP=+`P1N|9B~i=DsXaS#jT`CJRi7&iSA``$9%Yv4+cN7$))j{PLe|0myXF|0jx|0fzFkqMi=XTjcXLH~ z`Twmb|BamkpK|%bQPz|ES5}nc{})yiSqA1_QI6xpTrtOra*PO$nvF7%SC|#WtyahH zsI{=4#0GY*Ez5s$)j2^HiT}Y>XVw48s>78;XC+r1Sy}w7Ki;#h3LGC?Nvyu?UA#VL zF6MbT~*>$hrX@c-2w0Zs`IkKs#BWZnw2eHXRCc=s@l1~*_fTd z+Vs-@TU-7cYYR`z`NQAciAyQDlxUhs3ldE;Nq+vuK4h*L>7D_i>E~|B#hbo+fLXA$ zR>5QO^!k4D2($V^cJ(-WeOI%wuQpZPR&Vnp!9CG*E{sZhI78=mVP7DI0C#I=iFiQ? z_wpR)J$}sdQIl-%jP_2b&&e&hSo)yK!ODiIN`3zZ-Fjp%6v^E=wU}z`cc&KF3&htW zk{y?#z@2OCuC`~J^;Wrkpjxh1aE)UF7*yowqukh6KD?pqG`H8w(~ZgM6dszW0T1Qc zTz2~pOqVP5N$T|VdM!Q)l>2(UXJ~A|411o`W(&?s#dRRTe7E$<=q)AaOhL|6!CQmYB6U9ijk9l6z!97Y+w!%T1Q%wN9r5K z<=1Y1u!EHQ4Ir*M#7v(`+@as__)K-8whxCoGAtYQHjbh86PwlRXKQ$H=kZ#l?A(@T znhidpb4WemZZ{v<^v!>~_*yf*eeMBJNc-dNcHHXW86Wqt;%lCpbXYq6EZo7vO3Hzr3T*~&0@7cC%?SX1-|AF?pGPXA}dur3wXpZ-8=q=++tu`fTrBb;KEt##D|8=C- zEe6gH6Z$9#{@yH_q^+sOOw}KpnzK|Kv^3ice6$EhLvst-Wt^+#CdFe|hMoH;)};pr zuI=)k#$)vb{Mpy9ur5c^6U(bWG8*+qFRyGfyUHi z3x~OK5+)PH9iP7@VSC~ZJi7A^)h62qjxPRhcAe|Ab6o3X@a5S*y?pjLWeMkcs}Dn# z$t+{+GK&wF@p$8|%Xf@#TPx{X=hUO2PCF4}mtU^! z_7I71{v|uNUcO_eD|W+7M+tA<&FVBx;U-bfL!8PLw!yrN*t?+&Pk%3-?#?Z@b$zq# z7b^F@<&5#mcJG<<2GP2H7RTe|CeGKx{pkj&z}>k&Rrc!r>4^ARw`ud-_a`Vere@pK zvK*U-8XOXxSCo4S9bZln^6VZQpu7+go?>254QmvT?5)Vuxz2Dp%dMIB5Lm z>u9F%f8~AFVXgMMvb@&H+%`0?Im_KpJ3Pm`t2t_Zz0|z=pRf6UW-?&Gs9+i2>z0Iy zTM+hgX3R*qjhEmaEwjb1+jd^GrYloZm=)N{R%Gt)Wrpkj-|BhpT=krma2qb!bk$wf zbE?wZe^fQE6T2rTc|JEc8dKFuos0S8(Mt@rO3m3h?!Jxsz6w^+|H1-82TARAZNH$r=b~+wmbYwty6nf%`g1vu{-$eq-Y=f#jklXvrNQp) zW6qUG?p`S8^7|F#>5qgLY&(pH0PHo~3(;GoaM2djur`aQL;sp$OVKtPlO5N2KjUtT zKiOzpFcKFG#|1-iLB9l9co9yb8qFhRzO{nqJ}K=SKTxgmjnj)9R`F()FVkKqPR>7t zm5`qoNP!aU?;{31JNQK4-=k+|NZCo_wdvU@X<|9N-ObYh+tZzGp=_;`9W#zs^0*P| ztDN$V+TNzTa?kE93QOmZoOYLzQ}0r8ib%TD8!B~jT$21oO8jm(yO&V$#dz|&g33uk z^W4)Na`i95XB=p^XU^QX@u+v68^^0tc$mOj;wx&^L;jVg+uomimp1nGWcC;d@GLWD z<$DHhPkr>1)xG-BlE1mVntR)jZ>`(|fSbN9wB1J~|16cf>lV33EBC^+$IcE;i5^lM%c{EWS5k@_}H`0 zJ3KvA#+N(z^^9}YVBft4&oUE@Ni5vwths#81?xxGa3@}$tW4pnDCexHH`bhY?%DU+ zx^v5(t9NZHw+`+v@49^Rj_q5@Yu0bvc;&#BjT^V_*;*dI;=(ds_ix;|?b0>nHD=}= z8sJwGHeTqy)SBdo?LV8*>sGm)m!JxVcp-`+e_^=n+V zoHYk)t=itH>KfaaJ~-Ns>f?y;oHaNhoI3MlSmNg_!urNcWddJnIkWF9yaR7sS3R?i zCc=bYY$;Ekvu3(HxS@Y|Y;0_}e;tk<`vynH1_lSq>w7m0j`R(WjE$`8EpI6gY#13D z=^yJKrOrTK|Jdl*Fbao92l@ww$5=SjJ2KigFoMFNkzsflV&TZZz+f-S28YLb2L}gP zHa0XmHagUY!hylD!M>s4b?XN<^o{iQ4vY?voai^9;1VZgUSsL4fhX@p>Sk)3^hTG1~!cK_m2+uQggU>WTbDTZ{7MqdIF4~ zaB!$^c%WAb`$l?uN5v-!`Ujbb!3}+AEt(I_z9D310MU`DzP{cu6!vWx8}0A!A47B$ z_7B4UFbYS8M|%ecq-;FSH&Q8XE5H_k|-vL!&6`L$>;c zLm68>#eNtV8yPpjP|p3{ln~L zR;71naCnrp8yp@Q8yOa7qk{u|D1q51^T0Txqr)S}LN5SAY5+dV$m0+@aTL)Y;6U|~ z2^jbFk07xu?d=`wqq_lg4@Q&}q56Ytz!35|(vKeX6=6$Rx4}LPQIx6PAZj!=fG$DV zaNkhB*bWX1awsAedT@ZvV){qXd(PijKjshp^^W!S4T;0PK}>)FCJJ3VG>Cjq6{C9q zBL}6uBgh`d2z>RS?fpz(U+)k`5Qmd@Ho$yI=}51*MI%O7114zw$OiNZro@mLGfWW% z3akc^n6bWfV;lN0VnhRzdtgxV-piyw31J6@F-=e(Hcb|R{$37!4wT;B;ZgBD*oS6~ ziX}$Lka)v9!wi%@!!#cdQyG+QnDmYIqw)wb&^tURYYN(d@i{ocLW~5N@@OU|B1aEv zJlr>eenntjjrnF_`V9hB+rYWpmpaA>F(Yt)#Svn6iW_73%q4)?RLAEO_0 z8k&RsSo1KnSf%0qf!;w5aO559S)Z8q4vzMXG7yAlh^rD74h#lbW29gv&}HA~2#YYq zI4IqW9YoE!u*vwu@`E`!Ff>*k-7q*f27^%uZdg;^c<$L7_v3>#9)Cnu^3M~xV=4LP ziNOAOBCvm+2z>wI{V@H6uzPgak9Q{iYiQ?1+x*g1MY|+^Fi39|g{~{`zNLbj5AMeA zkdm91&0bJW!Abe+$(ZJM9HW{zDX1@}L5UHQyw%4cY* zO*I-bd0m_hPKg<AQhHQqBA1T^$^cBQMo5w1sBZrN9GifvT-vt&=$`eC(b}~~u>O{h^qqdj+F4x8 z_MN_QE$SjyCJ7<)tb_+FH*c5a<{o=+g1m2bYRYs23-r62>03qZ>;neC_NyHw)I4`{gVCZ;>W%u>GzHk@p7eLyh&5JWS$~4_+V06hcPd zBz6yoGIclK2l$iPy|^3pvUbU-?l34n{5d5mNMA4Ycy=;JMY>HF?!_3f%=6=JoAKz6 zA-XZ(_hQ9BMameZd$El0(hdD?29rOPsFXLB#kCu{xj_7f@t3TLZkfrwqJs1_(vFwK z?Z69De7~Q$_k-NDX&PVjpPrqbLr^(v1h$p%H9OUYMKn}gIoB6BT|4dU*OhUx)s5n9gffGlw8j%qr`+l`6qFjn0mN9T6_IShu!KYr5L5aD~qQ8bey#wfRW9s@mBCf3pq+T{{}}{eikKm}*qo0eiMKW^voh z*&UDMyt_KlXih3~+^5~m>OTC%3(Pur?2V%AuBwKwM(dx#$hW_2d$RmbH{^Q=OhVed zsoAU?vEdl VALIDATIONS = SYSDYN.Validations : L0.Library -SYSDYN.SysdynIssue -- VALIDATIONS.Issue.stringContexts --> L0.List links = new ArrayList(); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/ExpressionField.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/ExpressionField.java index 39e7381d..24c02b7e 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/ExpressionField.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/ExpressionField.java @@ -15,7 +15,6 @@ import java.util.List; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; -import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.PaintManager; @@ -36,13 +35,13 @@ import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Table; import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; -import org.eclipse.swt.graphics.Point; -import org.simantics.sysdyn.expressionParser.Token; +import org.simantics.sysdyn.ui.utils.SyntaxError; /** * Field for displaying a part of an expression. Expression field uses SourceViewer @@ -62,7 +61,8 @@ public class ExpressionField extends Composite { public static final String MISSING_LINK = "MissingLink"; public static final String NO_SUCH_VARIABLE = "NoSuchVariable"; public static final String SYNTAX_ERROR = "SyntaxError"; - + public static final String SYNTAX_WARNING = "SyntaxWarning"; + String oldExpression; ColorManager cManager = new ColorManager(); @@ -112,6 +112,8 @@ public class ExpressionField extends Composite { painter.setAnnotationTypeColor(NO_SUCH_VARIABLE, new Color(this.getDisplay(), 255,0,0)); painter.addAnnotationType(SYNTAX_ERROR); painter.setAnnotationTypeColor(SYNTAX_ERROR, new Color(this.getDisplay(), 255,0,0)); + painter.addAnnotationType(SYNTAX_WARNING); + painter.setAnnotationTypeColor(SYNTAX_WARNING, new Color(this.getDisplay(), 255,215,0)); _sourceViewer.setDocument(_document, _annotationModel); @@ -221,59 +223,16 @@ public class ExpressionField extends Composite { } } - /** - * Sets syntax error for the given token - * @param token Token with syntax error - * @param message Message to be displayed in tool tips - */ - public void setSyntaxError(Token token, String message){ - setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn); - } - - /** - * Sets syntax error for the given token - * @param token Token with syntax error - * @param message Message to be displayed in tool tips - */ - public void setSyntaxError(org.simantics.sysdyn.tableParser.Token token, String message){ - setSyntaxError(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn); - } - - /** - * Sets syntax error to given location - * @param image Token image - * @param message Message to be displayed in tool tips - * @param beginLine Begin line - * @param beginColumn Begin column - * @param endLine End line - * @param endColumn End column - */ - public void setSyntaxError(String image, String message, int beginLine, int beginColumn, int endLine, int endColumn) { - int start = 0; - int offset = this._document.getLength(); - if(image != null && this._document.getLength() > 0) { - try { - start = this._document.getLineOffset(beginLine - 1) + beginColumn - 1; - offset = this._document.getLineOffset(endLine - 1) + endColumn - start; - } catch (BadLocationException e) { - e.printStackTrace(); - } - } - setSyntaxError(start, offset, SYNTAX_ERROR, message == null ? "Syntax Error" : message); - } /** - * Sets syntax error to given start and offset - * @param start Start location - * @param offset Offset - * @param type Error type (SYNTAX_ERROR, MISSING_LINK, NO_SUCH_VARIABLE) - * @param text Message to be displayedin tool tips + * Sets a syntax error annoattion to the expression field + * @param syntaxError */ - public void setSyntaxError(int start, int offset, String type, String text) { + public void setSyntaxError(SyntaxError syntaxError) { Annotation annotation = new Annotation(false); - annotation.setType(type); - annotation.setText(text); - Position p = new Position(start, offset); + annotation.setType(syntaxError.getType()); + annotation.setText(syntaxError.getMessage()); + Position p = new Position(syntaxError.getStart(_document), syntaxError.getOffset(_document)); _annotationModel.addAnnotation(annotation, p); } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/WithLookupExpression.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/WithLookupExpression.java index 5d2fa12e..8da88f5d 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/WithLookupExpression.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/expressions/WithLookupExpression.java @@ -64,6 +64,7 @@ import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.tableParser.ParseException; import org.simantics.sysdyn.tableParser.TableParser; import org.simantics.sysdyn.tableParser.Token; +import org.simantics.sysdyn.ui.utils.SyntaxError; import org.simantics.ui.SimanticsUI; public class WithLookupExpression implements IExpression { @@ -392,7 +393,7 @@ public class WithLookupExpression implements IExpression { Double.parseDouble(yTokens.get(i).image))); } } catch (ParseException e1) { - this.lookup.setSyntaxError(e1.currentToken, "Syntax Error"); + this.lookup.setSyntaxError(new SyntaxError(e1.currentToken, "Syntax Error")); System.out.println("MESSAGE: " + e1.getMessage()); return; } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/functions/FunctionCodeWidget.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/functions/FunctionCodeWidget.java index 8fd7bdb8..2d0cb58d 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/functions/FunctionCodeWidget.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/functions/FunctionCodeWidget.java @@ -43,6 +43,7 @@ import org.simantics.sysdyn.modelParser.ParseException; import org.simantics.sysdyn.modelParser.Token; import org.simantics.sysdyn.modelParser.TokenMgrError; import org.simantics.sysdyn.ui.properties.widgets.expressions.ExpressionField; +import org.simantics.sysdyn.ui.utils.SyntaxError; import org.simantics.ui.SimanticsUI; import org.simantics.ui.utils.AdaptionUtils; @@ -164,7 +165,7 @@ public class FunctionCodeWidget implements Widget { modelParser.parse_composition(); } catch (ParseException e1) { Token token = e1.currentToken; - modelicaCode.setSyntaxError(token.image, "Syntax error", token.beginLine, token.beginColumn, token.endLine, token.endColumn); + modelicaCode.setSyntaxError(new SyntaxError(token.image, "Syntax error", token.beginLine, token.beginColumn, token.endLine, token.endColumn)); } catch (TokenMgrError err) { String message = err.getMessage(); String line = message.substring(0, message.indexOf(",")); @@ -175,7 +176,7 @@ public class FunctionCodeWidget implements Widget { Integer endLine = Integer.parseInt(line); Integer endColumn = Integer.parseInt(column); Token token = modelParser.token; - modelicaCode.setSyntaxError(token.image, "Syntax error", token.endLine, token.endColumn, endLine, endColumn); + modelicaCode.setSyntaxError(new SyntaxError(token.image, "Syntax error", token.endLine, token.endColumn, endLine, endColumn)); } catch (NumberFormatException e) { } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ArrayVariableUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ArrayVariableUtils.java index 574f71b8..88cc7229 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ArrayVariableUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ArrayVariableUtils.java @@ -12,102 +12,143 @@ import org.simantics.sysdyn.representation.Enumeration; import org.simantics.sysdyn.representation.EnumerationIndex; import org.simantics.sysdyn.representation.IElement; import org.simantics.sysdyn.representation.Variable; +import org.simantics.sysdyn.ui.properties.widgets.expressions.ExpressionField; public class ArrayVariableUtils { - /** - * Checks if the given range elements can be applied to the given variable. - * - * @param graph ReadGraph - * @param variable Resource of the variable - * @param range Range elements in the correct order. Elements are separated in range definition by ',' - * @return true if range is valid, false if not - * @throws DatabaseException - */ - public static Map isRangeValid(ReadGraph graph, Variable variable, String[] elements) throws DatabaseException { - if(variable == null) - return null; - Map result = new HashMap(); - // Not an array variable - if(variable.getArrayIndexes() == null || - variable.getArrayIndexes().getEnumerations() == null || - variable.getArrayIndexes().getEnumerations().size() == 0) { - for(int i = 0; i < elements.length ; i++) - result.put(i, "Variable is not an array variable"); - return result; - } - ArrayList enumerations = variable.getArrayIndexes().getEnumerations(); - // Too many elements - if(elements.length > enumerations.size()) { - result.put( enumerations.size(), "Too many elements"); - } else if(elements.length < enumerations.size()) { - result.put( elements.length > 0 ? elements.length - 1 : 0, "Too few elements"); - } - - - for(int i = 0; i < elements.length && i < enumerations.size(); i++) { - if(elements[i].trim().equals(":")) - continue; - if(elements[i].indexOf(":") != elements[i].lastIndexOf(":")) { - result.put( i, "Too many ':' elements"); - continue; - } + /** + * Checks if the given range elements can be applied to the given variable. + * + * @param graph ReadGraph + * @param variable Resource of the variable + * @param range Range elements in the correct order. Elements are separated in range definition by ',' + * @return true if range is valid, false if not + * @throws DatabaseException + */ + public static Map isRangeValid(ReadGraph graph, Variable variable, String[] elements) throws DatabaseException { + SyntaxError error; + if(variable == null) + return null; + Map result = new HashMap(); + // Not an array variable + if(variable.getArrayIndexes() == null || + variable.getArrayIndexes().getEnumerations() == null || + variable.getArrayIndexes().getEnumerations().size() == 0) { + for(int i = 0; i < elements.length ; i++) { + error = new SyntaxError(); + error.setMessage("Variable is not an array variable"); + error.setType(ExpressionField.SYNTAX_ERROR); + result.put(i, error); + } - String[] rangeComponents = elements[i].split(":"); - if(rangeComponents.length > 2){ - result.put( i, "Too many ':' elements"); - continue; - } - // Single range component, equals to the enumeration at that index - if(rangeComponents.length == 1 && rangeComponents[0].trim().equals(enumerations.get(i).getName())) - continue; - // one or two range components, they all equal to individual indexes in the enumeration - for(String r : rangeComponents) { - r = r.trim(); - boolean componentIsValid = false; - for(EnumerationIndex ei : enumerations.get(i).getEnumerationIndexes()) { - if(ei.getName().equals(r)) { - componentIsValid = true; - break; - } - } - if(!componentIsValid) - result.put( i, "Invalid range"); - } - } - if(result.isEmpty()) - return null; - else - return result; - } + return result; + } + ArrayList enumerations = variable.getArrayIndexes().getEnumerations(); + // Too many elements + if(elements.length > enumerations.size()) { + error = new SyntaxError(); + error.setMessage( "Too many elements"); + error.setType(ExpressionField.SYNTAX_ERROR); + result.put(enumerations.size(), error); + } else if(elements.length < enumerations.size()) { + error = new SyntaxError(); + error.setMessage("Too few elements"); + error.setType(ExpressionField.SYNTAX_ERROR); + result.put(elements.length > 0 ? elements.length - 1 : 0, error); + } - /** - * Checks if the given range can be applied to the given variable. - * - * @param graph ReadGraph - * @param variable Resource of the variable - * @param range Range WITHOUT [ and ] brackets - * @return true if range is valid, false if not - * @throws DatabaseException - */ - public static boolean isRangeValid(ReadGraph graph, Resource variable, String range) throws DatabaseException { - if(variable == null) - return true; - String[] elements = range.split(","); - SysdynModel model = ModelUtils.getModel(graph, variable); - if(model == null) - return false; - IElement e = model.getElement(variable); - if(e != null && e instanceof Variable) { - Variable v = (Variable) e; - Map result = isRangeValid(graph, v, elements); - if(result == null) - return true; - else - return false; - } else { - return false; - } - } + + for(int i = 0; i < elements.length && i < enumerations.size(); i++) { + if(elements[i].trim().equals(":")) + continue; + if(elements[i].indexOf(":") != elements[i].lastIndexOf(":")) { + error = new SyntaxError(); + error.setMessage( "Too many ':' elements"); + error.setType(ExpressionField.SYNTAX_ERROR); + result.put(i, error); + continue; + } + + String[] rangeComponents = elements[i].split(":"); + if(rangeComponents.length > 2){ + error = new SyntaxError(); + error.setMessage( "Too many ':' elements"); + error.setType(ExpressionField.SYNTAX_ERROR); + result.put(i, error); + continue; + } + // Single range component, equals to the enumeration at that index + if(rangeComponents.length == 1 && rangeComponents[0].trim().equals(enumerations.get(i).getName())) + continue; + // one or two range components, they all equal to individual indexes in the enumeration + for(String r : rangeComponents) { + r = r.trim(); + boolean componentIsValid = false; + for(EnumerationIndex ei : enumerations.get(i).getEnumerationIndexes()) { + if(ei.getName().equals(r)) { + componentIsValid = true; + break; + } + } + if(!componentIsValid) { + // Check if the range is an integer that is between 0 and enumeration indexes size + try { + int index = Integer.parseInt(r); + int min = 1; + int max = enumerations.get(i).getEnumerationIndexes().size(); + if(index >= min && index <= max) { + componentIsValid = true; + error = new SyntaxError(); + error.setMessage("Using numbers as array indexes is not encouraged"); + error.setType(ExpressionField.SYNTAX_WARNING); + result.put(i, error); + } else { + error = new SyntaxError(); + error.setMessage("Invalid array index " + index + ". Numbered index must be between " + min + " and " + max + " unless the enumeration is overridden. In that case, ignore this warning."); + error.setType(ExpressionField.SYNTAX_ERROR); + result.put(i, error); + } + } catch (NumberFormatException e) { + error = new SyntaxError(); + error.setMessage("Invalid range"); + error.setType(ExpressionField.SYNTAX_ERROR); + result.put(i, error); + } + } + } + } + if(result.isEmpty()) + return null; + else + return result; + } + + /** + * Checks if the given range can be applied to the given variable. + * + * @param graph ReadGraph + * @param variable Resource of the variable + * @param range Range WITHOUT [ and ] brackets + * @return true if range is valid, false if not + * @throws DatabaseException + */ + public static boolean isRangeValid(ReadGraph graph, Resource variable, String range) throws DatabaseException { + if(variable == null) + return true; + String[] elements = range.split(","); + SysdynModel model = ModelUtils.getModel(graph, variable); + if(model == null) + return false; + IElement e = model.getElement(variable); + if(e != null && e instanceof Variable) { + Variable v = (Variable) e; + if(isRangeValid(graph, v, elements) == null) + return true; + else + return false; + } else { + return false; + } + } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java index c9419675..758b90f8 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ExpressionUtils.java @@ -13,6 +13,7 @@ package org.simantics.sysdyn.ui.utils; import java.io.StringReader; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -141,27 +142,28 @@ public class ExpressionUtils { functionReferences.put(ef, parser.getFunctionCallReferences()); } catch (ParseException e1) { - ef.setSyntaxError(e1.currentToken, "Syntax Error"); + ef.setSyntaxError(new SyntaxError(e1.currentToken, "Syntax Error")); } catch (TokenMgrError err) { - ef.setSyntaxError(0, textString.length(), ExpressionField.SYNTAX_ERROR, "Expression contains unsupported characters"); + ef.setSyntaxError(new SyntaxError(0, textString.length(), ExpressionField.SYNTAX_ERROR, "Expression contains unsupported characters")); } } + // Get model configuration + SysdynModelManager sdm = SysdynModelManager.getInstance(SimanticsUI.getSession()); + SysdynModel model = sdm.getModel(configuration); + try { + model.update(); + } catch (DatabaseException e1) { + e1.printStackTrace(); + } + Configuration conf = model.getConfiguration(); + + // Check variable references final HashMap modelVariables = new HashMap(); HashSet ignoreVariables = new HashSet(); - if(!variables.isEmpty() || !functionReferences.isEmpty()) { Set noSuchVariables = new HashSet(); - SysdynModelManager sdm = SysdynModelManager.getInstance(SimanticsUI.getSession()); - SysdynModel model = sdm.getModel(configuration); - try { - model.update(); - } catch (DatabaseException e1) { - e1.printStackTrace(); - } - - Configuration conf = model.getConfiguration(); ArrayList elements = conf.getElements(); for(IElement e : elements) { if(e instanceof Variable) { @@ -180,71 +182,26 @@ public class ExpressionUtils { // Examine sheets for(ExpressionField ef : functionReferences.keySet()) { for(String key : functionReferences.get(ef).keySet()) { - String[] parts = key.split("\\."); - Object current = conf; - for(int i = 0; i < parts.length && current != null; i++) { - current = getElement(current, parts[i]); - } - if(current == null && conf.getModuleType() != null) { - // Sheets are currently located in the model root. Try to find the sheet. - current = conf.getModuleType().getParent(); // Get module type parent (should be a model) - if(current instanceof Model) - current = getElement(((Model)current).getModelConfiguration(), parts[0]); // Try to get the sheet - } - - if(current != null && current instanceof Sheet) { - Sheet sheet = (Sheet) current; - String e = ef.getExpression(); - int start = 0, end = 0, call = 0; - String cellOrRange = null; - while((call = e.indexOf(key, end)) >= 0) { - start = e.indexOf("(", call) +1; - end = e.indexOf(")", start); - if(start < 0 || end < 0 || end < start) { - break; - } - Pattern p = Pattern.compile("[-\\+\\*\\/\\(\\)\\{\\}\\[\\],\\.\\t\\n\\r\\f]"); - cellOrRange = e.substring(start, end); - Matcher m = p.matcher(cellOrRange); - if (m.find() || cellOrRange.split(":").length > 2) { - ef.setSyntaxError(start, end - start, ExpressionField.SYNTAX_ERROR, "Not a valid cell or range"); - } - } - - List tokens = functionReferences.get(ef).get(key); - for(Token cell : tokens) { - List refs = references.get(ef).get(cell.image); - if(refs != null) - refs.remove(cell); - if(!sheet.getCells().containsKey(cell.image)) - ef.setSyntaxError(cell.image, "Invalid cell", cell.beginLine, cell.beginColumn, cell.endLine, cell.endColumn); - } - + List errors = examineSheetReferences(conf, key, functionReferences.get(ef).get(key), ef.getExpression(), references.get(ef)); + if(errors != null) { + for(SyntaxError error : errors) + ef.setSyntaxError(error); } } } + // Examine variable references for(String v : variables) { - - if(modelVariables.get(v) instanceof Enumeration || modelVariables.get(v) instanceof Sheet) { - ignoreVariables.add(v); - } - - // Reference to some other module, spreadsheet or enumeration - if(v.contains(".")) { - String[] parts = v.split("\\."); - Object parent = conf; - for(int i = 0; i < parts.length && parent != null; i++) { - parent = getElement(parent, parts[i]); - } - if(parent == null) { + ReferenceOption option = getReferenceOption(conf, v); + switch(option) { + case DOES_NOT_EXIST: noSuchVariables.add(v); - } else { + break; + case CANNOT_BE_CONNECTED: ignoreVariables.add(v); - } - } else if(!modelVariables.keySet().contains(v)) { - noSuchVariables.add(v); + break; + case CAN_BE_CONNECTED: } } @@ -289,119 +246,252 @@ public class ExpressionUtils { } + for(final ExpressionField ef : ranges.keySet()) { + List errors = new ArrayList(); + // RANGES + errors.addAll(examineArrayRanges(conf, ranges.get(ef), forIndices.get(ef))); + + // ENUMERATION REFERENCES IN FOR-LOOPS + errors.addAll(examineEnumerationReferences(conf, enumerationReferences.get(ef))); - HashMap> errors = new HashMap>(); - HashMap result = null; + for(SyntaxError error : errors) + ef.setSyntaxError(error); + } + } + + + static public List examineArrayRanges( + final Configuration configuration, + final HashMap>> ranges, + final HashMap> forIndices) { + + List result = Collections.emptyList(); + try { + result = SimanticsUI.getSession().syncRequest(new Read>() { - for(final ExpressionField ef : ranges.keySet()) { + @Override + public List perform(ReadGraph graph) throws DatabaseException { + return examineArrayRanges(graph, configuration, ranges, forIndices); + } + }); + } catch (DatabaseException e) { + e.printStackTrace(); + } + return result; + } - // RANGES - try { - result = SimanticsUI.getSession().syncRequest(new Read>() { - - @Override - public HashMap perform(ReadGraph graph) throws DatabaseException { - HashMap result = new HashMap(); - for(String s : ranges.get(ef).keySet()) { - if(ranges.get(ef).containsKey(s)) { - for(List l : ranges.get(ef).get(s)) { - String[] ss = new String[l.size()]; - for(int i = 0; i < l.size(); i++) { - ss[i] = l.get(i).image; - } - Map invalidRanges = ArrayVariableUtils.isRangeValid(graph, modelVariables.get(s), ss); - if(invalidRanges != null && !invalidRanges.isEmpty()) { - for(Integer i : invalidRanges.keySet()) { - result.put(l.get(i), invalidRanges.get(i)); - } - } - } + static public List examineEnumerationReferences(Configuration configuration, HashMap> enumRefList) { + ArrayList result = new ArrayList(); + for(String enumeration : enumRefList.keySet()) { + for(Token et : enumRefList.get(enumeration)) { + Object o = getElement(configuration, enumeration); + if(o != null && o instanceof Enumeration) { + boolean isFound = false; + Enumeration e = (Enumeration)o; + + if(enumeration.equals(et.image) || + "size".equals(et.image) || + "elements".equals(et.image)){ + // The full enumeration + isFound = true; + } else { + for(EnumerationIndex ei : e.getEnumerationIndexes()) { + if(ei.getName().equals(et.image)) { + isFound = true; + break; } - } - return result; + } } - }); - } catch (DatabaseException e) { - e.printStackTrace(); - } - // FOR-INDICES - - HashMap> indexList = forIndices.get(ef); - for(Token t : indexList.keySet()) { - boolean isFound = false; - if(indexList.get(t) != null) { - for(Token rt : indexList.get(t)) { - if(rt.image.equals(t.image)) { - isFound = true; - // remove range token from invalid ranges - result.remove(rt); - break; + if(!isFound) { + StringBuilder sb = new StringBuilder(); + sb.append("Enumeration "); + sb.append(enumeration); + sb.append(" has no such index.\nAvailable indexes are: "); + Iterator iterator = e.getEnumerationIndexes().iterator(); + while(iterator.hasNext()) { + sb.append(iterator.next().getName()); + if(iterator.hasNext()) + sb.append(", "); } + result.add(new SyntaxError(et, sb.toString())); } - } - if(!isFound) { - result.put(t, "Invalid index"); + + + } else { + result.add(new SyntaxError(et, "No such enumeration (" + enumeration + ")")); } } + } + return result; + } - // ENUMERATION REFERENCES IN FOR-LOOPS - HashMap> enumRefList = enumerationReferences.get(ef); - for(String enumeration : enumRefList.keySet()) { - for(Token et : enumRefList.get(enumeration)) { - Variable v = modelVariables.get(enumeration); - if(v != null && v instanceof Enumeration) { - boolean isFound = false; - Enumeration e = (Enumeration)v; - - if(enumeration.equals(et.image) || - "size".equals(et.image) || - "elements".equals(et.image)){ - // The full enumeration - isFound = true; - } else { - for(EnumerationIndex ei : e.getEnumerationIndexes()) { - if(ei.getName().equals(et.image)) { - isFound = true; - break; - } - } - } + /** + * + * @param graph + * @param configuration + * @param ranges + * @param forIndices + * @return + * @throws DatabaseException + */ + static public List examineArrayRanges( + ReadGraph graph, + Configuration configuration, + HashMap>> ranges, + HashMap> forIndices) throws DatabaseException { + HashMap errors = new HashMap(); + for(String name : ranges.keySet()) { + if(ranges.get(name) != null) { + for(List l : ranges.get(name)) { + String[] rangeReferences = new String[l.size()]; + for(int i = 0; i < l.size(); i++) { + rangeReferences[i] = l.get(i).image; + } - if(!isFound) { - StringBuilder sb = new StringBuilder(); - sb.append("Enumeration "); - sb.append(enumeration); - sb.append(" has no such index.\nAvailable indexes are: "); - Iterator iterator = e.getEnumerationIndexes().iterator(); - while(iterator.hasNext()) { - sb.append(iterator.next().getName()); - if(iterator.hasNext()) - sb.append(", "); + Object o = getElement(configuration, name); + if(o != null && o instanceof Variable) { + Map invalidRanges = ArrayVariableUtils.isRangeValid(graph, (Variable)o, rangeReferences); + if(invalidRanges != null && !invalidRanges.isEmpty()) { + for(Integer i : invalidRanges.keySet()) { + SyntaxError error = invalidRanges.get(i); + error.setToken(l.get(i)); + errors.put(l.get(i), error); } - result.put(et, sb.toString()); } - - - } else { - result.put(et, "No such enumeration (" + enumeration + ")"); } } } - - errors.put(ef, result); + } - } + // FOR-INDICES - for(ExpressionField ef : ranges.keySet()) { - HashMap tokens = errors.get(ef); - if(tokens == null || tokens.isEmpty()) continue; - for(Token t : tokens.keySet()) { - ef.setSyntaxError(t, tokens.get(t)); + HashSet removes = new HashSet(); + for(Token t : forIndices.keySet()) { + boolean isFound = false; + for(Token rt : errors.keySet()) { + if(rt.image.equals(t.image)) { + isFound = true; + // remove range token from invalid ranges + removes.add(rt); + } } + if(!isFound) { + SyntaxError error = new SyntaxError(t, "Invalid index"); + errors.put(t, error); + } + } + + for(Token t : removes) + errors.remove(t); + + return new ArrayList(errors.values()); + } + + + /** + * Examine if a given functionKey is a sheet reference and whether all the tokens in the function ( Sheet1(token1, token2) ) + * are valid. + * + * @param configuration Configuration where the function is called + * @param functionKey Function name + * @param functionTokens Function parameters (sheet reference, either a cell or cell range) + * @param expression The whole expression where the function reference is + * @param expressionReferences All variable references, including function parameters + * @return A list of possible errors + */ + static public List examineSheetReferences( + Configuration configuration, + String functionKey, + List functionTokens, + String expression, + HashMap> expressionReferences) { + + List result = new ArrayList(); + + String[] parts = functionKey.split("\\."); + Object current = configuration; + for(int i = 0; i < parts.length && current != null; i++) { + current = getElement(current, parts[i]); } + + if(current == null && configuration.getModuleType() != null) { + // Sheets are currently located in the model root. Try to find the sheet. + current = configuration.getModuleType().getParent(); // Get module type parent (should be a model) + if(current instanceof Model) + current = getElement(((Model)current).getModelConfiguration(), parts[0]); // Try to get the sheet + } + + if(current != null && current instanceof Sheet) { + Sheet sheet = (Sheet) current; + int start = 0, end = 0, call = 0; + String cellOrRange = null; + while((call = expression.indexOf(functionKey, end)) >= 0) { + start = expression.indexOf("(", call) +1; + end = expression.indexOf(")", start); + if(start < 0 || end < 0 || end < start) { + break; + } + Pattern p = Pattern.compile("[-\\+\\*\\/\\(\\)\\{\\}\\[\\],\\.\\t\\n\\r\\f]"); + cellOrRange = expression.substring(start, end); + Matcher m = p.matcher(cellOrRange); + if (m.find() || cellOrRange.split(":").length > 2) { + result.add(new SyntaxError(start, end - start, ExpressionField.SYNTAX_ERROR, "Not a valid cell or range", cellOrRange)); + } + } + + + for(Token cell : functionTokens) { + List refs = expressionReferences.get(cell.image); + if(refs != null) + refs.remove(cell); + if(!sheet.getCells().containsKey(cell.image)) + result.add(new SyntaxError(cell.image, "Invalid cell", cell.beginLine, cell.beginColumn, cell.endLine, cell.endColumn)); + } + } + + return result; + } + + + /** + * Option for a reference. Whether the reference does not exist, + * it can be connected (normal variable) or it cannot be + * connected even though it exists (e.g. enumeration index) + * + * @author Teemu Lempinen + * + */ + static public enum ReferenceOption {DOES_NOT_EXIST, CAN_BE_CONNECTED, CANNOT_BE_CONNECTED}; + + /** + * Get the reference option for a reference (name) in a configuration. + * + * @param conf Configuration + * @param name Referred variable + * @return The result tells does the referred name exist and can it be connected to + */ + static public ReferenceOption getReferenceOption(Configuration conf, String name) { + + String[] parts = name.split("\\."); + Object element = conf; + for(int i = 0; i < parts.length && element != null; i++) { + element = getElement(element, parts[i]); + } + if(element == null) + return ReferenceOption.DOES_NOT_EXIST; + else if(Boolean.TRUE.equals(element)) + return ReferenceOption.CANNOT_BE_CONNECTED; + else if(element instanceof Variable) { + if(element instanceof Enumeration || element instanceof Sheet) + return ReferenceOption.CANNOT_BE_CONNECTED; + else + return ReferenceOption.CAN_BE_CONNECTED; + } + else + return ReferenceOption.DOES_NOT_EXIST; } static private Object getElement(Object parent, String name) { diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/SyntaxError.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/SyntaxError.java new file mode 100644 index 00000000..a2f86e76 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/SyntaxError.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2012 Association for Decentralized Information Management in + * Industry THTH ry. + * 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.utils; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.simantics.sysdyn.expressionParser.Token; +import org.simantics.sysdyn.ui.properties.widgets.expressions.ExpressionField; + +/** + * Class for containing errors caught in expression validation + * @author Teemu Lempinen + * + */ +public class SyntaxError { + + private String type, message, image; + private int beginLine, beginColumn, endLine, endColumn; + private Integer start, offset; + + /** + * A syntax error without any info. Remember to assign location, + * type and message information. + */ + public SyntaxError() { + + } + + /** + * Creates an error from expression parser token + * @param token expressionParser token + * @param message Error message + */ + public SyntaxError(Token token, String message){ + this(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn); + } + + /** + * Creates an error from table parser token + * @param token tableParser token + * @param message Error message + */ + public SyntaxError(org.simantics.sysdyn.tableParser.Token token, String message){ + this(token.image, message, token.beginLine, token.beginColumn, token.endLine, token.endColumn); + } + + /** + * Create syntax error with full data + * @param image + * @param message + * @param beginLine + * @param beginColumn + * @param endLine + * @param endColumn + */ + public SyntaxError(String image, String message, int beginLine, int beginColumn, int endLine, int endColumn) { + this.image = image; + this.message = message; + this.beginLine = beginLine; + this.beginColumn = beginColumn; + this.endLine = endLine; + this.endColumn = endColumn; + this.type = ExpressionField.SYNTAX_ERROR; + } + + /** + * Create syntax error with location, message and type data + * @param start + * @param offset + * @param type + * @param message + */ + public SyntaxError(int start, int offset, String type, String message) { + this(start, offset, type, message, null); + } + + /** + * Create syntax error with location, message and type data + * @param start + * @param offset + * @param type + * @param message + */ + public SyntaxError(int start, int offset, String type, String message, String image) { + this.image = image; + this.start = start; + this.offset = offset; + this.type = type; + this.message = message; + } + + /** + * Get start location of the issue. If start has not been set, + * document is used to calculate it. + * @param document + * @return + */ + public int getStart(IDocument document) { + if(this.start != null) + return this.start; + else if(document != null){ + int start = 0; + if(document.getLength() > 0) { + try { + start = document.getLineOffset(beginLine - 1) + beginColumn - 1; + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + return start; + } else { + return 0; + } + } + + /** + * Get offset data of the issue. If offset has not been set, + * document is used to calculate it. + * + * @param document + * @return + */ + public int getOffset(IDocument document) { + if(this.offset != null) + return this.offset; + else if(document != null){ + int start = getStart(document); + int offset = document.getLength(); + if(document.getLength() > 0) { + try { + offset = document.getLineOffset(endLine - 1) + endColumn - start; + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + return offset; + } else { + return 0; + } + } + + public void setToken(Token token){ + this.image = token.image; + this.beginLine = token.beginLine; + this.beginColumn = token.beginColumn; + this.endLine = token.endLine; + this.endColumn = token.endColumn; + } + + public void setToken(org.simantics.sysdyn.tableParser.Token token){ + this.image = token.image; + this.beginLine = token.beginLine; + this.beginColumn = token.beginColumn; + this.endLine = token.endLine; + this.endColumn = token.endColumn; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public int getBeginLine() { + return beginLine; + } + + public void setBeginLine(int beginLine) { + this.beginLine = beginLine; + } + + public int getBeginColumn() { + return beginColumn; + } + + public void setBeginColumn(int beginColumn) { + this.beginColumn = beginColumn; + } + + public int getEndLine() { + return endLine; + } + + public void setEndLine(int endLine) { + this.endLine = endLine; + } + + public int getEndColumn() { + return endColumn; + } + + public void setEndColumn(int endColumn) { + this.endColumn = endColumn; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java index 25867476..fce73fd7 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java @@ -13,21 +13,24 @@ package org.simantics.sysdyn.ui.validation; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Set; import org.simantics.db.Issue; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; -import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variable; -import org.simantics.issues.common.IssueUtils; -import org.simantics.issues.common.StandardIssue; import org.simantics.layer0.Layer0; import org.simantics.scl.reflection.annotations.SCLValue; import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.manager.SysdynModel; +import org.simantics.sysdyn.manager.SysdynModelManager; +import org.simantics.sysdyn.representation.Configuration; +import org.simantics.sysdyn.ui.properties.widgets.expressions.ExpressionField; +import org.simantics.sysdyn.ui.utils.ExpressionUtils; +import org.simantics.sysdyn.ui.utils.ExpressionUtils.ReferenceOption; +import org.simantics.sysdyn.ui.utils.SyntaxError; import org.simantics.utils.datastructures.collections.CollectionUtils; /** @@ -67,7 +70,7 @@ public class DependencyFunction { // Find all references in equations of component try { - references = ValidationUtils.getReferences(graph, component); + references = ValidationUtils.getAllReferences(graph, component).getVariableReferences(); } catch (Exception e) { return result; } @@ -79,14 +82,28 @@ public class DependencyFunction { if (dependencies != null) { for (String dependency : dependencies) { if (references == null || !references.contains(dependency)) { - Resource variable = ValidationUtils.reach(graph, component, dependency); - result.add(new StandardIssue(sr.Validations_UnusedDependencyIssue, component, variable)); + result.add(new IssueWithStringContext(sr.Validations_UnusedDependencyIssue, component, dependency)); } } } return result; } + + + private static Configuration getConfiguration(ReadGraph graph, Resource component) throws DatabaseException { + Resource configuration = graph.getPossibleObject(component, Layer0.getInstance(graph).PartOf); + + if(configuration == null) + return null; + + SysdynModelManager smm = SysdynModelManager.getInstance(graph.getSession()); + SysdynModel sm = smm.getModel(graph, configuration); + if(sm == null) + return null; + + return sm.getConfiguration(); + } /** * Evaluates dependency-related issues for a component. @@ -109,44 +126,86 @@ public class DependencyFunction { if (!graph.hasStatement(component) || !graph.hasStatement(component, l0.PartOf)) return Collections.emptyList(); - // Find all variables that are linked to component with arrows - Set dependencies = ValidationUtils.getDependencies(graph, component); - Set references = null; - // Find all references in equations of component + References references = null; try { - references = ValidationUtils.getReferences(graph, component); - } catch (SyntaxErrorException e) { - } catch (UnsupportedCharactersException e) { - } catch (UndefinedExpressionException e) { + references = ValidationUtils.getAllReferences(graph, component); + } catch (Exception e) { + return Collections.emptyList(); } - + + Configuration configuration = getConfiguration(graph, component); ArrayList result = new ArrayList(); + + // Examine Sheet references + for(Resource expressionResource : references.functionReferences.keySet()) { + for(String functionKey : references.functionReferences.get(expressionResource).keySet()) { + List sheetErrors = ExpressionUtils.examineSheetReferences( + configuration, + functionKey, + references.functionReferences.get(expressionResource).get(functionKey), + references.expressions.get(expressionResource), + references.references.get(expressionResource)); + if(sheetErrors != null) { + for(SyntaxError error : sheetErrors) + result.add(new IssueWithStringContext(sr.Validations_InvalidSheetReferenceIssue, component, error.getMessage(), error.getImage())); + } + } + } + + + // Examine dependencies + Set variablesReferences = references.getVariableReferences(); + if(variablesReferences == null || variablesReferences.isEmpty()) + return result; + + // Find all variables that are linked to component with arrows + Set dependencies = ValidationUtils.getDependencies(graph, component); + dependencies.addAll(GLOBAL_VARIABLES); + + // Remove all dependency variables from reference maps + for(String dependency : dependencies) + variablesReferences.remove(dependency); boolean isStock = isStock(graph, component); - StandardIssue noSuchVariableIssue = null; - // Check that all references have corresponding arrows - if (references != null && dependencies != null) { - for (String reference : references) { - if (!dependencies.contains(reference) && !GLOBAL_VARIABLES.contains(reference)) { - Resource variable = null; - if ((variable = ValidationUtils.reach(graph, component, reference)) != null) { - if(isStock) { - /* Stocks do not get incoming dependencies. They are allowed - to have references without arrows */ - } else { - result.add(new StandardIssue(sr.Validations_MissingLinkIssue, component, variable)); - } + ReferenceOption option; + + for(String reference : variablesReferences) { + option = ExpressionUtils.getReferenceOption(configuration, reference); + switch(option) { + case DOES_NOT_EXIST: + result.add(new IssueWithStringContext(sr.Validations_NoSuchVariableIssue, component, reference)); + case CAN_BE_CONNECTED: + if(isStock) { + /* Stocks do not get incoming dependencies. They are allowed + to have references without arrows */ } else { - if (noSuchVariableIssue == null) { - noSuchVariableIssue = new StandardIssue(sr.Validations_NoSuchVariableIssue, component); - result.add(noSuchVariableIssue); - } + result.add(new IssueWithStringContext(sr.Validations_MissingLinkIssue, component, reference)); } - } + break; + case CANNOT_BE_CONNECTED: } } + + + for(Resource expression : references.ranges.keySet()) { + List errors = new ArrayList(); + // RANGES + errors.addAll(ExpressionUtils.examineArrayRanges(graph, configuration, references.ranges.get(expression), references.forIndices.get(expression))); + + // ENUMERATION REFERENCES IN FOR-LOOPS + errors.addAll(ExpressionUtils.examineEnumerationReferences(configuration, references.enumerationReferences.get(expression))); + + for(SyntaxError error : errors) { + Resource type = sr.Validations_RangeIssue; + if(ExpressionField.SYNTAX_WARNING.equals(error.getType())) { + type = sr.Validations_RangeWarning; + } + result.add(new IssueWithStringContext(type, component, error.getMessage())); + } + } + return result; } @@ -177,13 +236,12 @@ public class DependencyFunction { public static String missingLinkIssueDescription(ReadGraph graph, Resource converter, Variable property) throws DatabaseException { - List contexts = IssueUtils.getContextsForProperty(graph, property); + List contexts = IssueWithStringContext.getStringContexts(graph, property); String result = "Missing a link to "; if (contexts.size() > 0) { - Resource component = contexts.get(1); - String name = NameUtils.getSafeName(graph, component); - result = result + name; + result = result + contexts.get(0); } + return result; } @@ -200,14 +258,13 @@ public class DependencyFunction { @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") public static String unusedDependencyIssueDescription(ReadGraph graph, Resource converter, Variable property) throws DatabaseException { - - List contexts = IssueUtils.getContextsForProperty(graph, property); + + List contexts = IssueWithStringContext.getStringContexts(graph, property); String result = "Unused dependency: "; if (contexts.size() > 0) { - Resource component = contexts.get(1); - String name = NameUtils.getSafeName(graph, component); - result = result + name; + result = result + contexts.get(0); } + return result; } @@ -224,49 +281,50 @@ public class DependencyFunction { @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") public static String noSuchVariableIssueDescription(ReadGraph graph, Resource converter, Variable property) throws DatabaseException { - - List contexts = IssueUtils.getContextsForProperty(graph, property); - Resource component = contexts.get(0); - - // Find all variables that are linked to component with arrows - Set dependencies = ValidationUtils.getDependencies(graph, component); - Set references = null; - - // Find all references in equations of component - try { - references = ValidationUtils.getReferences(graph, component); - } catch (SyntaxErrorException e) { - } catch (UnsupportedCharactersException e) { - } catch (UndefinedExpressionException e) { + + List contexts = IssueWithStringContext.getStringContexts(graph, property); + String result = "Refers to unexisting variable "; + if (contexts.size() > 0) { + result = result + contexts.get(0); } + + return result; + } + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") + public static String invalidSheetReferenceIssueDescription(ReadGraph graph, Resource converter, Variable property) + throws DatabaseException { + + List contexts = IssueWithStringContext.getStringContexts(graph, property); + String result = ""; + + if(contexts.size() == 2) + result = contexts.get(0) + ": " + contexts.get(1); + else + result = "Spreadsheet reference error"; - ArrayList result = new ArrayList(); - // Loop all references - if (references != null && dependencies != null) { - for (String reference : references) { - // If dependencies does not contain reference and the reference - // is not reachable from component, add the name - if (!dependencies.contains(reference) && ValidationUtils.reach(graph, component, reference) == null) { - result.add(reference); - } - } - } - if (result.size() == 0) { - return "Missing link"; - } else if (result.size() == 1) { - return "Refers to unexisting variable " + result.get(0); + return result; + } + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") + public static String rangeIssueDescription(ReadGraph graph, Resource converter, Variable property) + throws DatabaseException { + + List contexts = IssueWithStringContext.getStringContexts(graph, property); + if (contexts.size() > 0) { + return contexts.get(0); } else { - StringBuilder sb = new StringBuilder(); - sb.append("Refers to unexisting variables "); - Iterator iterator = result.iterator(); - String reference; - while (iterator.hasNext()) { - reference = iterator.next(); - sb.append(reference); - if (iterator.hasNext()) - sb.append(", "); - } - return sb.toString(); + return "Range Issue"; } } + + @SCLValue(type = "ReadGraph -> Resource -> Variable -> String") + public static String rangeWarningDescription(ReadGraph graph, Resource converter, Variable property) + throws DatabaseException { + return rangeIssueDescription(graph, converter, property); + } + + + + } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java index a027fce7..782fb3cf 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java @@ -47,7 +47,7 @@ public class ExpressionIssueFunction { // Try if there are any errors while parsing the expressions try { - ValidationUtils.getReferences(graph, component); + ValidationUtils.getAllReferences(graph, component); } catch (Exception e) { return Collections.singletonList(new StandardIssue(sr.Validations_ExpressionIssue, component)); } @@ -62,7 +62,7 @@ public class ExpressionIssueFunction { // There should be an error try { - ValidationUtils.getReferences(graph, component); + ValidationUtils.getAllReferences(graph, component); } catch (SyntaxErrorException e) { return SYNTAX_ERROR; } catch (UnsupportedCharactersException e) { diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/Functions.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/Functions.java index d53a9128..eca08108 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/Functions.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/Functions.java @@ -22,8 +22,6 @@ import org.simantics.issues.common.IssueUtils; import org.simantics.layer0.Layer0; import org.simantics.project.ontology.ProjectResource; import org.simantics.scl.reflection.annotations.SCLValue; -import org.simantics.simulation.ontology.SimulationResource; -import org.simantics.structural.stubs.StructuralResource2; import org.simantics.sysdyn.SysdynResource; /** @@ -36,13 +34,7 @@ public class Functions { @SCLValue(type = "ReadGraph -> Resource -> Resource") public static Resource baseRealizationFunction(ReadGraph graph, Resource model) throws DatabaseException { - SysdynResource sr = SysdynResource.getInstance(graph); - if(graph.isInstanceOf(model, sr.SysdynModel)) - return graph.getSingleObject(model, SimulationResource.getInstance(graph).HasConfiguration); - else if (graph.isInheritedFrom(model, sr.Module)) - return graph.getSingleObject(model, StructuralResource2.getInstance(graph).IsDefinedBy); - else return null; - + return model; } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/IssueWithStringContext.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/IssueWithStringContext.java new file mode 100644 index 00000000..a491add4 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/IssueWithStringContext.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2012 Association for Decentralized Information Management in + * Industry THTH ry. + * 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.validation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.utils.ListUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.layer0.variable.Variable; +import org.simantics.issues.common.StandardIssue; +import org.simantics.layer0.Layer0; +import org.simantics.sysdyn.SysdynResource; + +/** + * Issue that can have string contexts. + * + * String contexts are written to a graph like resource contexts and can be accessed + * in issue description functions. This eliminates the need for making complex calculations + * for obtaining values that have already been calculated in the issue source function. + * + * @author Teemu Lempinen + * + */ +public class IssueWithStringContext extends StandardIssue { + + String[] stringContexts; + + /** + * Creates an issue with one resource context and an arbitrary number of string contexts + * @param type + * @param context + * @param stringContexts + */ + public IssueWithStringContext(Resource type, Resource context, String... stringContexts) { + super(type, context); + this.stringContexts = stringContexts; + } + + /** + * Get the string contexts of this issue + * @return string contexts + */ + public String[] getStringContexts() { + return stringContexts; + } + + /** + * Overridden function from StandardResource. This writes a list of string contexts to the database. + */ + public void writeAdditionalContext(WriteGraph graph, Resource issue) throws DatabaseException { + super.writeAdditionalContext(graph, issue); + + // Add possible string contexts to the contexts array + if(stringContexts != null && stringContexts.length > 0) { + SysdynResource SR = SysdynResource.getInstance(graph); + Layer0 L0 = Layer0.getInstance(graph); + List contexts = new ArrayList(); + for(String s : stringContexts) { + Resource r = graph.newResource(); + graph.claim(r, L0.InstanceOf, L0.String); + graph.claimValue(r, s, Bindings.STRING); + contexts.add(r); + } + graph.claim(issue, SR.Validations_Issue_stringContexts, ListUtils.create(graph, L0.List, contexts)); + } + + } + + /** + * Gets string contexts from IssueWithStringContexts + * @param graph ReadGraph + * @param property issue property + * @return String contexts of issue or empty list if none is found + * @throws DatabaseException + */ + public static List getStringContexts(ReadGraph graph, Variable property) throws DatabaseException { + SysdynResource SR = SysdynResource.getInstance(graph); + Variable issueVariable = property.getParent(graph); + Resource issueResource = issueVariable.getRepresents(graph); + Resource list = graph.getPossibleObject(issueResource, SR.Validations_Issue_stringContexts); + if(list == null) { + return Collections.emptyList(); + } else { + List stringResources = ListUtils.toList(graph, list); + ArrayList result = new ArrayList(stringResources.size()); + for(Resource r : stringResources) { + result.add((String)graph.getValue(r, Bindings.STRING)); + } + return result; + } + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/References.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/References.java new file mode 100644 index 00000000..0d9ed3d9 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/References.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2012 Association for Decentralized Information Management in + * Industry THTH ry. + * 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.validation; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.simantics.db.Resource; +import org.simantics.sysdyn.expressionParser.Token; + +/** + * Container for different types of references got from expression parser + * @author Teemu Lempinen + * + */ +public class References { + public HashMap expressions = new HashMap(); + public HashMap>> references = new HashMap>>(); + public HashMap>>> ranges = new HashMap>>>(); + public HashMap>> forIndices = new HashMap>>(); + public HashMap>> enumerationReferences = new HashMap>>(); + public HashMap>> functionReferences = new HashMap>>(); + + + /** + * Get all variable references from all expressions of the variable in a single set + * @return All variable references + */ + public Set getVariableReferences() { + HashSet result = new HashSet(); + for(HashMap> map : references.values()) { + for(String key : map.keySet()) { + if(!map.get(key).isEmpty()) + result.add(key); + } + } + + return result; + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java index 79293b0b..e8f84068 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java @@ -13,20 +13,20 @@ package org.simantics.sysdyn.ui.validation; import java.io.StringReader; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; -import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; -import org.simantics.db.common.request.ObjectsWithType; import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.layer0.Layer0; import org.simantics.sysdyn.SysdynResource; import org.simantics.sysdyn.expressionParser.ExpressionParser; import org.simantics.sysdyn.expressionParser.ParseException; +import org.simantics.sysdyn.expressionParser.Token; import org.simantics.sysdyn.expressionParser.TokenMgrError; /** @@ -35,7 +35,7 @@ import org.simantics.sysdyn.expressionParser.TokenMgrError; * */ public class ValidationUtils { - + /** * Find all variables that are referred to in the expressions of variable r * @param graph ReadGraph @@ -46,8 +46,9 @@ public class ValidationUtils { * @throws UnsupportedCharactersException * @throws UndefinedExpressionException */ - public static HashSet getReferences(ReadGraph graph, Resource r) throws DatabaseException, SyntaxErrorException, UnsupportedCharactersException, UndefinedExpressionException { - HashSet references = new HashSet(); + public static References getAllReferences(ReadGraph graph, Resource r) throws DatabaseException, SyntaxErrorException, UnsupportedCharactersException, UndefinedExpressionException { + References result = new References(); + ExpressionParser parser = new ExpressionParser(new StringReader("")); SysdynResource sr = SysdynResource.getInstance(graph); @@ -65,16 +66,22 @@ public class ValidationUtils { if(value.length() == 0) { // Empty might be allowed - if(graph.isSubrelationOf(statement.getPredicate(), sr.HasEquationOrEmpty)) - return references; - else + if(!graph.isSubrelationOf(statement.getPredicate(), sr.HasEquationOrEmpty)) throw new UndefinedExpressionException(); } parser.ReInit(new StringReader(value)); try { parser.expr(); - references.addAll(parser.getReferences().keySet()); + + HashMap> refs = parser.getReferences(); + result.references.put(expression, refs); + result.ranges.put(expression, parser.getRanges()); + result.forIndices.put(expression, parser.getForIndices()); + result.enumerationReferences.put(expression, parser.getEnumerationReferences()); + result.functionReferences.put(expression, parser.getFunctionCallReferences()); + result.expressions.put(expression, value); + } catch (ParseException e1) { throw new SyntaxErrorException(); } catch (TokenMgrError err) { @@ -82,9 +89,9 @@ public class ValidationUtils { } } } - return references; + return result; } - + /** * Get all expressions of a variable r * @param graph ReadGraph @@ -132,42 +139,4 @@ public class ValidationUtils { return variables; } - /** - * Is reference reachable from variable - * - * @param graph - * @param variable - * @param reference - * @return - * @throws DatabaseException - */ - public static boolean isReachable(ReadGraph graph, Resource variable, String reference) throws DatabaseException { - if(reach(graph, variable, reference) != null) - return true; - else - return false; - } - - /** - * Find a resource starting from variable and using reference path - * - * @param graph ReadGraph - * @param variable starting point - * @param reference path to another variable - * @return found variable or null - * @throws DatabaseException - */ - public static Resource reach(ReadGraph graph, Resource variable, String reference) throws DatabaseException { - Layer0 l0 = Layer0.getInstance(graph); - SysdynResource sr = SysdynResource.getInstance(graph); - Resource configuration = graph.getSingleObject(variable, l0.PartOf); - String varName; - for(Resource var : graph.syncRequest(new ObjectsWithType(configuration, l0.ConsistsOf, sr.Variable))) { - varName = graph.getRelatedValue(var, l0.HasName, Bindings.STRING); - if(varName != null && reference.equals(varName)) - return var; - } - return null; - } - } -- 2.47.1