From b045519ddec0e7ca90b3f02454c594d9623acf32 Mon Sep 17 00:00:00 2001 From: lempinen Date: Wed, 22 May 2013 11:31:52 +0000 Subject: [PATCH] Sensitivity analysis with a better configuration UI. The UI is not perfect, but enables defining variables, indexes and distributions. Multiple parameters are supported. (refs #4239) git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@27438 ac1ea38d-2e2b-0410-8846-a27921b304fc --- .../chart/properties/RVIModifier.java | 24 +- .../chart/properties/RangeComposite.java | 106 ++-- .../properties/VariableProposalProvider.java | 5 +- org.simantics.sysdyn.ontology/graph.tg | Bin 151735 -> 154519 bytes .../graph/PropertyViewpoints.pgraph | 28 +- .../graph/Sysdyn.pgraph | 40 +- .../org/simantics/sysdyn/SysdynResource.java | 162 ++++-- org.simantics.sysdyn.ui/adapters.xml | 2 +- ...sitivityAnalysisExperimentNodeHandler.java | 28 +- .../SensitivityAnalysisExperimentTab.java | 467 ++++++++++++++---- .../DistributionPropertyWidget.java | 118 +++++ .../sensitivity/IDistributionProperties.java | 30 ++ .../sensitivity/IntervalProperties.java | 115 +++++ .../NormalDistributionProperties.java | 158 ++++++ .../sensitivity/ParameterChildRule.java | 50 ++ .../sensitivity/ParameterLabelRule.java | 59 +++ .../ParameterProposalProvider.java | 16 + .../SensitivityDistributionKeys.java | 21 + .../SensitivityRangeHandlerFactory.java | 33 ++ .../UniformDistributionProperties.java | 110 +++++ .../sensitivity/VariableNameModifier.java | 121 +++++ .../SensitivitySeriesPropertyComposite.java | 2 +- .../ui/trend/SysdynRangeHandlerFactory.java | 37 +- org.simantics.sysdyn/adapters.xml | 32 ++ .../SensitivityExperimentParameter.java | 101 ++++ .../adapter/distribution/IDistribution.java | 33 ++ .../sysdyn/adapter/distribution/Interval.java | 111 +++++ .../distribution/NormalDistribution.java | 137 +++++ .../distribution/UniformDistribution.java | 110 +++++ .../sysdyn/manager/SysdynExperiment.java | 4 +- .../SysdynSensitivityAnalysisExperiment.java | 236 ++++----- .../representation/utils/IndexUtils.java | 18 +- .../utils/RepresentationUtils.java | 31 ++ 33 files changed, 2213 insertions(+), 332 deletions(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/DistributionPropertyWidget.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IDistributionProperties.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IntervalProperties.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/NormalDistributionProperties.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterChildRule.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterLabelRule.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterProposalProvider.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityDistributionKeys.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityRangeHandlerFactory.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/UniformDistributionProperties.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/VariableNameModifier.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/SensitivityExperimentParameter.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/distribution/IDistribution.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/distribution/Interval.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/distribution/NormalDistribution.java create mode 100644 org.simantics.sysdyn/src/org/simantics/sysdyn/adapter/distribution/UniformDistribution.java diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java index 81edb2b9..6a863802 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java @@ -23,6 +23,7 @@ import org.simantics.browsing.ui.swt.widgets.impl.TextModifyListenerImpl; import org.simantics.browsing.ui.swt.widgets.impl.TrackedModifyEvent; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; import org.simantics.db.exception.DatabaseException; @@ -39,6 +40,8 @@ public class RVIModifier extends TextModifyListenerImpl { private boolean active; private Control control; + private String rviUri; + private String indexUri; private char[] alphaNumericCharacters = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','å','ä','ö', @@ -51,6 +54,14 @@ public class RVIModifier extends TextModifyListenerImpl { * @param support */ public RVIModifier(Control control, WidgetSupport support) { + this(control, support, JFreeChartResource.URIs.variableRVI, JFreeChartResource.URIs.variableFilter); + } + + + public RVIModifier(Control control, WidgetSupport support, String rviRelationUri, String indexUri) { + this.rviUri = rviRelationUri; + this.indexUri = indexUri; + this.control = control; this.active = true; @@ -101,11 +112,18 @@ public class RVIModifier extends TextModifyListenerImpl { public void applyText(WriteGraph graph, Resource resource, String text) throws DatabaseException { if(active) { text = "/" + text.replace('.', '/'); - JFreeChartResource jfree = JFreeChartResource.getInstance(graph); - graph.claimLiteral(resource, jfree.variableRVI, text, Bindings.STRING); - graph.deny(resource, jfree.variableFilter); + graph.claimLiteral(resource, getRVIRelation(graph), text, Bindings.STRING); + graph.deny(resource, getIndexRelation(graph)); } } + + private Resource getRVIRelation(ReadGraph graph) throws DatabaseException { + return graph.getResource(rviUri); + } + + private Resource getIndexRelation(ReadGraph graph) throws DatabaseException { + return graph.getResource(indexUri); + } public void deactivate() { active = false; diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RangeComposite.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RangeComposite.java index cbb2f51a..b3d95473 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RangeComposite.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RangeComposite.java @@ -13,6 +13,7 @@ package org.simantics.jfreechart.chart.properties; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.Map; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; @@ -41,7 +42,6 @@ import org.simantics.sysdyn.JFreeChartResource; import org.simantics.utils.RunnableWithObject; import org.simantics.utils.datastructures.Triple; import org.simantics.utils.ui.AdaptionUtils; -import org.simantics.utils.ui.ExceptionUtils; /** * Composite for range controls in chart series properties @@ -51,7 +51,7 @@ import org.simantics.utils.ui.ExceptionUtils; public class RangeComposite extends Composite implements Widget { private Composite composite; - + public RangeComposite(Composite parent, ISessionContext context, WidgetSupport support, int style) { super(parent, style); support.register(this); @@ -69,21 +69,21 @@ public class RangeComposite extends Composite implements Widget { RangeHandlerFactory f; try { - f = context.getSession().syncRequest(new Read() { - @Override - public RangeHandlerFactory perform(ReadGraph graph) - throws DatabaseException { - return graph.adapt(series, RangeHandlerFactory.class); - } - }); - + f = context.getSession().syncRequest(new Read() { + @Override + public RangeHandlerFactory perform(ReadGraph graph) + throws DatabaseException { + return graph.adapt(series, RangeHandlerFactory.class); + } + }); + } catch (DatabaseException e) { - //ExceptionUtils.logAndShowError("Insert something intelligent here.", e); - return; + //ExceptionUtils.logAndShowError("Insert something intelligent here.", e); + return; } - + final RangeHandlerFactory factory = f; - + /* * Listen to the enumerations assigned to the variable in this series. * Listener is needed because the user can change the variableRVI for the series @@ -106,6 +106,8 @@ public class RangeComposite extends Composite implements Widget { // Remove all content (even with null result) for(Control child : composite.getChildren()) child.dispose(); + + composite.layout(); if(getObject() == null) return; @@ -117,6 +119,10 @@ public class RangeComposite extends Composite implements Widget { TrackedCombo combo; LinkedHashMap result = (LinkedHashMap)getObject(); Iterator iterator = result.keySet().iterator(); + + // Set the width of the combo + GridLayout gl = (GridLayout)composite.getLayout(); + gl.numColumns = result.size(); // For each array index (enumeration), create a label and a combo int index = 0; @@ -125,7 +131,7 @@ public class RangeComposite extends Composite implements Widget { Composite c = new Composite(composite, SWT.NONE); GridDataFactory.fillDefaults().applyTo(c); GridLayoutFactory.fillDefaults().applyTo(c); - + label = new Label(c, SWT.NONE); label.setText((String)key); GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.END).applyTo(label); @@ -137,22 +143,19 @@ public class RangeComposite extends Composite implements Widget { GridDataFactory.fillDefaults().applyTo(combo.getWidget()); index++; } - - // Set the width of the combo - GridLayout gl = (GridLayout)composite.getLayout(); - gl.numColumns = index; - + // Set input for the combos support.fireInput(context, input); - + /* * Find out if this composite is located in a scrolled composite. * If it is, resize the scrolled composite */ + composite.layout(); Composite previousParent = composite.getParent(); - for(int i = 0; i < 5; i++) { + for(int i = 0; i < 7 && previousParent != null; i++) { + previousParent.layout(); if(previousParent.getParent() instanceof ScrolledComposite) { - previousParent.layout(); ScrolledComposite sc = (ScrolledComposite) previousParent.getParent(); Point size = previousParent.computeSize(SWT.DEFAULT, SWT.DEFAULT); sc.setMinSize(size); @@ -199,18 +202,21 @@ public class RangeComposite extends Composite implements Widget { @Override public String perform(ReadGraph graph, Resource series) throws DatabaseException { - JFreeChartResource jfree = JFreeChartResource.getInstance(graph); - String[] filter = graph.getPossibleRelatedValue(series, jfree.variableFilter, Bindings.STRING_ARRAY); - + String[] filter = graph.getPossibleRelatedValue(series, getIndexRelation(graph), Bindings.STRING_ARRAY); + + /* - * If no filter was found or the index is not applicable, return "All" + * If no filter was found or the index is not applicable, return the first index */ + String result = null; if(filter == null) - return "All"; + result = getFirstIndex(graph, series, index); else if(filter.length < index) - return "All"; + result = getFirstIndex(graph, series, index); else - return filter[index]; + result = filter[index]; + + return result; } } @@ -223,7 +229,7 @@ public class RangeComposite extends Composite implements Widget { * */ private class RangeModifyListener extends ComboModifyListenerImpl { - + private int index, size; /** @@ -238,20 +244,46 @@ public class RangeComposite extends Composite implements Widget { @Override public void applyText(WriteGraph graph, Resource series, String text) throws DatabaseException { - JFreeChartResource jfree = JFreeChartResource.getInstance(graph); - String[] filter = graph.getPossibleRelatedValue(series, jfree.variableFilter, Bindings.STRING_ARRAY); - + Resource filterRelation = getIndexRelation(graph); + String[] filter = graph.getPossibleRelatedValue(series, filterRelation, Bindings.STRING_ARRAY); + // If there is no filter, create a default filter with all indexes "All" if(filter == null) { filter = new String[size]; for(int i = 0; i < filter.length; i++) { - filter[i] = "All"; + filter[i] = getFirstIndex(graph, series, i); } } - + // Modify the filter index filter[index] = text; - graph.claimLiteral(series, jfree.variableFilter, filter, Bindings.STRING_ARRAY); + graph.claimLiteral(series, filterRelation, filter, Bindings.STRING_ARRAY); } } + + protected Resource getIndexRelation(ReadGraph graph) { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + return jfree.variableFilter; + } + + private static String getFirstIndex(ReadGraph graph, Resource series, int index) throws DatabaseException { + RangeHandlerFactory f = graph.adapt(series, RangeHandlerFactory.class); + LinkedHashMap map = graph.syncRequest(f.getRequest(series)); + if(map == null) + return null; + else { + Resource enumeration = null; + Iterator iterator = map.values().iterator(); + for(int i = 0; i <= index && iterator.hasNext(); i++) { + enumeration = iterator.next(); + } + if(enumeration != null) { + Map indexmap = f.getRangeItemFactory(index, enumeration).perform(graph, null); + if(indexmap != null) + return indexmap.values().iterator().next().toString(); + } + } + + return null; + } } diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java index aa19d6de..c3229747 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java @@ -25,7 +25,7 @@ import org.simantics.db.Resource; import org.simantics.db.management.ISessionContext; import org.simantics.db.procedure.Listener; import org.simantics.ui.SimanticsUI; -import org.simantics.ui.utils.AdaptionUtils; +import org.simantics.utils.ui.AdaptionUtils; /** * @@ -67,7 +67,8 @@ public class VariableProposalProvider implements IContentProposalProvider, Widge @SuppressWarnings("unchecked") public IContentProposal[] getProposals(String contents, int position) { if (filterProposals) { - ArrayList list = new ArrayList(); + @SuppressWarnings("rawtypes") + ArrayList list = new ArrayList(); if (compareRVI) { for (ChartVariable proposal : proposals) { if (proposal.getRvi().length() >= contents.length() && proposal.getRvi().substring(0, contents.length()).equalsIgnoreCase(contents)) { diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index 87734e2663aa803cc9f3f1f9c9b7222b11d033bc..66e2ea9e8141a34ad0e972fc6fa54460534b1138 100644 GIT binary patch literal 154519 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%0DUSIjeA7rpKABV>~TIDf$yfA%7n+pY5D( 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 bjxHRRZpzzH=tl|1FsAFc<4}50Zi)UMHeDk} literal 151735 zcmagH37j28_5OYDboX5*`^pXhM3jA#9V8?H0@+Byj=*H*CK;H_gjqrYE{L*P5JpDOviuYQK~x1Og?Rh_D??%Vg?nPSy& z&)U_sTCI+sT9f}N|5r>H?u-nL4fl5D42=zrcCom-vwpNQIMP2fIMOCJ_@D9oPj`P` zXK=KCw7)abBKt^PEsYd{^mSJD55hV$IF8oxe|f%iXlOL5mk;)==|~&-=!Ui7s5K8= z7#{2yh@6eJJ;P{U;#aj=i`TN1;4ND72dkPz_sD3^XlD&7g$_&A#=4$?vH$-O6k?Nc z*J|w>!vM0EbtFipWOK?{XPvdnNdKB1B&K&{m!aWRwOV!&rTVxf!$YTcdPjHOeV08@ z_#PBy7fo5(IdiNZxmtu_kF4$K?I8MFDZQsTe{fxAcmxH-RPjzP1I+H_2iSa zLMDp$N?CjU$jDfyEP5Y`iuX2`$yyx2@s%ui-;}wXwVgq%y50@ThkJU@=%DT5DoR%t zQoUAdeME2)x^igGNawJzLCF?`TSb@v0m&H|9q#Gp(26fh@pu$-mB~Vf`J+!P^>c!WJdBej@Z1F9@=5am4Y!u1tLP4$Gy`X19XLz@AGB#ydS7RPeMzoP#BHUU$ zIEv|6g_7bcQqo3O7O&tMtkv>XC<@~kSv&~W`$N4dXW6f$WP+Z5$MRIciGGh1PeZsl zJ;QzdINQ+h(w@Op5Zl*5Y~EANc_xdXD?4WCdwTs0VJodzZZkT z3VE%RP8`9)8|YxCkT^)f&!oIHw`a5`oE-V~QqmsTux3qXbhy74P3#a%2r1hRhI<7| zcBp2r*k3H&^G6o+51tXS*j_7Tla`I4v0-Kx<6`z2-xXopGK3#oHG6ot2Ptlr{dS8G zdsE&c)`@*XYjBFsS=~R-hwB7Fd`HTel;xc@YdLe68*IC{LU20Hf!z2`)xR2d-CEc9 z`PeVktQ{KZ^vM!@e+fY1!Xd_K_I* z%Hg3ki12RGK9!umngw0i@*ZBQE*0QvTdH>g13e>5&)cA6msa&^GsN0FbZBUx(}Voh zc9nugm?G2&ClL1a#b*t6`VPf}<-3JC2p>+e_9jx$t*c8i{D_pb%Y`mQH;1A_H;i`5 zxz7&>p?HB`#p^(AcPVJ;+~VB&VyG3)I4mUe=|U;u&SfjTI-$me2=fP5bGOEzo4h#G zx+Y^|>ctI6I4?t>YoIj0T&1IZtyss&%H^Gl^!u?;h~}!Zr8Z3pr|LG_Ga#qCgl*g} zT5))JhsMivtt;%7dj8cWNMUQPtZz1x?+p4bT zn~AS`Xt=L~(|=h93$1pPl+CpJ0b8@nWTiG_>piN0EUxTdj}zyY@Hc*uufR1IX&xp0 zvTCC7gvec^*eTm*@%RZR@+yP64N??*wcYvz1RS-O{T^vy8MQ~y^*X2Lea z>o`Y(z~7c&%xJYx+HYi&->#Z$-X$(xFGH5Gm_8sy+#}_36L!A#yHHwupsHC5dpPfe z=5|JUhx_Gj6ocjsOl>=7+)3h1oDZMg)5wxxBC+Jc?p2Nnci0HQ9%L@5wP(b-#bTSc zg51!@hklq;%h@;@l1pcO;GUbyyc9(mYsr-LL6^ZtG}6v>@#-sE{dB4^Yr z8uP~SCoCBlLhp)$gbS$yoG5Wx%lZe-2;D4BLP;?(A4N+zI8F#xf&rYR;1h+5(VlP# z=Ow+EIJK8OI2A8DdItMP0LH%u);q6#+4(I@!XR#0(aVfydUpPdWw@gD;l^{}P#>FJ#n&`E zLEb5HQ*L!wko`akrq3Q2z-yv14))pd+&z=(52_WFJuLPKyx>cd;hrSlSc-9576Qwa zEW1w%+j46#C@+$-A4(DLFJ_O9_Vlji0;x`v?2{sMStb3>J|@bT@4*f4nkp-l>DDZj(|eWJcdp8HE|w4d zPY9JT#y3l~?p_OrM(>ugNoBR_!jau0!nB1wYu9$t<(O{}aWZZ%*YG7$)kgMtQRE^K z22*zV1}UEr4Se~K<~o;+>B5!<#EO5DRM@T>W5~t-j4#A*$_l-3=l_wlYd(uwhMAHZ zj(T@^?oe({tI%>Wp%Kb(c!_ZLTwJ2HtX5=PnBB`ZjPz|7EZ60gEGuR%!#>sN(|eWb zm1=&M==O^9uzoqUv(HKK<``(TV*Y13@=uDZ8I~LNuzX)Bm^2r!BsO5z#&Z@v$bzsx zgnZvZu6xMi(L{I8oF6DURyry!mkqeRltl+t+eN;gc<_9zirZeoGIs-?A0(#n^6;x? zpnRp;UdGacs#w`KP*5xUc9I`2MGf4CWBY{tOx8KRdt~*{Syr|-lwn-(FjpAMYL>07 zV&_9*nt;b;;l)nrTgBp`%2(S11?Yp_K7i)3Xpix{GuwTay+?<3-(?>Z9Kq7LvV6w^{g5;+ zAnsJ9C*N6gJl9-_*Nc3~pM6WBjH?zF`rqCKh1s`Uz4OJDEg!-QylkNWoK0}R7UT*%`r?zfF}`li-XIoSwea|?d`^Vo{H>v- zV{y-g7xdVw^Vdnqm0Xh(gc_mq z;ECXTLJKcNS8>ZjI=EKyi7p*)kt*XB5BKBpsCOurheDhleAt6i6eB3Ui^Aga8OIEc z;k9=)Rpo=ef?Gmy`NU)JSQ*>Gh&|9_w~Nd5DO_>h;^*R+ zIfe>l_~=zb@e$D{P%m>MPX+Im;z?{rnhwuN(b!WWY!g~sJT|&^Y?P)~jQxaox7ObM ze_5}g_42l!W*pnKEV^bRzTR{980vs_ofz~%=T{{{(a%K+@iD;}^VX~#-7s$uPm9AX zw&w9ck*6&hS~k|Z`q-Y~e%|D&_)!sgfv)D98$Xtc3l%MiJ4N1XA)3b`sFqS>xB&y!NPQMvh~lL5nz#jq(JKyckJ<3_ray9*!U>1b~e&v-X7 zW}hc)am!3sDt=oBKNWoz9&`O~e&ZGPA#rZfag%@t?Cbq%yCHPJ5+hrC@e`KbuwDQE z+4;DgkBfbl1=)c7m3Orxao;~H&h1}mvR=8uBT`|;Fs~pIDsK59k+&TD-^CRBzAe7Z z*7VCFS%B9&uMq5qV%kXBdd0KqMlnq5vlsO~lOGgu)3S*w7v6KfcxH{g(ykYG_?L8E zJ~OPZ`GfHZ4CyA(^|RH^lA3Mw(1)ybDzbl%ERy~uUb{O zG9Deu@v^N4w@2kQ7uId0d~&#^*%^oPs5DX~Kz9i-2@e-%M*GlV| z#MvU;!C)HjVzjqMw?KO%AmbGX_f;w|9{1_&>Z$Aaeu_S~LKe9R%Xm? z?8q`NI<;cjF}N7pmdvd}UTJevt`$woO@4?JOkOZFbjH})XtriU$q#7^h=L38G343R zg*fNcrdEuT6&(V?aGn&l77XFdiEs-quP%ylZppybV6b9px}C}60p85!4cAax%+piz z0(pAWK8$MH*}KI)p&yTdWod>dFKyoU=H1S>H419Qx!eL5lid*JGow&=(lvcg*|f}U*cg`MxfERC zT$rx!ZFBT^+#1RI?By$j6B&M~3qM)HPgvUN>+}xbwyfO{27u5%V{kl8iOm21Yz0q?+qo1B9ic6QCgb>%GxY%+1}*RuSB&iH;Y zw((xi@Mzf__mb@W&U1YTHY9ffayy7~99@4k%ddCF4~TI(K1NVJx8yVT1)afFe9ySO z9aZYlQz}e097am9B zhBMrE!T5K&pKr-kZ{A=Z>hmPd{_g7kRV?{fn2pzy{_5m^h&;JRp0WDvAp4sr-4YuQ z_V|Rj&CMhGo2&kUnA)XD-YPqkMK4sId?gC-Q3-v4ghuQkZN7zQ6T>2^PRNXRb$pd5 zPbjKuM|fP4GfBvaEj_##wOu_wLBvUNNr>8dy9mY7{#C0nmW;P*wPMo{qI>3dfg)`eg1ep%hQ%mh#Rh zyl>M!7^T@?T-v4uTi7gYyWL7kv4O&+rqObI$XFCzVe+=AAiK zZV&C{6kcC7nX#Z&;Ei9r7%0b(@w~4Uukb?GF5B6c#gaZX%rBGTNp^|TF=Ra=OgsYH z`OxqNdk~)wqde;=j&n;u6t`lpK%yI0%cb^zI(I?S9n;*L~iyD4UDbf>(urD z1B_Jx@=Y9WtS(AO1$uZjnvr{_rWa5Y;^ z_lha1%gsRe)KAtY5}&kV-{o+^LeJO%SH*o$DEkv6Jm{-dQ8+{SlqUO=>+%KSYaBW> zI?A`1^G^%U49k8oPDNP_)x(8VqHnQ`pSZ#ME_jz|HXayk80p8yaNLP2le20PH{ed6 z-TfhFoZP(MbwFleRU5BkGnWb_F{JrKM*0OIeI7;*gdL>`R-zB;o~9NJGm(D z?ks$mQY*Gv))~M%4R{YYdOYhkp#1Y<9Iy8e<=q7@tu=hwZX`T&K%f34CH!a(-&4!E zTVta=u77m(E8G$u0X07~N0v{(!C^+ngXRiVw~NP-=en-T{TC|t53=&SvU2|*E6)*% zAJHDmvN=_N8ZVZ$V#@5kKKX#6yYH{b6=3@b3;6~X-f8MA#~l&|_g$$mUOtK-cWiQh zDQW(06*zB-ZK}J$sm#*PW@WAyVlGdZ^u+`pqeHJ3h1?Zm0Ng|CMK|7xDL+?u9~0rdvU{g(xG`nVb3{<9Lf1J{rhVW@{F@t#byS}y+!`uB)rl&+G}YY`#X9dw>qRr% zSFt0rSTMQDL%u{D)6^!ft;qR$63Q2ZCgZfj>!om+ELS~WCu31p zuj8vb2-N#H!+vb?+9BL2W2dQJxwHqVeB3p7hge%`#SmmGxWie_mbUn@M!u@3Zei+m z5vCqGhI`YU`v+&~nTbTyyP(zU8|!MCB?~rk%%G#Iu?}ZFpDfllCeNtP#(y`L!$x>- z0nE!UB94q!$bVsQ2m$0$Ljd>PyO+5h}Spur{u7z#NDyp zl@AT_Osew|TR**yXY6$*0*83drp{|-eNETkp7@8?a31f0f6gqA@z}r4>r%a|ko^!x zyeFw2DBa{XQGb1X4*tsx28V~@zdQ|b$no;USpC4NOWYO^YOt?=4g0hMaeHxcebl$F zoDYk$jVI89BmAHk)E}_uVR7?8S^eS4eX+P(<%g%4mrGf4v5fKEa@fLJmbpUtx5VDW zHLDMAkE4K_PyJgG`XM>2LT@SF20kh_%n#+%>psyE+DO!IByPz zjPrILzB$BsTY1bl=at8db55|cGoR&QSBC2n{=J*C|eoFm^(mrERHbI&az0bk7Pd;pYi|k~8!LIjq|In)sT7L-W?j z2Uxo9LqXTq%29WHN675i0b2z zcOI0|{_kKP>0gD_km3I*VplVsP+kAFw!3Z z@DSfa@e-->&Tz=)?+O;&765oociknH&xb=sy;lyasB-E(m-Y3dmtCh)JVC0Q91dBf zR}QNxJA*MDm{y331IS0Xx_F{@EiUlHkISrUsi3C6BhI|j#}Cza4N&W}Njh7+yXxxH zS0G)xi#L;#D%%&3?ap3hdxb+TiWkL__eeMD|0TUhPyL_akolM?o`NSQ_Jgj?fn77Z z#(2C*d5pcGYnB|#un!3}OrRmSR~GFUw&Sd$0>J#nr%;G z#uE;QJcF7z#89PA*+iB%#nK%P(OmH~E%nLA@+J5he^=Px3>Q!5C(fUHB0d<)sEt|m zv+-Y^#yG^9Yw%RZVGsU$p4hWx^A`awX8C#IK0h2HXN@a^WzOPdfDf?zO0irM4r%$a zSh^N4$bBsSvN(9Bi9PAziHsT|6zIend_K>yaUKEs2!$KPesehFFxHDFn|J&Yz*|_pUM%N?Lt40* zVFH%VUc@k8pm3*nJ{Jz@IU{(MEuY6YXHqx=kGnhN*O^9N`4eXG@A3WRj)U2n=M;b&NCBwJV?SfbL{H44fk5F;I^g!T_#H8gfS*)J}k)pFhik_nQ zbC;suN`a*42|4sB;+=DNSXR9Y!0UB>O!(Y)zJhRS`RVGmNGsgy?c>C28EKzPKF%5H zD5zD>V|WqB>r?PNhVM1YGZuZ$H-}PJRW;+3;GK%{d2aMBYj&(?lkt(|@{4ODhYt^p ztwn?rMVWXwoZ!nb~p5_O{`PpPV?T4333dM5C;pqTVZ?=uuT2B})6Rk+uRRDNg$&&%*Bkx@6H z1<%`J(xmVSSNWQ7n3&=a5vGOi@qG(7GsV)-pgx@R^W&-X+w!Ct(S#YMT_exX2eXHz#i4 zTHj3cCU($XdD+{Rg3X*`x`b}5!B=7Vn4-NYdAc+3e!A8@8F!7oZM?o~Z%oBKoX&Z( zhkPS3d?&Sf5>oJ{$eoShL-TS%$d~#%kc-L9P>jgMnJnnE7In^&4+;WKmy)R{(Y0sq zP8Tahn*gmkVNiuPf<&|e}=yp5^Wr69NIIi6?kt~bS9$Ey|;m) zHB!D6%K!HR%hhH8v5Nl6InQtQCRXSkiC(9*{x~G78>rUL6sFo*{)B zvum&L-WKm^u7?AsfV{D-r$F`}DJ-NAyX?mWr^}+#?}e3HF4ViAW*={l$eU>LA*D8V z&+M*h>D0$b1h+M0522(!u7$^B@^K;fzau5%wQ*$^mvHl#ekY22U2XPxl@G3sYb_N4rPlOGg$+&cN( zioCmvAl#U;2c7FD;^H?#c|t;bTnfVP_dtccS>bV4fw$)Majd`zg7{4-7*`FPo#kGf zebZIDUtE*@B$f%`#^|p{| z2_H@BEaqp?@Zl1F{$&@};i*q@Uk?f2E6e%C-|XUXPCzVvB|N)0$Jfa5XuGzx)E!rz zQ#zKTcigo%@ zyorMj;#ciplI#n7`8lHEYwG$;Jm)k?Q7M>$ zS;G56_HkEU=rVpGw6=Bi`!1UDmE*0)#{+HhYjMph`OVrgd2N1At$CHp)zxMxQ{GoPRy`+>a95E&mD!reeMary019DTJi-&D$? zuU6hEMH~Ge!$b-)8tM~{?eCnmmOTtBy(Ql~&ZbrubGPN!a zS4DOv_sH@U)qDv&!1LO5NQHQn9CO zhw^L9s);LJCzAX=0-lae*;^Dhr3Xj@Gx$lbS2(+~l_K+d$4nMZ{qXs}VqYnrf$ADh^hyOoq;2Yxe4+*e!+`Nam=l-{zoAMip6yqN}m#he$57;qs^@x%1j+;FWH zi>h6|;N_!O$ZqH*i6>*>KDCn#pulzjH-zk55xS$(?MNxxB$}yYGUYd*WD^$+CFQ3{ zRZhn;{dIE7JN$hU1%L5Saaz0$HeV3C<<_mo=06}cUvO7~Hut3FKdNyzUldC?zxjF7 zqdK?{wD}t}nlC!re^7vTf}=c_+xCCy{SOBhCL-wOci@q>acWs8-oBPJAun z^8|lZY$l~P`)+*M86vgag2;HUgU##}7s7GhM(6OEpV}1sizlA7o$xPq4(0YHU_8=} z827dj^Cq&aFaFaL#^+ih9Z&ru=I>bZ_4#X?Jmc0SVm@*3dT+vb>L2Mn37?kmsR^Hw z@Ea07+3`lm&qteBK{x+J;CA8%oB#ZvoB!?LR^kVn|1Ci`|C_O9(S7_H0KiK>|L03OxbGoAQ1!#WUi%>T|Vzsc3ct6?v#|K^gjpM-o zN&H~*FAuu;mw^AB_`&8sI_T3%dCctMy#s2b&*zJ9YIpuy?oqnE1iw z$H_?D{Mb)ge@Ogb^J8zLu71{S{XX%7%}AFN?9ca|pZx(xe(LIHe;#pu9FrqIb>E+douBo=k)OK8XMeuu z{Ok`n@>5qo<2~g3I3`Da>b^e@IzQ`!BR_SG&;C5%{Ok`n@>5qob^hU zb$-qlIPz21`0USjoS*#xM}F$+XMeu!{5U2@e(JtI=!2av><@k-KXr}I{(RH<*&lG^ zr>=hX=RW8EM&QU#-S-C`>ksRLBR_SG&;H!&{MQDK{M6O|5$L$KSp3fgj{MZse=GDa zSN@Fs2Si;Mt99#U)2af#IOMkut^e;L;`wx!%)HOc){{`p2GjQamu739ap2}Yz z7dY}$S3moIcjYhc3LN>V`~D+`HeU809QmnheD)tP&Hu^3k)OKy+5gX0{=5hr`Kha) z{YOqL{-*;+e(JvepK*Tn9~}9qYkc+}F)jWbfg?Y4^|Swb)nh#%po^xdlIE{p`w;wf@B=YyEczj_TiB)#oSD zTV!9Kto1JodQ|@=l$Gt*xjxChK3VI(E9g=E52CEBU+jcqvae6p`tJ;SRR8@byCmc* zKL*F-ivt&9C0|tXaLE^zd}81l_M!F_$nQlZm(Mp_7l65@%J{`@CHwf)HU62U?&CwZ z_!kD<`p@{+;78*hT(XZ(UE>dxy2dZ-Tm18b9>u>JWj;RH$0uw2wKyhg{I>_T_-`xO z=jSSv`S@fXpRDl*aZJ|u=LNR-Z!Ou!=bF~|JK~t^vA+*y6vXWFH^v%Hr>gW3rD=*7#?Xy2d{)u*H9K$v*z&C@b?L=ReuU zCu{uEaZL8{!505bL66qYdr?-#m;Hh4(dUQk+eSGej zK0jn1pX}q4eSEORUk{D!<8x2d_}k%_?BkO){_0Zq@u6G%vw|MQzZ7K}e=8i5eSETy zPxkS_7Jpr+AI0Z2QsW`XBBKxJv~T-P4=1F-7S9(8Sx`zQ7w zYY&=m*oFHsB3%Nk8n;~d)zgC?x)oe)^-OMC09c+wu$sI_+@{9yGj_xvOM62VOM8P!dtmiTd(^c()NQSC?LiaP@x3~* z)|d7Ms(8}g8LmB^zhL!Cd(^c()NSFKXY&V5Sli=%2VLt+d$%F1FK)!qpt0tZVP*sR=5y!3T3_0GLlsZjJK43z@qyJZ?NQhEP`8CW$i@dvSlinvu-2FMu!ks1dpO6V z^#WF1+M}-RasNRMtUc}zWNmN9z*=A0!x+?G90Q$P`g7DJNB(1-e>o(w#y==<<>8dqYoCJesbxT>nAw!AMN~`LL!%b zIUm50e~I&N0*UPV1CIQQ6F=Ga2ORkqIsbe}vCu@J0e{keq;QWU` zBCCJ9z>yzwWcg%&$m-|*3y%EAx%rQPMArVF7C7=Frm~z5sH5Ec4-OdvMeU36FWUz< z{|mkZ_fv;~53kx2FIoGyThKM0oDZm@EbYy!SlRJBwY?n!NA2z7;y(`^-27|#JZK~E-dKmPE1BaX?^QDM1=e`Q zo~6Eb&>5@sx=L?N0Pp4Eu|Bzsmt7k;ibq}JAyx}*Xn(T%O4j~t7dWaN+08YVBI-&F2GK{yD$c<21h9 zf0DJoJRiYPeCiq>v0Lz{F8#%t(|85zlQkaKBYxB`^GRLf(T_Exy7U)p{pEVup^7ip z7qZ6ZehSw55}&%p=laAPsV?!sQGCpS_FtaAkbQix)|dFyH9qHm`%0Ji;3z)&Xz>g9 z$z^=WKRAj{UE?EG3u{Vsi4TtAzskku{UO=M2Wx%lKXr}I^}21POMGw?e;XH{{U?|4 zC12nuK6Qv09r|x|~m&R`H~NWM2ua_2qu0 zUB#33TCP3rCt&qUd(^c()NM6gd(ecnz54=deQB>z#gq1M4oBkyt6$oquI-_2D|hWd z6W049t~cmfU)sYSr!4JtRV?>oU=C64|F|E5?S72=UEQ@uFWI*T*7$6%`R|Ik-pIDz zu-7&J<<S>y416}?;^66>Fp&ha!~#*eNK>~&s`H?H?=11M;vxy&Ue)NU9`dPR6yTlJR z|8Ik?{`@!KXA(cy{7(hl{J#VvhR&zV7ufty1YP}%-9#IaA8h`g23`I6W8kL~KiK>~ z3cC3p0{=SkgU$c_psSy;n;28nAF%nq7j*L@R`bck4>tdIg0BAj+u&a%ez5uP3%dEg z41OZ+mn?H6KTRrhwPt`0iKWpCx+ASOZc0l(8lvJ!PC*k)AU4lt@qc$1XqT zqW@qWujGfi=I0XVScAI1#23Eg}m%8?U1$3MfI^K-?9XRq+SAP$5oRjL$PYE3PsjL5F=np#o z8v;ju>gqoU`UCj!`2nk6@=4u~_x{8Wj{MZU|GSAF9QmnxKlcFNA8_QS?)}&!EI&s< z1FK*1PhH1*B=m13@xkhs@lyBkzv=wD;}{(Ise3>7B<(-_;K)zi`@fO+!I7W3_kX?e zXI#7B$WPt-x#yMr$#_2ij{MZU|Etc=_~6J--TUu#e#Qq!e(K)OJyr9w1CGIwpSt&d z9{LowAKvNadpx{gtuOOUUFW+S`e&SfT;Rx0z4X@^@6*no2af#Iz5i3rpTQ4~{M5BS zUC=*S`Lq89j{MYpd|YF6eP{oM9~}9qd;iCspZ9Cv$WPt-?{I$J|9~Su_0pgH0s8IE z|NFp^pSt>=h5j+;|6Sn7PhI`bK)=oTaW6!U{M6Ntd!Ob5qoj+-|-Kl)0J{M6NdBlMe`AAKc9e(LJK4*Cb3AAKc9e(LIfKXkN_m7kBv&M8@6 zU%fT3*3Zra-{AajD%ty?NB--b|7`fl-VcuaA3*=g=l>boCzr3!YMTaDzq~%9UcNrd zHb6%X?EG9G*!*8C^)F%nx)A+X2}TZV{HK?!{aX`Q>&y7Bb^W7G*4M*5fz>blqptm1 z4gDI|zg2-}&xAAU0K3HkalI|6)p;s=}m(4d=tHu$}XA8h`E zgKqwV!0$=?VDs-Abo1{6es|&rn}4sMn}1L6Wr-hb{#}A@{++?^O8j8+?+|qJzZU$? z#1A(Ac0o7)tH75gez5ts4!Zfb0KX&ggU!EX(ACe_%}WwL*!&v@UH#cc;ENMK*!_Uo{Kmu&Hvdn9 zZhpL$Y~mUc;l2#vUKV2b=$)pqu|e@cP6LHve~m zZvJnB&r1AY^WPVA^M3=3eK+b4*!=ef-TYqxk0pMv`MLj-%lvfR4IWMWVDo=I=;~+P z=1AfPoBz(BtH0|r;Nip%Hb3ug$l4#=OEz&25RDgX{yT!Me%5WSP5fZ<-x_rFcYOpr zl=#8sza{AAzY#o`_`&A?V9?di*v&PGA8dZy3s6^o*R|k*#1A(ARY5oZmEbcHKiK@Z zR#G?ryTPX?ez5u98FcgWdeNWw!RE(aP2K$1i<_$xKiK@|2HpJJA66xPu=#nul65`c zTyCyR{9yCrT%c}#uFp>52b-Vkg{<+hwwis3A8dZi1$FZyx6R(f4>mu>PTl+%OLIly z2b&*W>gLCBvnTO`&3|gp&3_X3w8RfK|H(mDKVvr$BRapp=07&*>hC%Rd`jX6n}2E0 z&3`ob4T&FY{)Is|{{k@j81)Bi{`o;S|Dj-vIr4+ee^}7f&)Cfq6F=DebAoPu=JSNa z4>tcnL05m*f#Bm4KiK^H1>OAnf{#o5VDs+~bo1{C#+*ds1)G1jpqqbJFy<`sgU!$Q zWL^KS1uswhVDrB^=;q%Jye#p9%|9#X=HCj8H4^m)Z2rxIZvKtISVNH?Z2nDxu71XD zE=l}g^G^%9`n#rr7bkwO`6mV4{Jei%l=#8sZwKA{Eil%2)PJz~i=dl72jd)w{9yIh z{~L7m*Z%`PD)EEO|IeVC|7Gxzi63nK7lLm7zk_kkMg0Mr|M{Ss|1aPp5tefL03OxH?e0# z{Q;Z*CqY+#{l{SJS&<)X{vQP0{ND%9PW)i=KNNKHKL|b~@q^9(-JqNQJK)zRez5uP z3%dEg0X{hKgU$cdpqu|*F!so3ykPTxG3e(10vLN}mZf57JdXwc1nD;Rrv?1zr7&-P}9z zgUx?K(9Mrn&DSMu&|`ocO`!e_PPi&)ChK5$gRcI12fSV42b=$t zpqn4}rp;F+ez5ubf^PmE@V1E`Z2sP$tDmu(+a!Ll`Bwzp{D{@uI`M>)9~*S_Gj?te2pqn4DnllqW*!=qkUH$d_z%vp**!+73-Tc^#o6{3N*!_ybg&;4Oa;s=`_=Q4Hk^Zc5e_`&AKxkKIjv%r%QKiK?O zd(_R3wbh)M_`&Aq{E&6Ln9Jsb#1A$<#!B7%7)x_};s=|*9dz@zz~d4>SpBts23`HN zBf;H?A8h`=2VMP)-9($wehW7L?}BcA#A>2XksoaSUk6?NwfW#?;s=}miJ+^Wv73#= z4>tefK{r2QHH*X#Hb3Txy7>{SnJ0d*`F|L6_1BI7XNezd{)dCEe#UNgC4R8^9|*en z5vy5G{9yBcJLu*|ET%KcKhXT&2)g+h>%Xy|Z2o(LZhpr4PvQrg|BFFazr^}?;s=}m zZXA=#^M$b+|4RH|^WPbC^Rq72xF0Xs{GSZE`5Eiw#1A(A?Ljv`W4)C4!REg;=;mjv z7ZX3&{I>+%{EYRF#1A(AjX^g*W4)00!REg%=<2T>4#qhhbn`P7_V{T1fX%-;=;mjvXA(cy{Cz<;KV$ti@q^8OTF}kUSied9VDq0Gbn`RT z(}^E!{^Nsge#ZKB;s=|5dC<+zSWhK>u=$q+-TaL8Wa0;#e?idA&saZC{9yAR8Fcm6 z=7Jwj{9yCrdP`mXjNSNI;s=}mh@hLFb$^=p!RF_FLw+>e?=g1c$JOUgY8waE^~BHX ziT)+{FGSqw;2*j8-v$5B@%`W*IHvx6$J8HjO#NZU)W7GL`a_PXKj@hH1CFVGtK!w1?XWe&v9O~Prxy9*}&XueNFz&1Xvr_Vc` z$-aRtpBPKTXp3z5L@cSV z`D9zveLlgKPvo-knM#*@emdb#RV?`gTR!1!e6rGId|(^jR2+Z8>CETH9do>QxO~DZ ztoekOZ2jl@yuD&+4{YsC!STnO&iJ>ve6nv~%O}PXG1?+qJ`qdmYd+Z)b)QeLA z-t%2P;T0Z_>*bcgOSb+q-rFmd_Q2L&8^>>RI^*LyZtIhM16w{ZmWa_7+46~4QeX4Q zwy67jf-RrOW#g@tF8Mq+;kQ&Q`2<@&;cdLR(q(*L8(#~@Z*n^G`9{Ya?>R1?@Cs`_ z;U!!Dna{H;miEBb9>(3+;B>}c@AApMfi0gHOT=i4Z23eisjvBDThx6%!In?tvT;_W zOFq{nJXW#f6Kwf}w=r7jGCr`4uYuzcr!$|!jyc{lT|VIz)_lTCw*E7pYb&lX-jHL) z8+7sD71nt0lB0NQDwh6(t^YZW2b|9MXSn&{Siv?w$W6rPd&Fp)Z1aWKWV_z6ZR&o$ zz&2l)tH$Y-F7wr&@al?XzQ8tL@bYhXX?z(k*v6aTc%{>s-;QIBzt80tUSZ8Iykw0p z_qV+j%Y3bH%y>O69=yUD4_im?-UmgUSW*~FFA_$hKeO0V9N)j#>q}+ z{F4xWGWO3|IA-p^HvhF^#Hc@fVI##vC?HdoRIMG z70Y@6+j@YPe-lmP%lN@Me)(H!$2y(s;TXrv$8wi%c!f3J@RBvYtcPV4%ls{M%y>t; zc<>5qJb1}byd@P&|H0P(f8%(u(;0se;_LiztYDiz!GK<3HTx7hYk_FT7-p zFY|R+#WG*>95dcr7Y|-xjR!9|iZ`cX=|9-||1ypbbvonEMtq$wjumY4h1^7pzDJC< z$u?hzO}6=B+tmGhfo;ApSB*m|UFPfc2_Ias%oo_^3ts+BJ&iBp1>1OE!tsGl=X@RD znB(8y-@uknj3r{UMYen*mekjLmhrUxdJ%NX zCvw@?DQSPlgmC%k0qKl8a= z#j>Bg$}!_@>*B#HtnuI_NAb3)So#mP{{I!nTRWZcXSw;|Siv?w$W6rPd&Fp)Z1aWK zWZO^JHg&)L!8Tu*tHxH9F6)2Ggtw?z<_m1|1uy@mq{f%=f^EFd<9IWtbG|lp%<*sH z@(Zu9<`-VF#+UtMTdu;t@VIG*Bk#>ch9t|!bL*ybO(ju^R!7-NYTeIeU=KwD&659|wdzaGH(66_x* zK*L&SOsaHQ4-*rfP_e8Bu;l|@{*6wJFXIQ>_@Be^IHxn8I0wzoe6$e{apd_wyuzAq zp0Ci=FZpg&Ec4fN%y?qkF_wtAzEI!R1KJ|ndfw#;bU>|W^lJ_6Koba6q-UeBdJYVZ41d};5IilsfUwFhtUFn+W>9($(ObLo%T`(71a+Iy&CX%B4e z!CO3tA8n7vp6T^m`lI$9sNzd|_g5_Kfvr7wi|^t`+vBlkdOer^sJ-u0@uj_QS1j#; ztvz^)Z{kPW`&PpAdcE{V?cG???O0sg%eKVaP-`S~NV{{EYS*O$*%x{T*;l$Gs^ z*RlNVThFZT8SO@lzLB+mEwoj94&AvO{d*6NKN48`*ZLUvuB1PoMVaPwNKi|FrY(3jP#+w7-ZQG5h9qj5T89hOGT9 zt^|M5_4h-8qyBy(Y5(K!X#1#JcxKz4(f5ckHgeSd9j^Twp@XCLF_+4+emIBP{)chw z8FeFOU%Za7kZa?xKCZ z&l{or^YHIe`0@Ni!n}TZo$J#x&p*#x?_h1O)&^rvm8CzopiJBUDULmJ{ycL%fVIBF zLynXs-c2svH*oBkx%bTcg0;TH!#ScX@or4`1{eP)IQGo*!!zf@^Fs;WpD^Yv^7H)h z^|^jL^L+M97bkpC#j-zNnDF@t zzb)anCVXzfZ%O#g3BM`fHzs^e!e=MEA>s82pOx^sghvw|uDDo*e4Ux_+Jte=>img+ zFyS=`4!lx(PpYZC0S0>y+pLIXL+!vlP_Yq^P5o4?oW2_N#fAaA#)`&6Ih%wfP zG1iDN)`&6Ih%wfPF;=qXTlUYsii^{rgNvT<_o#A?r?=9xl8aNyzh^}+bp8F4#e$N{ z-#^K}A5xrJ)tC5SjbE(5@f#{#;)5+dz2GQ5S>qoA9X~prVi|aO!b=lAx?(wB7bnay zEW-6+1ph+gm@NLnz{L{iV>m{v;;>4Wc=HmToA8{34^4P>!iQ9xasFSQ@WB-q+e1Gv z;e8zM3mshShyG(9sn>D52Y&SLmA$%T{d*ES1-9|;S?cBQ1C?02xcJkc@9dcUK@6); z-Z9}FD$clmUz_l25=I}Rc-uLC73$+h?{71nYg<>k%+JPdJ#7Nr^QI-2>#J6>wkPXz zdJ>Ogz6bflb)fkd9D}3wC!=f%^ta>RsrVu5{=)u&Ek4dAa_ju^@4<2Z@qB5*?@ahz z3161*yAys-!tYJ^@`QQ4_5FEY!hAj9^{W!TI$>VVz5m*T-w&RIpWVy-hHKR^|JF&w z|B6}u7LfDH-yZVJ`S6Unjd(`FGZV(ziTqqIJ|6EsJa3ustc15onCscs=lSUw=SIXh zmm=o*>-{)aBi=3HB){^v1AYCy693*M>w3msEb*0>C%iP_1qmOLF!s7A9`?G3vDZbc zpMCvu{aO5FiC(T()64a2d~D)BF5%-7J}Kc-67EU3FX2@QpPulVgx4lK zlJL5OHzfSVgx`|zd4XFume;2bCVW%EHz#~c!XE;UtIh|xK4VSl`61k{xX$wjZ0}EU z{kAGyUeAN|{mAB@p<~TQ@fwb~zZZ^o1>>Bt_e0?o9*^rc_ilK}8c*I21?N~dZO%2% zudLFg|6PvR9?ns%FFMfLgO}a0@xe=0zqALm_Rv=T-%4k^{6CJ-UjA>#)Nzf}_UIKZ z$44*Ow@23Y*cSh0m+H(n+4B8I@XJnTd)TAw{a?%t+3tTZmk~3UUPma;(iiCZ{x932 zuJ8Y{FJ$ds9mylx`@dLg`HPh<`T9q~FI3F+mVdZoWxl~S-|*(YsdO1H*v5<4`O{8kzJKkQh&T^QhA~ z-k&&TzJKhP`j1?`;T6_=!%MdDbO*Nc1#RU&taO>rA2??G@4NW$3Tu3L$x-}ADwcQ; zJ7&D^xp?pjYdm<#QM`vLmVALNU!30uozD0VAik~-_;9~KFInTu`TdQGW&XbInDM^m;=wDd@!%y#@xEHI#Jkrq<9)@&gI8GN!Ap+f zeYs-E2iWq#^Y=?mXZ$ZBzOD!4j%@1zxsDjQh!|su7=0nzdO%xbTMy`q=zcvwxAlOv zkbeR7ry&1p@$aVKdlLS9!gnY9xrFaZ__GP$neb;4{&d2hO8Aore*#>NueLe*|8ejn zjOS|n%Y1`1zwN#??~c-E#8_^_wk@xJk@=?kD<-%l&Tg0VbpQq)wm-;xwyB5dO6XyAeculN<_WSWKuPwLY zNBg%+V2k(Bgc%Qh(;4q0`1ufGJs$og>-^+f1igth)4m?ZTjS@$!6$y!1xJ3c`L_yQ zU9b6Wugund8C&N=`(*LC-fyYell5{lcqf#%FpoT+$UC6FR|GG)i8bB60X!k-`NrUP z;uzx=zt1=8D>FZivlGEL;paxgj|85?fx|Un}coqTchl{#1A$<>w?YCd?8kpk5^#JXW6Fphxviu;(A8dZ+2W*?O4;&NwqgBYzTmfbjQzfrz1R6!7aaM)=4bv8+v1mXqkKV6+45KR$>KAA@2=XDd|d|i z>x-=Uf;R_izOt7CTfW#g`hC7UGhdD+U+=2gm-7*vUy3$<68_D>Hh$&{`=s@s`Q*N- ze%1w>pZS6o<>M9D>X)%BUu7SxJ?85jh&KuQHP`NWCHw0)^(oNTV&8lT`X$c)mXf`n zy7ylUR()6aF9Pp`fAx9-wtQnO)NMZgRr+K4!YfMl<|z&-i0n6^L0VO=O_I3gx{7hVrc*5e)Fvr=gjZ938O6XzX`1S*#YpM z4KDNFejRvy!t1~~-)BK5>-Vw3502`Ct-k8M{$SN#c|8C&|4PK8?&lwD z{Z)NRIR9n7*Cg@47O#VN)O|d##Z%qK!ua{YoWUSHrS9(0SRx{rr7r150DV2js_ zc+`FWz!pz+9}jCPiU+oMD-e&mj|aARs{43YV-{~$)CXIh2t9iejD0wJ3XC;i z`93wU)~|8Dg&v*HV9kGOa`Vr@CKHCnfQ~77uOb)cyJdTRhc$yc4VX>`U$$b;*{WluLg! zzpQ^xnXd=Sc*+lfPe44)FV_q9eC@yN_nxtrNBU4;>rcw1-}Ey}f7zbg4df8rfGyF$aI_r{ce9FFk=+^#s;a>v3 z??2eu!`cwNT)+9Afo=V&J_YLwb1m1;#rW5+cXFxoo;Y9RnD<|f<-VC*_9ribuKmdo zi+*iiS=*ETElk=c`}P+&X8Vq%eX?&KZ0#c!{nmcTQTs=^_GiF&x{BZ247PKbo%`1h~ky+%v`&G5QuUVv}utQZD_b zm+_3t_M-K?kJ}HwiTL11-`nZ;K?g_r>zw`#=-^1l99uqk{Q}$mwR>R8C-ps`m)9T3 zH`sJs`$RAEE!R8f7GL!;-*P=h8>aJm1=jXexA?02_IGjP`x@$d{(4{=@AtqvSN*T! zyb<>6gL>INdET&7<(K<)u=NMm+MK$t54Q2B?#H`hRiAD1JvWWVSY%rdDVP3eJn-A~ z^4?J2_^aU8IQ=VuwLZT;lE13zzdSDi+w&A0i|+HWE&M+JVC{eVDEO)Se1I(v;IAPul+> z?r*NbI3EJf!p|n?j{{~1O9|hL+lb-|L96vfX)&!Kbk{} zw<)|ERq-v5F%A2q}x+xet=dA_l&d~&7pwMN9;Z=hR$QZD^be-PXHgM05B z9Q6lm{ZU=}gSPUC@Ruy-FWCINKcnu?XRx)WdU<}!>o1(wOJRIZ%|Ex)eSe@^e^mGVDNtYc zi_f6G=Q~TrdHVm_`|j{KuPS}bj3oCINP!T-heC*K%aUf)o!E)&IEf*#L+k{I31KXa z?TO_XF(cWMjY;Sb2%(qId+#;$-h17pt>~SlE-YpFz3;j0+-oV=3GDOxWA!{^&wJ0g zr`=w@Z)81)SP9)>_!?p8BKGB*V$1L8R$f%T$iMj2(DxMB^+w2RRq>_&1%=uF4U zya)F2*l6iG9-#YpIJz$HoQlVrkYD3BTkQOYEYiI{36}b-=u)4FZFx$429DZ~#;pHf zkLVg(y3;TEK=;_umA=B?L3i;$Jb9@APr}Df|)ok4JlWmdPvFl^@1N!M6Su{u}te0zcaWv9o8%220oZLHF&! z(XBl8e}z9J@_@a(QA^kIK=<+-UCaA@A`jThJ0E#T*Ybe9JV)2^ey8NIJ_CDs=NTNy z1KrDWbS)2c+T~|#S;swQtTd*t8q-#dsSDVZH*Jfs<@a%8&JI0XWJJ?DIRi&i|h(|GD@M9OVc0`5j&7{}uRc zdGoxekm6q|`gcGFcIQWhIg7uC_Hhv3zmLx^@Nx0Leg(0U&;2^VCxU+x>8#(Nd;7@$ z^T1!AKHx|nvDWu9V5e^_z5_@4h_yaouaEpc4g8`HIMPR~_5B3c=^Md!;7A{_)(7nM zk^jemU-SV-`iQl@9|1dk*efXjNBW4hK47np{67r*q7OLIN38Yz0NCjp!gt_EAFP3II;(HZ;zvEdp-j5QLkma@(%CH@pp7rpH2b&#rU}LS!HlkKA?N~j_%6mCE$NiVh^zAUuo&u9?-o$ zN7weeFp&rB<#i%2=~^DJm*?nO-V4C*;&nQBfxW!54UXi2?&UeUmiPQb9`FM?4x_#f%BDSaLpud&#NsZBOL>T?J#*|I$+7HhrB5d>f z_n8)z>$ijmRUW7h! z3EE-d3X7i!%({G~!7l%zrvpC_`L(>Oft@_i3cz08c8j$<^4t6(?*Rczec7SpfmQ(a z@-DYn%Ok&*hnP4l^5BEFkMSb5{Du1(T)02{eH6Z5g%4uj>Dz$eLxf>Ngzp3F#)Hei zzcs;I0zcmeUI1?YB=jA^|DUz^6Ts*z+qrhVG}KR)RfG|{2*Y<`XOGOU@2xP)`(6p& z95CM(Pyo&?hyF7RZ{gxVm-QpCe?Fdd=AwiS?C7KwE>v{t-vs$5Kz@erkR^Bza0kA_ zR^pbkfS-Zy$V=?nCv6k{MWEjm-zlH`w)}E!z!wB8<-IY%8v+)8fxW-rEsO@b_zUd( zS@cxU&sX%5fzMNz{;h|+<6z(246k4pPw*0ZdzS*Q3t034dwov<{ai(-{BuH?sPw*Co0$uzA_WnH?^g%^u zJO>n}zx^tn;1%rR30~qTo_zs}K47o!N#HLjI_0Ac`uYgl1iN^`cVZt;P@uax+TAk zC)SY8UWp&-zQYnfU>`rOH}4VX;t#O*=SI-aRCLA<{gRU>^6#$V2i^j)iywH29lyjc z3%G^y&@VW;$U{Ht%LBXxVkeJ%2(aT9dFb~X7W;v{{f`I#>59(sxC`XD{uaIx`*@{Y6pFL(vJc#7`0t?Tlz*a%Cu|e!>Nk8R_VGeqVjpk# z8e!NX`CUArTk`98qU@Z#QeTcwFno1%i6^j+CwL1h0$uzA_WnH@5DZy$V=?w311@&TO>bt zWPSkMl3&NOJ<;Em;MRa8p1?ky;4KsaUHk*~{yiG>yrQ$d@5C;i%uDR!311@&R>^PUN!^lP#WVlcME_qB z{O5of&pfb;XCA!y{|F9>^927a zV5twlzCM6A|I#*Mt5OMQ8c_SYhh>k%~8X1-p2Im)P5j{v!XwfTjF?pfKfq zU&#ZnU?&f}#F4!31uXUhd;1>>{_iR}<>3$eGi4fsC-mh$>ez*0WIzI?!&|8}5@f56_q!=Qgl(OEvDk-V=3EcOF?`wxNttBOwfUx9pAKJb;;#~-nY zFl>)7bQAmXgKT179?&gx-JgCr(f_4@r96Ord4M>pKAc z4=Xz5e@Mj(wh{Yy!Iua_7qO2QWJ!J(FX$4wj@JhheIE!|;sxyE1>XGo16}L~_V(`w z{e6ngc)eF)`tu$YFYpR>@d7V#6t8y&EbaYW3RB)KN*;IxJ9*$Gj^w>FV6h+A+dl>V zcPKjLzg@*2z7qTRe;~oIJ)%Q5u`dtE7J05bpj+s=Jl>Y*e`~-}9>Bglz?*+dpo>4i z-k&DuZ&q}c$D0(Ue{WRr1Fv8gKkyPq@q0tSQXa2YnDSny1Fv8gKkyPq@q0nDSnv*GMCwPgYcs?Ou(Fg4HT?_ulD>~)hpyCPJ1iN^` zcVZt;?hE}qaW`E@)oW_kOlAANj;Cjypu0(*bKn@1n!`A;y|`!^1Jt)erY zXrrE=`rwq?5y5pC4q7FJN@dWnz#=ws<^mNKcjQ#k_{+8Iq6TTDsc(VL~ zeLUf7gprTfkH64O?DXZ@fZ?;lEz}R29G3V2`}i?FI|E((0rvjjd%mpbjNgM4ru=Il z&$Vyx3U=`WFR|Cp`uMgc-Yh?jVS9w3o4DmH$g(>esB@euUojmXoNAflWEb=Z;nDRC%dEgc7sMDIe|3Ua1uXSz9_;HI^5#MJ7_rT-4Ee=BVDH}*pm!=d<9W8iwD+DWp5PVi z;t5`2?+@$WSpkbaV6Tt;|2-6)^3PQ9gl&RdJP{jWA5Y{Z_Vo{C8DaPyVc16O+c#_> z_VzOV9RW-GzI%eRfF=IGKEB}1-!0I^UtsSq$G0;So$v9fxW+%fqtT* zv%KJIr0;kYU-0IMU3|ey?D%EAj=tMr(Fg4HZ3X`dMehNIEFWLkCfLQ7_JHo=i@d}> zzVJ1|utoB_{s+1xzwVEhCHhgvyuGw%Nx%|MVDB&MzDAh!kMdkR zp7O9K47nJGx&e4=#>AT__+ANHew$i`l2yy0rv5PEXnWU1zked@%mMeSJ(u)#^)rs znBcP$e3t~Dm|)nU<;&e4rBCjDXgrtjeFCR@Y51}LxLZX;MoM% z6I@B~{S&-3!RIGJI6?zM_B2{W4(bkA4xb zv?pNSp1_;?d7w-E2KM#)V$gr4=xk3vRk#QEC-@v^`y=oQcI^qg#NNK8z&{RH^Z|Q) z7lHmGMW_59;^W#AY$NvViN0t|TY!Cgf-K38dd&NO)Gc(~o_>(%`+mR@FJK=p@aDc3 z=wd&xxBo)WzpLns*PO!i=Rfdq@dB@47ccM0sHcRY+^qiK)29!dHh?V|LXxu zc>w$J0B`PVfiC_4dw({9{#8Y1{Lm-4cu4#BGSXc9z?&m>@nio5?D%Co{Zhc957_J5 z0RAs3I^*{Rd|dos8?lccebJb@fqlFnOY*z=3td9j@%ntC?{fi5ynwww;LUwD(8YdW zZ~rLhpHXzi3w^fpU*h#Cq`7#3H%IK^1zuvuFY)?hz@iV>>pLI(pHOth>*M&ic)>Pe zA20f%F?9p`ctMurUu4@mbO~L@>tl(&j|MF90`~rZH}{c17yE&|{pW%HVMS-WKBO@H z`5-n%;ssvfC|(~3So8sVee1#henqGJ_u=E>1>1;yyy%O@v<2A53$i4?ix+eW zUB~OaiN5y)Eb#*N@d9t|-GMIl1AF_|f&MN#d2tw*)Nl0`~C&Z|=>3 zF7^X^`_BRWO^SXp@EaATKX1Ut#S6TGUA(|c9L4MP0gFChuWv2*U#IAl|5|)pykHx# zj~9K>n6?1>ctMurckzNQq3d|PCeiom1iva^i65|!A9!=G40Q1a*!wdA`YRNj@q4+# z^zUW(xcGrr@N)DYXBb}MD1I*uSjOX*C`@@TR`S3r*vSJgaU}0W0gJpBDolAVQ1ZYl z*vSJgaU}2g0ZTl9eLM!i|2##f{O981$``Q{ya)bw;5*8O*q0~r68rLJS!m2yYRp(@ zOkaTg_z&BNz5Q*#&k0z@o0|ic@(1?i58m9f16}+D_Wlll{wzgj`9D)(`u_}kT=|1n zu!}EviM@SGfuA0*=mYlp`ayr1qEr4;@p18mZNxr)^hIOZ0_@`rS(4wiALtUgZa+^+ z^gTJjPYPJ#2kheq-rP-rF8%;}fBHawqM|c?H!4j3o`8>wA9w}3_<@%=ir?b{7Ja~8 zUkUs-C_3do4j&gk*e2N3Pxwyki&!LGibY>9n+L0)3-Kg&{M#!h3}4(#g>Y$5jcwgDdqSo*L1 z37!sE>JPB5Kj6(x1-keP?EPI0dQ;KqZzI7EPVl6{tUthB-r2x)MQ8n~DNOnM@Nwk@ zUcs)sz)S4o2iqbH+ae6xh<&`yH`v!7*p{0Jbnze9+q(+%y^2ozuEhs_Ey?gdVkOv> zH_C$8mpAefdwUssjp-}px&8sVCBN<;s)>H+YB?VIpN0Qv53rLjV<_o1e=GY_VE_In zj73}H@c!^znXj_C~TZCa7v5Q~se1m;`fo-{~16}+F_V%6y z`U4c5_U*vO)fdD{Fv>v63S~>|>kIM{dwUssjp-}px%vX#l3&-Cs}lY94_L|**q0}G zbN35$@dw!Zb9d11tLXIS$^>7L;Oz>tJb}Hu(}A}sI?MBNg(?3&__*=}uV7c6;3f9) zfo&0nZ4riT#8G)tz8gPaTkf(zm+@n(!n9`#KF%KS3U>B@m)Of+3Vdn65>H_7|J^{p zMA0e#-uSrsf!Gs!|5#QU({_z%8?diGkWK9C2XqTv*N=N8`ZotG^#j<~5AfzL4s`Jc z*!y!A&@WPS`g37|HzoK2g;_s97FcuL;EFz3p5c~Q9-Ne2= zA{M#zfiCd|_VGCl^mU3(f6m3n)gP1%vA2h1sWEi{`}zV|lHc_w&?R(TU(QMNtqoY} z3$U**;LVK$y4Vlw?dNzitmrJCA%*GBAU-bM;1%rR4PIg&KbFrxz|wyE6{b9nfleNH z1v`1*C645k0v7v$z5OSFzo_Vx-;0lnKYS(j@n>u_rf(Y4Heg>KkWK8{FLVoCmj`q@ zdu6=t4p_@Nw}2uV5EH@De+Fq`j;RSo8sV zeQdv-ica}w;{&^vWcVMp68rek7maBPu#XpHN&ZF1e^-2mE}`pqVNT-glXzi_c39#C z?BfOA+&uza><9Mtp9uPyiq3d-C`^CYXSsNRSFnp0c!|A!*552((Fg4HvApi4=#+m3 zJ}zFcjo8PFzGzHafPK6mOY*yTL6^{VybwocpTz6*fF)kQK3YC1X zyGPjSbNTtM`-pDs^>pix$JSquZG1eo@$}fn+hbcE9^3Nr*p{cqzp(bfXU+%tZg<-2 z-}i3olgGBcc>GrIFIDkE+YxN#N&m0}X-Jp*RcM16CO9+K2Rii&c9;Th9Ue z{(|L!zRI^Z(t&+@V}DJ$Zg0T8y*avTZ|pDH6+hc=TaeefJ;ChnH2ty|bMHaZIoFOb z`!AKBeOZJVKTSt$BMe`NUHS31#x-{O9i6d==vJTWzgsvSLEfTwqdcC2|5<-q@d4kG z4F4le#4f(<>m$sv1ij@f@IM3J5lhJry)s`x*$Lg22X*BNN*~(?u(yZu@=D(i6a4Q9 z{z8I3p5XT-_-zS(ZGxYh;HM_|aS5JD@U;ovk>GnL_}m2dCiv_GbG*>;;drJo%MRGp zf5tYK3-N4YJQZ&J?*#uW!QW2sXA=CL81wFwmQP!VBYOZH=KQPW?+N~&1ph6;e@*aT zfPZJ+uT51g#J@3EWE20V#gt9_ON%L+_~#Z=HnHnJ5RaBWD@=P4GfyY}Q-c4P;QvbS zAAqgBBAeLROWDNEUdkqR_EI*nvzM}owY|SrnD+fP!M{#0%FO2{|1T2!Q($K=WfMDl zDVx~YOWDNEUdkqR_EI*nw)e*hQ$NNqPbWs5jqndJ9())5OT+RK&jB9*rYz$B0G`5k z<^^{7+rNYF-w#;o-}e#>*^Xc6b4nk1iSZGAltt|HF)y&oFZw`pSme3i@toC1S;S5s^8!10qVLNAi@q-<_=^d~7~$&+d5Pz&KFT6?`j{6u z()am*Mc?NVj5(a+7drY#uaCUMb50@5tNZ-2y7JctZ@GS{`r_x7W z;yJ62vWT5N<^_)Qy(3`J_x1$8Ex~V9`p8Q>XZ2AQvD3%Az>&VU1T6aAoZvSl_>D>* zd5Pz&KFT6?`j{6u()WgdMc?Zaj5Uel7y4_JKJpUJS$&j6?DR1&u*)y);WYt^yjLgq zRSABj(nnt6IjfJdh@C#>1&;K+B4E+?@&vyu!7o+%$V)tD^-&hF)5pBPk-nD%Ec#xY z;1?zMg-RcJiRY|7$|839m=`$G_kw^$-}4jvyaYd2=_4=koYhBJ#7-ac0!R9u6R_yJ zIl<3P@UxUY@)FNkeUwG)^f51Rr0XZ2AQvD3%Az>&V20v3HwOz@2feuC0RUg9~ckFtoJKIR3E^gTXc z(RV|FAD7_ADt+W7p0oNWi`eO7Uf@XIV*(a^k52HT68uP|kG#ZlRv%>%JAKRx9O-*R zz@qQr34U0DuUGoWOFU=wQ5Lb&$GpIizUu-OeGg6W;RHWK=_4=koYhBJ#7-ac0!R7| z1uXgwCU`c%GfE$MiRY|7$|839m=`$GcOYQVw?Dzt37%5=$V)tD^-&hF)5pBPk-lcY zqOXzQ2Pb$^=_4=koYhBJ#7-ac0!RAl0gJv`g7+nOLg^zf@toC1S;S5s^8!cu_698a zu1#XZ2AQvD3%Az>&VOfJNWV z1eX*1Af=DI#B)|3Wf41l%nKapyCz`K_rL^So!|#3edHycv-&8D*y&?l;7H$&fJNU` z3BG@V@2B*Umw3+Vqby>lk9mP3efJGm^j(?YD-yh2=_4=koYhBJ#7-ac0!R9`1uXh5 zPw;&be3{ZmUg9~ckFtoJKIR3E^lc4T^leG-r3t=7=_4=koYhBJ#7-ac0!RAp6>!T> z@EzFie=h;vtmyYP*wfp9FHY#dj^6%9&@M{oz@Gj+&@NE)O$NLDU)n;t$FxOb>eiUL zfW7>00B^*{?SGt(?;8ScJqO=M1D1H754;xfyB_~D@A>$Q0N;fFAzSbe@RRVJvd+iH z$?vy#0QBqdowCnUbn0Ipu;^bGu;@P**y(3pVy&Op>8C7Wt)JNGr)>J@=%?fRaKNH} zFksQ&2ki7SFR|87?DSI>v6D~#iJgARE~)Zk{4_p4!L(h|DGS)kXMOBdxM*{0ZQP0Dlg+Tk-ec`|1RD0nHH}C{b~5lyreG!z8U|o z#OG|~A8Ejmz4ug@`YF@J>mC-L1$ocFck*@wd4>1JSn`4%<-Z%SvnRv%yW(>^*(6v}bZTYevp3$Kep`ay zn&7u2_{|A^Q-a@^;MXMhl?i@Hf}fw@XD9e+3BD=8D8nc|k4f;O6a1(IKQh56!${u4 z6a26QU!UOX5_~YhGYQ_EV9uXa{b;Qu_^JfoKf(7)@D&NZPl7imcte8EPcX+*ZQpQ$ zG44h5!2}N^xIe+&30|FGjvrb-#|Mq?o?y0TO=th8G5a@-llIxVBB8S{(ERuw;aq|N zHNEgZ3I1z>|D51ICHS`q{!M~^o#6jW@TU^|@dSS)!5>cWhZ6k01SkDZA?Ys)uTS`2 zo8VU`_*Dr``Un1Ybrj!cCioc%eqw^B6I@R4l?hJTll(TSst<+B6aJ*V6}BeyOJgi| zlaxHUE242B;ZN$D%mo#{%(XP;+(={AXUK5l6KVMm;B(h@5L#LDp~Wv)8$4bYt-vgS$$VFgBIRC zC^8RN$!5-3v6M;m=_#Lf5NYL!-StLwYR^7HKA5dtmz8%mq3ayT8ql`2 zpG&j(*IqLJ<6AM}=oYkUbi=lJjD^_x#Pn1p+h3lTu6TR9B+va5d&|~d<-*QmaN%He z{9tFfE88h30`2aMk7ut}-H73RhJc^{l!p8?WxKj#tLB zowM0NBz0xwo}F18Wz#rNZB|$wca!Y(qxVm_d@Q9l<6Q2_OyQjodBMr8{I#DJrQgY? zpBkl~?9)$*(oYmLa6cCfYGXiVjxHL5HQDRzYhn-MqyVe0D_p+gF~Ls5DrY%UQXGYQPegm)N!ZD|}o( zpO12<&vhql90cXB?Qhzbw!irFKkM}NhiKIHM?qbej^1wpk0;8F-KoQEe~?^HZ3(5i zpNNg!W!5=< z5zn${FtAh`4lubM$gv|1KM>jN+ol>AT!6nD1}cM|F|1$S)RI$zg5R);gb zOK=p0D}Zz8X{8z7*s3mqy&5PpDT!)X&7!wok^mz3FUfe|?ni zqY2-eHMRXMf^JV8m@3;kkYBGQx7{M>_$zAFsm;yibY)w8x-rK2z20oM z`|9JBiRxIHbmY#eW(pGL+P!C=^n>6j_FAr;W%S0yUMm+I=p)-qAWzv;o8DJxlxb;f z=!ac_w7o%0&W^&lkLlR7e_YV*DIDV{hLN_{ima12%}h2b2z6p?%aetC#-6FE$+bN_ z2M!$QZdUh|Yg5&+W_P`@yXT5#rGZVpmZu0`q1fFE@Kgti08bZi_JR^FsE;8~wJC`d zMLomirYO@kAs3?BOe6Bmy!{5@bj|w7n1o_im`fS?YkN|fa8sJ_#5Cc?G~uzLV)@0@ z=2X2gn_Wb~g-2|#w)M0s{+d2DZn5~(z*j10tGXYxjA50$i2w?QLjX%|k{q*Y{>RChMY zjag(mEDR^iLop-ce3U~A<~b;o#ZDUJ&mNItrudrYfbc9+jR6_=&zCqQiS3ux$ERg^ z@B+cNr;?-Vwxf^7c%dXN--c#Y9&fUupd)VEB&jDPWk$*cLRnxkM-BsO<{*hf3oVrPp(d4icYBMwB#aE)+ zEbnBwv@H|zOOFpTB3Xq^XB}pCZ3ubZZ;IJhb{sh~62gwPUD?{J zvkn2-R6W~S!4hw((wMBHT~x;7e49bxRILi}Sx2JdR&whJ_blxV3haW?8ZUwqxnlcd zqdtYv2kp%)D!nDvFNbSc2fF{AW;)rZA87gzI8RK_iZ-7?C5>hy+AL{)MQgR!xTI}X z4^}!QZe3Y%rImo$QY|}oT~?Ioqp_6*Hp?6);>9kB-_uHi{s&2tM*O!UA+R(Q>xUEMkwM$s?!qQ|zSM%-2P>aU!80v^y=-+khkQd-t61+Wxfl z?}E-eimndZV1B{c8ZB5`%`)};YACS_rza*lr@KrwHw{x3LEEttwhJ^>`Mk;{T4I zEwV-FRmesnY?Od3xVh-qd#puaosXz|OXNN~t>!MzKMISA*HLaeqSJJCtj`de3`OP= z&Xt6?E0xV%c9n=`vUk{ zQ~+P`>0gf0zv$Dy5T$=s(3ZGPIO}hH|I>LpZ-ISU~j_El?G1?`)M zP@Yi)UB$6tMRbrO=|@aD$6N^Q+-LH4W%$x*wRB~{l$BEPKNZD==UejE8m+urdCq0h zKOFIY(5HVON`J4QS!kv5pcBRzcf~5YC#~a>>b~lf*?h1*aQn5ERd5)>*}0k9P93OV zFBlat+g;s{Z&sAdm8`SoAfk!wh}JilCG9QG=HJaoFq=#5Sc8n$4*;8mR@=6z`q*AT zv!m6vnFlO@X8GQ>aiTsw4rtaBOGc+>suNXNX0|>~d~Cb2Jh30^gVw`-=)@6G6g_!^ zwgf6?wzOPje_(3j)h59&UL8%?wx-Cbt;}y%7rV33#%|f4KuWDr8As{J>I9|2&Ruql8|EM? z6ffeQZ`bCvo+@a$S>A1%-M6!j1G2n1AUZ2)C9ZGKRehF2-*V@Bt67A#PR&jtjLzNYtGU+)6Q%9a~C;Xbw~Oi_V%iE75l@!aiDgR_b|ICf~PX zjI&=aE%P{KUJfZPefnqCVJH;FG%HU@ZJ&ayjQ_0hqPsKBkYaA^-ZkVP5v#NVJT;Mg zT&*B$cXJ9GG?^6pjEv~TR<5l({IM$iPk_qj1CYrnI6BG3V*mOl0?WR<74{~LD>J#H@vtG6iA3YDms<1cDPD@q^^|7((MzcHH zj3JDbgJz+GxUgtWPfku$QA%ik?9E&VvI*?AY1i5AT(g_sa5Mt5fnqCb{vusH3m+6@ zb~>hayPb}L*$8Ueha-kPsP3;2uC@yHpBjKKm*#N(JzRq*yew@Wrf*!e;MV50%7odf zzOq_5utqji3g#%{%mwpYQl70edclkh&w|-iU6^^>Y;%0JW|>XbJh*MLGFIJHMSWmQ z;aVM=y@U;nB2{h`;y|~YnXDN$G_Hk>=&%Ymiu-3`S6A)vHbq0X;l^ylZq^sfPTvXF zmiL#tC$LG@y>%z$K+J9tqbTlFrtl!4oM8qYG+MKzlwkrpU7n~Olo1X)ScL})-*K{Q zo*gJRYN(4$e}IwfcSx~JhB>6%PbiBvO;pT&Y2n^R6+2ej>A7ROYIA#~ff2JjfgZJR zFO$&{kzls%u)||`I;CgYjw8LSyo-&cIXyOpeR8bH&>+hbv(0K#_a8j`bKMoEb{xKS z9&Dc5jb29FRN1t*Ua0#om>oga9iQ*(<$2LfNgP->keG^>@XCaXQAE^Lx;tBbnGxVmQzdaPZT zzfH}qyzPJ?t0}y)taucUQ zGGC3x0GN5t$ec61nH^Dj=F{&MrJpY7?QP9{xD~XgbJ7fw;>YINI1cb|1IN&6Xrr`W zP083yF$hhrU)O7yyZ}32XxiBZ+AU@YQ__QC>LF7Ql>4NN{7s%(PgQHv_30)C((&r9 zU6lro2xNQQjNNN}mcYs}j$R9ff(9PBhdN;QVW4m8YuY>2I!tF#4hn&->Bj{PpOp>() zXQ0So&PDxFhZjjqhNit`X;yz=MPH3>mgA|NZQwY@pDD_HQ>^B=vcmo#XZO&G*)J>R z)E>KHR!cc7YB1bD-gJ{3ILc|1YfW_R`^2o^+xWDMN3>N~>=JNt5AV7l+Ge zW^*U#B^t{!&SoFK*`rL+)!f3AEo(@fM=yO6y*3b~BLXpl{Jl1_`+uImENHysH@cY} zcEcXt9nJqjn(=ikhOiHyIu_X^!4dup!muurOL=ZCIxjzZ!b$_XF!N_QVgS|>JxE3@oVn3sIAt*h*yNN@Hjb*nvnx@~GdPolR0CIKv-BuBM#z7`TOMuT=FJ_%XD!ENmri50-poTP z+7oAbexq(S+tsL}=kb9yeHQwdOg4ohuQJH@9$(;jJ9 zzE&X!m~t3{!)lN-kS$xcXV{%#^RV45gl)z$u)#)(8oqf~Hd~*@ln!;GEVspF2b+1( zkjC~@#`dBRW%;XK^JF@UG?~0=M4ndDw#j2{uJ7x1~@%82T(uAquDl+_28LIe->bq#e59$uMcJv|tcM^G7G6&2IVeeu04N`Hw@ ze{q!l0zq#q?85;x2YOC)aTI-9icg-9Ir`E|*#9+!=al>}lwDMAh~cf3TR#i#c@p|{ zoMx-Z^|ar?En@H1R0+LmpN;4c^W3OBpY6-}SyB4a1>MwQo=-3*;2SuE`R5bxC}ekr zJ9yZukUm|v%=k$LaI&=AmbqlP%{$$B^Eklm4ud%ibA9hkJ`7Kc!ti)OH!=)%yK9Sm z_@h&N!~_u}^Aw15KSx9XQ#{A;PYi;&C2?2Qg(KEOo6VGn3!=x0`D#J*NJ&69A+siR z9%a|*@pLNoh=);XIUlo{nU{2+=BX=xc+2U+!$s}U?zH9~ChV?k9_q{HaFl*f&oC%7)WacP4@Tj~d2?PHv^h3~p?q zjwy(_!^>sR&I^hrTaYQbn^YbSOSW!oE5Vm#B! z_Il)96_d=N$g%2*xh6i)x~=zBYGPVn=&am{Y4UKgORkLXuE+$7)pnm`R@L@eAAo8~ z+9e5(YN3G6ol*(>=}n`Gedyg8Nl}5bCY!q)DEg?Y-)SGmRO9MkDdu_b4Yt6lm7efrfA|5bwC-(nWd$)cS{H!P}STC3wS zTLq8s;IQ_=)Agw;7etuMnIoTajVouaZRFOd$meoS)(u%F9x<(Fdu#OrJcGj;3KxaV z`qZ>3*hT(tB))HJ~jk%h>inEHC8dIemL?cl6tfnPgr`I7}Ox`zEPC6 z`{-?p(k~PAjCSm6sI>+s@<_3#T?`+&P6Tl(8{PlH?xx(kp?U1S%$iO~;53sfINs<3Z=j%Svxlhh z>7iVb5g$2sMG^8e4R^v}m|ORWMRo%Lt00QEQ)!uUl2vkbXBT3rEWN7Vu%pazMocz4 z%mmw<5EwuCkiVPtBD2V$b21smT_Nm19E5n2(XGH`zzNgY%;?1K)y#N(3R|=MkDH=y zf0xr8A)(f>(D6LKOX60lB(366)r?%r%x{URi@ye#Hy1ZtsnZ}Jo~ zM0v_$^Z1;{d2@>*9q|^rsj&Ev7#6al&YQpdT5a;;>4a}c@WWg1aN0bljpk|NcoavO zl9@%}CS-A!f177!iyMh?{gLVQgj$TK6A86j@Buz_XC|K;<;m&!e7V)5#@l+~aSUOWeAL;K#O%RjL!Hlrk!rO%7Al?U9U|v`lhoztEh;FK6|n z1?MtsQvYDaQr=ldcPBHJ#b#g%u9(XcYs1Jb?S9%ld8NAZY4@m=?!gbTyS{rA1T(}1 z4QIGuEjGhlpxnh#@~3GD`BMemlE-6GwNmGFv1_^{vAYmAwlJ=h$8q|^`Ixzc3npIE ziRucEOmuE@nq-c)a2H3mJEn_0c#??Snrv-i&w-s4MDMO-YbN;w*(@(8PFB15^pic{ zF)uU0#W(YM6FeM+?v`@`61uZ1cpD9OAYd~yf#z8uUyih}2WrmEupx--I8{N11%qe^ zvaDl!1rF`9Nj6JWml-!Nc!8KVxAP}U43D-)%AXV!z=^(K%%uX?Rjv@O8(J7z+fX;( zN+~d&u9P?ypql;*`k~EYRH#+Uj?MF>+VjO1Cc^lxBKccvF3@>3i_cFubH}81@wzzs zV$<<^@i}wMZv&tEZ{dDyOF8tAF7Qa1?@ES>J2;}a|5L80=l&=rIRk(1;*|Scl>Tc$ z&&~1ONoGrLG)u90n)+f`@-+|t(?Q2hFSxOV+-Og0toM*&RA0W8SJ+p8J~b=$3ZirbU)@Wou@3^j+PBiP(;k ze$Es$OYm&bW2&GdXUXI&@;~xjjo~BrgD6Pf^}(Bq(!b-=za6E2L(toD!G#c;OcL1# zb0?RTiHDuW_ao0aT~y}D#R%J&>5b!{69F<}I}s@QxoQ?+l33we8Yy@#e~cK&MKTsm z7Q3hCI6R|}OJo$;c#`?*jBBM_bPSyPs*loFq9}b)(2Y5{m4Ul5mCjKaQn&LW2Qh<# zyD+Bqp_1d^ao@CguZF}85isX;jG%w`(40ir)du5!nPjskHVbCp+9VrAyEb(kr)a$H zFGp8qSFg=HewW&V5=(VR3sx{eZIT#~U9)XOpxkkH*^quBW$-68h|x=<@dca(UAfM4-n zH!$w;xRd~pc(t?Vww-k7Rzw@sa#x=R+@`Y)xK!z!MjH^r_u~uCpO}OmgWkC$e9u-H zW_olaO)@DGzoMqI73|~0#fQ4>?-viMtv?MHT1*2J%32oQ~HWyud1swE5IVVeO1-kC{5{KT}@XXSxr~Vk?`ty ztEr_OMKwL@BE2nDl)|gw;IO#1*lSJ=FOn95dla5o<$Cz;K?9 zO9H%e&9_!c<1lqX31q5SO5rUcbsQt1vtDE_lKRd%_ui;VzspzkTcULHGOq{l;L(g( z`q0Jg?F&=)c$Wmwpn*NC2L zIr^&=_)IbDkZT_9JusS9$~w?+Vv1WM`ZvlrvR=aP-%*ok*sCK?U*RM0a!C&_z{tJS zlU@>Kd67?lVU+$nLC+~n;DXa6Zbx>`aBmOSD{E#)M5e%IL&VIX%yu<*{kWEvJmY>$ zj8+Wxa<eZODA)l_>xz=uymhy7F+ViR^_fy;9(}}h6WLV3tjZP05%&YkY%~lZ zl41W2yw+$PuBh3#6>+-Xzw#V=x{r|~>+8w}y0RgR9J%L2G3Psqz(B;}zSLO~G(caACqxLw$ETHwNhx8Pj zYPk9@MOb7RmiP}>)zEh9`8mGX_6&_jOxK%PXRRvx57=MW&SB9#Uv5}=ZIxrQXR!{(ehr1)1x%RGOrLh>LVt!tw8`2ayT zv`CkA>#>p^-n0u99aHIVPk5+WwJpTQoG-XuXPYr$%C%xA}tSFi03yB67A2?9HUf0jU8SLS1p zy!(J!!@eZk?QQVU9W-p-;2z~jNz<|oUD-zIN6lffY_a%0C_E<=wt4JEVTOys`$2ad+O5@d;);;K->bD;MtIwfN=)mTAzRMg}qt7Po7sX8fXia>~?Z#kGXAg zl}D>+j1y#^nlSrRJLTm%+*9Lj4|c0jAkwFXXDe6|d%=wcQ0H9}UdX_81)h#$>dL2W zsosa)MP4SOdu1{@ZyA{4*of|SZPws*V=zjG0Y?edsTL9 zb#a5Xat4zxC+T316VC1A9-@OajUgrc!ougg2^vh!}eBXfYU173)ZR8(|3n}ZR z$#94`VyMC(g%PL0Q#7_(%n=S9!dry9Fek@9eTNS-p5)%@i~qu#=t-`u=Hss(&XdkeWL(ZH@V<^;Z>r_5MH<3a_w z)R-aL=IcIKu9&cyClO77uUuye+y1|C<+{*g z94YSh^%bUfkT-1Vt|V&2c1v%8E`+Oc)?@GG|8Kp=rZSeX@>Gqzpd|*wsrjqNIIcsM zIhk0)QW#h5(vBAL9F#l$Ht&p+ewwd)u`IqV%MwpV$8-w5z)d%#>iNnrR$$n`>r3^} zA#XGFb>Wa6nGSPT3$H)fg9keJ#4gVQ%yl&MtVStXT^Px@2g|N?yL8a*j+8$);9SZT zXtu*tPGcpW`D$c`4ufjmSSqva%J|isaGeu1`C;h@a!4PF(g%DO(I2IoD=)7sm`2c5 z#Z1Ds2fgHoLyI0;%<@g%oS__fcEDB{b+Xm@cawPY5uR1Upunfi8Bp$P*rCL0(?&|Lv-~pl^M|peH z4hy4d-GJRk&tmpNY!I=qa62I)jO&RWDF8Kp^)EBYt&Ym3Q_zfaBov6dNDODCAc9Xv z9vIFyQ^N4;vxX@SCtWhr-zzIY)~$O+_MGYKkhzfKCb4%Dt~a-v9hZ4F9Tz-fvlWSV z?o4JW2)EG13B^9VjT%j;gaH^w-L_CGFLW=a5BAouIfqRFJROLep7I7%sb{Pd5Ne(bMODP( zxO>doP0V3qHa&^H%;(8!C=h8G9oWS46}p2_a8`9^m*CaS8)2;0ZVR$D>N~PgL=d&V zwqmKNP8+4m!>PJd)QxJoBU2yxDziB=+Zjz5;R6_IqpKAUST^pcUV~W*I!hGSH8s>H zhj0|UE*nkA(tX`Es$!@4Di*!kBzKB%y*qCTJgH)?KQgtKy=bjo!$88H;hMr8RtP9c z+DM4%Svf0)y9Q}3o4;=P{zi2!^Bs(uQAlpNqI}GKETplsL#3O-)0?=oPm6<%Qc2cl z#Rd8#?s7;p4;?zxcev|N|KY=j?VK^&kCRBm*$2sQYxH&T;4XJk6zk)CtXD+o<^cMp z7PDWJ###nx+pKKwgQyzbD5`Ybl!03#g-!>@>ZXHSsGX^8ZA6BvId(@aVrS=3yjrg3 z60A|+-`^$21qkTRn`o)C@WUzRL)xxmk8dhkmL8*x*7O6mKOFfF~@Qbd=z)^$v}%*!_t=PoJZsW$E0 z=R}!x&QL|+XOG=Ea|P=R<(vs<>ffhCG~TnyUwsjxX#C`{Yy7xmxvSE+t2$-mY9Bu) zY|Ah7G|~6bW7qd#$#Pn(59Oqlnd>`Cpo|33^uc4-^nS^5(#Fcf1ga&r_M#f{KA|qz zXb7U?y~nQO-A2cJwI5aWT|!;1YdunLId(PgkSuppL34E;^4>1Q#jdU+e#isLbBW;*1{?aaW5CzB3p-%U|u3{Ox=&UGO}FPUg8jSuO*;G>UHexTLzJ0X zJIYH9 zmsV!b3GjD7noGQ@<@~nPUOvFvo5ct%DZkiBGD5-$^D<_&f32;T<5j6qzSbJ9L5lz4 zihNtV2`2FBLB!dPM+eb6iTsdj@m4u1FuZMYSHtCOTZ*eqQ}PaGW3Xgxm*;i=p$Bck zCVs>C)2#z{#{WxOY>=6@q$Px!xUEGM1`|$eNlE~3lWehX$|qg^a@YcFX*uzT-!nww za*}dO$&)9yRKF{{4(f*pTC6pkoyoV(uVm%fWaV=92OvU1VP-sVHHr$Z?o~l#?Z83HaRWzFUr$ zq(w$8d0Hjm2tOA|@>28-!S}^XTeRd#*K~l+$A`b$-2Qj`;eS5HM0~Qv+yNoJgT<^L ze6g$MCwBaNWjnFsKNa6kz(?~FJN|;DJN}b^m*R80)kj{9DO+ROM!d}OABXR>VX?(* z;lxfq8yt1ES~_X?YyWc8>$J92`BSNjn|S*Yieh#dE-;NXs?rc!aT>b^IliRZ?*H{O zCXW0!=_%uWhV_SkEXPw@Y%E#re5^?=w3q_m_mf(5^qFG+ZlzC=4kYeVOfzqjZUzFR z86C%_y3Lj`5>H|KIL;Z>MwD9`_GAF1F}E$VEr>%15~ZoNd(rcC7N%)0(VfMeq=6=# z#ZpO(I}7O`^J!;cn!WBUPLRA&XCVzfuR06qNb|?R?F&MUh70}p@Mqdju+4(amUy|v z(!_z6TFf!;4){2JHd|uHe@BZoKbxZCXE#Ia___H?({+5W|- zSs(jk;tT)R$noyJe>@$E8~I&O8vHYz^RY}O?)<+nnJ_)UypsuOlgG_7nJ_)W5reEW zr{mOQV!pON&qU$0`P%;NnJCDtCLgu^f5Ak7y@DG^*@B5rvONMjc49Y>%OnC=^RtyY zeh$3Ej-Nfud=mx7&(=-4<7W?}Cj#k2fwDE7w*50E3ZIX9l-5PK6vZE6Y2_-Ul}Bm$ zDO{Iq|J?s|9Jit4;Ovcm{C{za#FTJ(iv*Qjyo)!Jm$pd2`PKg2%9LQfmMJ5A{**0P zpve;xmsag>6>Y;?Eg{m3_U)LX7s&3ozBvq!Hixy)#C>gcn2`AGJJ2OL(*cl<+74*@6;A zzoNJ>W4Z&;|>d0*r(0=%DXEi8gvV|&EFv>fYJ^+KxwlA7XJM+{;t|&9lLN8XttMo zwvTSPc%C;i?7{9w{2N$2Z=b=rdCLK(lKS^MK>gDK&RVd9wyARNz)UTwv-4zT)F1uX2B z>l9$o%fN2_b~_1lYL^2PXZ9s;VZ;yUX1hw{H6f1#aH2;>&x?s5BkbP6udA zS-{b_({hcoo5l1%2Pouf0k`6W+!mg0lqXo+J6u-Exyk_s10d^vA*+NgSq15|yffQ5T0yRiz*j{&#*wVSTnUg~Q%J#q$%=ILv+744>n-f%lQ zUR;2%{q9ED9z5^Y&Zw3RvcnF@`QE5_b+Y-%Mt1^nNfqys3Vz!g16sT3^=;v{4C`;YpB8Ih?6!ivT2@u~5S_2*JBOsh55&6B&- z+uKX-jtsB0Yv6sr2iA4qcR=}6QHOS>2tm7HJ|>XsI(TPv?U~4lzjFxia|q=zY=^Hc zo`XY$`rgV~+-$5M3%A6x@pT>hvi|PU;K<0xV5tie(_;VdNMC<{wx+kcf2cS(G&0oH zi_4*X-9rOIrIFGwdHRZ_k>Qa+Bn}SumHGxpm^jcoG+gW(LgK*CAfya1aj37azn5wK zgCo8D{ryZE85kZJ9w;KQuYaV!I560?rmwp=RO;;;?n7b;Uk8iO1Nu;DU~s5}#NOfF zfxdwuCJqdjipYj%Uq)cZa6k0GxWR$JQvV1NhXzMr2`uXC9x0WE2Ybmm*gG^-9KzF_ z{gebSgv9=V;$UB|Bo>Eydxu3P5=#AyM1OY?-okxw76%ZWK4?dzipAa$Bo@0zhD)W= z2(%-y)DQWCNE{j*?(OfBw7$OKk%2y>4Gi=R_tQ);_V=M&n7eOaV6eAj6Nd%{hLKi8 zw2Fg1jh;`i9QsBE`ym14(AzbHau^;O9w}yH*_wgw-hs-R!LD9d*xNT!9AP8rE23zS zR2m*eM249(GBAW>r1cjO#QqY~;LivO4p~bBeWk&Eq!ot-`XL7e4v$L3!Qmk!q5ww* zdsz#*OC!AlrJ+9N9xnBks03n1dMU3o&<~L%CJhY~Q8#+K5#C}^l2~1aOSG;u$YQ1` zy#xJ&!?dn{aA0I;P=pQl_Z5+XY{QHL^$ZUW4j~G?0H{)ZkeMNl11!X0Xaj*2)l$Zg zxmX%PV42$6J5r>$J`@jXlqA9YemXFKcn+0NqSg?4O56I2sG>-7eEqO!qz^@cw87#) zN%Hph^|LBM7fP^?ZZZ7BC_N)@q=fcEdA%dO#Q_mm>_-FWW1vvP1O12xSy8+DP;-#l zJA~-5jzCrszLyxlV($QI5UZ0F*2j2B>QJwUg%d-xfB{-F)QwU>lNeBSh9N>lL9TuT zW~A6P(p^H05e_u&zJ7^&FM|RmsO=j>Gl4yHO*(;6FRMN)N^kGru*mK&!mVM+iCQus z($LP(0;SB*%=;v(R7z7#io+!s4;6jAgZO#6HBQ5Xw%^AFQLyv z)uKs*rM}*NR&c}}{aI16_x2ALhp7leIATmf$AQYg+^8vN2^3iz9%2%j7%Qb|vHh@| z9h=lobU$d5eFG!eaCd+I2r>+VaIhomS$}TNZhX+kug7?477g#uCzF8Y{J(1`FG!hx z5`g!k%ul%0*#XtASc(^G*KvBfXP;T~9?aIRL#mi0^8^l)jVX$^&jOIdKbc0Hi(yrC zn-SS$H1sSJNAT!U=M;WJ%ZpiQ#gmJ(jRo>JxjLs|jWfwi#jLp8Ol7sq0v2tQpZ>!Y z()L|`Dq`<=@M%e;-BTD(zes*=X991^m(d#!gz_;l44U{+G~_mmAvb+_ke6i^J1*Tk zIR3Df%SSK0tW!Maw8n-GKa*&}MCZd>8G7-1h+%#M-eiI!V(^;ONxu8v@rHiy2QORt+eVa>bH>)PcYgRS0mtAqmwoUH$?nF^Eqoh756F%34Cn-2_ zHVI-Vf5eOwKBt216Z!EzV{SKlZ1V`s6n^sw&krKEd65Za@I5B&X8ELEVj#~)myZnW z&SRLX@57HJm9diG22=YbQeB6@bT4T9ERy7hu9F4<~?>bl5BFTxMu z$Vwrva2CwMoP4V1f zyau$)Ct-OQZ7!jSn8h1rrz(y+zsx>e=MxrRf)^HewB*8xdU?vjr5DtvG0zTo8{Rak z*ztfh-$mf)RGeFaBL_V3fo!dO?+23d3z>%NSguErQvmHxK3pS{8(9oOZizh3pw&_3 zg7Q~*`OG2`7F}ANO!F5OvTxDe%B;`HyJXW63K6v2-EDKy+LqvIeqG+gLq7_*Y0;>n L>v8nFtakq&bEpb5 diff --git a/org.simantics.sysdyn.ontology/graph/PropertyViewpoints.pgraph b/org.simantics.sysdyn.ontology/graph/PropertyViewpoints.pgraph index c7d1d53b..f9f52469 100644 --- a/org.simantics.sysdyn.ontology/graph/PropertyViewpoints.pgraph +++ b/org.simantics.sysdyn.ontology/graph/PropertyViewpoints.pgraph @@ -57,4 +57,30 @@ POBC VP.BrowseContext.HasVisualsContribution _ : VP.VisualsContribution VP.VisualsContribution.HasNodeType L0.Entity VP.VisualsContribution.HasRule POBC.ParameterSorterRule - \ No newline at end of file + +///////////////////////////////////////////// +/// Sensitivity experiment parameters +///////////////////////////////////////////// +SPBC = SYSDYN.SensitivityAnalysisExperiment.ParameterBrowseContext : VP.BrowseContext +SPAC = SYSDYN.SensitivityAnalysisExperiment.ParameterActionContext : VP.BrowseContext + +SPBC.ParameterChildRule : VP.ChildRule +SPBC.ParameterLabelRule : VP.LabelRule +//SPBC.ParameterLabelDecorationRule : VP.LabelDecorationRule + +SPBC + @VP.customChildRule SYSDYN.SensitivityAnalysisExperiment SPBC.ParameterChildRule + SYSDYN.SensitivityAnalysisExperiment.Parameter + +SPBC + @VP.customLabelRule SYSDYN.SensitivityAnalysisExperiment.Parameter SPBC.ParameterLabelRule + +//SPBC +// @VP.customLabelDecorationRule SYSDYN.SensitivityAnalysisExperiment.Parameter SPBC.ParameterLabelDecorationRule + +SPBC + VP.BrowseContext.HasVisualsContribution _ : VP.VisualsContribution + VP.VisualsContribution.HasNodeType SYSDYN.SensitivityAnalysisExperiment + VP.VisualsContribution.HasRule VP.PassThruSorterRule + +SPAC.Actions : L0.Library diff --git a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph index 54d927db..9fa3788b 100644 --- a/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph +++ b/org.simantics.sysdyn.ontology/graph/Sysdyn.pgraph @@ -349,14 +349,38 @@ SYSDYN.SimulateOnChangeExperiment -- SYSDYN.SensitivityAnalysisExperiment.propabilityDistribution --> L0.String -- SYSDYN.SensitivityAnalysisExperiment.variedParameter --> SYSDYN.Auxiliary -- SYSDYN.SensitivityAnalysisExperiment.variedParameter --> L0.String -- SYSDYN.SensitivityAnalysisExperiment.minValue --> L0.Double -- SYSDYN.SensitivityAnalysisExperiment.maxValue --> L0.Double -- SYSDYN.SensitivityAnalysisExperiment.numValues --> L0.Integer -- SYSDYN.SensitivityAnalysisExperiment.mean --> L0.Double -- SYSDYN.SensitivityAnalysisExperiment.stdDeviation --> L0.Double -- SYSDYN.SensitivityAnalysisExperiment.parameterList --> L0.List -- SYSDYN.SensitivityAnalysisExperiment.randomSeed --> L0.Integer -- SYSDYN.SensitivityAnalysisExperiment.resultRefreshRate --> L0.Integer -- SYSDYN.SensitivityAnalysisExperiment.Parameter.propabilityDistribution --> SYSDYN.ProbabilityDistribution -- SYSDYN.SensitivityAnalysisExperiment.Parameter.variable --> L0.String -- SYSDYN.SensitivityAnalysisExperiment.Parameter.indexes --> L0.StringArray -- SYSDYN.SensitivityAnalysisExperiment.Parameter.numberOfValues --> L0.Integer -- SYSDYN.UniformDistribution.minValue --> L0.Double -- SYSDYN.UniformDistribution.maxValue --> L0.Double -- SYSDYN.NormalDistribution.minValue --> L0.Double -- SYSDYN.NormalDistribution.maxValue --> L0.Double -- SYSDYN.NormalDistribution.mean --> L0.Double -- SYSDYN.NormalDistribution.stdDeviation --> L0.Double -- SYSDYN.Interval.minValue --> L0.Double -- SYSDYN.Interval.maxValue --> L0.Double - none 0 fill 1 + none 0 fill 1 \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/newComponents/NewSensitivityAnalysisExperimentNodeHandler.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/newComponents/NewSensitivityAnalysisExperimentNodeHandler.java index 8c8c00b6..c1be4933 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/newComponents/NewSensitivityAnalysisExperimentNodeHandler.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/handlers/newComponents/NewSensitivityAnalysisExperimentNodeHandler.java @@ -11,12 +11,16 @@ *******************************************************************************/ package org.simantics.sysdyn.ui.handlers.newComponents; +import java.util.ArrayList; + 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.layer0.Layer0; +import org.simantics.layer0.utils.direct.GraphUtils; import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.manager.SysdynSensitivityAnalysisExperiment; /** * Creates a new sensitivity analysis experiment. @@ -29,12 +33,22 @@ public class NewSensitivityAnalysisExperimentNodeHandler extends NewExperimentNo @Override protected void configureExperiment(WriteGraph graph, Resource experiment) throws DatabaseException { SysdynResource sr = SysdynResource.getInstance(graph); - graph.claimLiteral(experiment, sr.SensitivityAnalysisExperiment_minValue, SysdynSensitivityAnalysisExperiment.DEFAULT_MIN_VALUE); - graph.claimLiteral(experiment, sr.SensitivityAnalysisExperiment_maxValue, SysdynSensitivityAnalysisExperiment.DEFAULT_MAX_VALUE); - graph.claimLiteral(experiment, sr.SensitivityAnalysisExperiment_numValues, SysdynSensitivityAnalysisExperiment.DEFAULT_NUM_VALUES); - graph.claimLiteral(experiment, sr.SensitivityAnalysisExperiment_mean, SysdynSensitivityAnalysisExperiment.DEFAULT_MEAN); - graph.claimLiteral(experiment, sr.SensitivityAnalysisExperiment_stdDeviation, SysdynSensitivityAnalysisExperiment.DEFAULT_STD_DEVIATION); - graph.claimLiteral(experiment, sr.SensitivityAnalysisExperiment_propabilityDistribution, "uniform"); + Layer0 L0 = Layer0.getInstance(graph); + + Resource distribution = GraphUtils.create2(graph, sr.UniformDistribution, + sr.UniformDistribution_minValue, 0.0, + sr.UniformDistribution_maxValue, 10.0); + + Resource parameter = GraphUtils.create2(graph, sr.SensitivityAnalysisExperiment_Parameter, + sr.SensitivityAnalysisExperiment_Parameter_propabilityDistribution, distribution, + sr.SensitivityAnalysisExperiment_Parameter_variable, "", + sr.SensitivityAnalysisExperiment_Parameter_numberOfValues, 10, + L0.PartOf, experiment); + + ArrayList parameterList = new ArrayList(); + parameterList.add(parameter); + + graph.claim(experiment, sr.SensitivityAnalysisExperiment_parameterList, ListUtils.create(graph, parameterList)); } @Override diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/SensitivityAnalysisExperimentTab.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/SensitivityAnalysisExperimentTab.java index b334ec2e..68ae677c 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/SensitivityAnalysisExperimentTab.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/SensitivityAnalysisExperimentTab.java @@ -1,26 +1,66 @@ package org.simantics.sysdyn.ui.properties; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IWorkbenchSite; +import org.simantics.browsing.ui.swt.SingleSelectionInputSource; +import org.simantics.browsing.ui.swt.widgets.Button; +import org.simantics.browsing.ui.swt.widgets.GraphExplorerComposite; import org.simantics.browsing.ui.swt.widgets.StringPropertyFactory; import org.simantics.browsing.ui.swt.widgets.StringPropertyModifier; +import org.simantics.browsing.ui.swt.widgets.TrackedCombo; import org.simantics.browsing.ui.swt.widgets.TrackedText; +import org.simantics.browsing.ui.swt.widgets.impl.ComboModifyListenerImpl; +import org.simantics.browsing.ui.swt.widgets.impl.ReadFactoryImpl; +import org.simantics.browsing.ui.swt.widgets.impl.SelectionListenerImpl; +import org.simantics.browsing.ui.swt.widgets.impl.TextModifyListener; +import org.simantics.browsing.ui.swt.widgets.impl.TrackedModifyEvent; +import org.simantics.browsing.ui.swt.widgets.impl.Widget; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.procedure.adapter.DisposableListener; +import org.simantics.db.common.utils.ListUtils; +import org.simantics.db.exception.DatabaseException; import org.simantics.db.management.ISessionContext; +import org.simantics.db.request.Read; +import org.simantics.jfreechart.chart.properties.RangeComposite; +import org.simantics.jfreechart.chart.properties.VariableExistsValidator; +import org.simantics.jfreechart.chart.properties.xyline.AxisAndVariablesExplorerComposite; import org.simantics.layer0.Layer0; +import org.simantics.layer0.utils.direct.GraphUtils; import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyFactory; -import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyModifier; import org.simantics.sysdyn.ui.properties.widgets.factories.IntegerPropertyFactory; import org.simantics.sysdyn.ui.properties.widgets.factories.IntegerPropertyModifier; -import org.simantics.utils.ui.validators.DoubleValidator; +import org.simantics.sysdyn.ui.properties.widgets.sensitivity.DistributionPropertyWidget; +import org.simantics.sysdyn.ui.properties.widgets.sensitivity.ParameterChildRule; +import org.simantics.sysdyn.ui.properties.widgets.sensitivity.VariableNameModifier; +import org.simantics.ui.SimanticsUI; +import org.simantics.utils.RunnableWithObject; +import org.simantics.utils.datastructures.ArrayMap; +import org.simantics.utils.ui.AdaptionUtils; import org.simantics.utils.ui.validators.IntegerValidator; /** @@ -29,107 +69,350 @@ import org.simantics.utils.ui.validators.IntegerValidator; * @author Tuomas Miettinen * */ -public class SensitivityAnalysisExperimentTab extends LabelPropertyTabContributor { +public class SensitivityAnalysisExperimentTab extends LabelPropertyTabContributor implements Widget { - @Override - public void createControls(Composite body, IWorkbenchSite site, - ISessionContext context, WidgetSupport support) { - - ScrolledComposite sc = new ScrolledComposite(body, SWT.H_SCROLL | SWT.V_SCROLL); - GridDataFactory.fillDefaults().grab(true, true).applyTo(sc); - GridLayoutFactory.fillDefaults().applyTo(sc); - - Composite composite = new RemoveFocusBeforeExperimentComposite(sc, SWT.NONE); + private GraphExplorerComposite explorer; + private WidgetSupportImpl parameterSupport = new WidgetSupportImpl(); + private ScrolledComposite propertyContainer; + private Composite parameterProperties; + private Composite content; + private Button remove; + private Resource experiment; + + private boolean dirty = false; + + private DisposableListener> contentListener; + + @Override + public void createControls(Composite body, IWorkbenchSite site, + final ISessionContext context, WidgetSupport support) { + support.register(this); + + Composite composite = new RemoveFocusBeforeExperimentComposite(body, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(composite); - GridLayoutFactory.fillDefaults().margins(3, 3).numColumns(2).applyTo(composite); + GridLayoutFactory.fillDefaults().margins(3, 3).applyTo(composite); + + // Scrolled composite for displaying properties of a selection in explorer + propertyContainer = new ScrolledComposite(composite, SWT.H_SCROLL | SWT.V_SCROLL); + GridDataFactory.fillDefaults().grab(true, true).applyTo(propertyContainer); + GridLayoutFactory.fillDefaults().applyTo(propertyContainer); + propertyContainer.setExpandHorizontal(true); + propertyContainer.setExpandVertical(true); + content = new Composite(propertyContainer, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).applyTo(content); + GridLayoutFactory.fillDefaults().numColumns(2).applyTo(content); + // Label - Label label = new Label(composite, SWT.NONE); + Composite labelComposite = new Composite(content, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(labelComposite); + GridLayoutFactory.fillDefaults().numColumns(4).applyTo(labelComposite); + Label label = new Label(labelComposite, SWT.NONE); label.setText("Name"); - - TrackedText name = new TrackedText(composite, support, SWT.BORDER); + + TrackedText name = new TrackedText(labelComposite, support, SWT.BORDER); name.setTextFactory(new StringPropertyFactory(Layer0.URIs.HasLabel)); name.addModifyListener(new StringPropertyModifier(context, Layer0.URIs.HasLabel)); name.addModifyListener(new StringPropertyModifier(context, Layer0.URIs.HasName)); name.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), name.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(name.getWidget()); + + label = new Label(labelComposite, SWT.NONE); + label.setText("Seed"); + + TrackedText seed = new TrackedText(labelComposite, support, SWT.BORDER); + seed.setTextFactory(new IntegerPropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_randomSeed)); + seed.addModifyListener(new IntegerPropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_randomSeed)); + seed.setInputValidator(new IntegerValidator()); + seed.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), seed.getWidget()))); + GridDataFactory.fillDefaults().hint(80, SWT.DEFAULT).applyTo(seed.getWidget()); + + + // (Ontology-based) GraphExplorer displaying range axis and variables mapped to those axis + explorer = new AxisAndVariablesExplorerComposite(ArrayMap.keys( + "displaySelectors", "displayFilter").values(false, false), site, content, support, SWT.FULL_SELECTION | SWT.BORDER | SWT.SINGLE); + explorer.setBrowseContexts(SysdynResource.URIs.SensitivityAnalysisExperiment_ParameterBrowseContext); + explorer.setInputSource(new SingleSelectionInputSource( + Resource.class)); + explorer.getExplorer().setAutoExpandLevel(2); // Expand everything in the beginning + explorer.finish(); + + ((Tree)explorer.getExplorerControl()).addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + updateSelection(context); + } + }); + + + + explorer.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + if(contentListener != null) + contentListener.dispose(); + } + }); + + + GridDataFactory.fillDefaults().hint(250, SWT.DEFAULT).grab(false, true).applyTo(explorer); - // The propability distribution - label = new Label(composite, SWT.NONE); - label.setText("Propability distribution"); - - TrackedText propabilityDistributionName = new TrackedText(composite, support, SWT.BORDER); - propabilityDistributionName.setTextFactory(new StringPropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_propabilityDistribution)); - propabilityDistributionName.addModifyListener(new StringPropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_propabilityDistribution)); - //parameterName.setInputValidator(new DoubleValidator()); - propabilityDistributionName.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), propabilityDistributionName.getWidget()))); - - // Parameter the value of which is varied - label = new Label(composite, SWT.NONE); - label.setText("Varied parameter name"); - - TrackedText parameterName = new TrackedText(composite, support, SWT.BORDER); - parameterName.setTextFactory(new StringPropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_variedParameter)); - parameterName.addModifyListener(new StringPropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_variedParameter)); - //parameterName.setInputValidator(new DoubleValidator()); - parameterName.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), parameterName.getWidget()))); - - // minValue - label = new Label(composite, SWT.NONE); - label.setText("Min value"); - - TrackedText minValue = new TrackedText(composite, support, SWT.BORDER); - minValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_minValue)); - minValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_minValue)); - minValue.setInputValidator(new DoubleValidator()); - minValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), minValue.getWidget()))); - GridDataFactory.fillDefaults().hint(300, SWT.DEFAULT).applyTo(name.getWidget()); - - // maxValue - label = new Label(composite, SWT.NONE); - label.setText("Max value"); - - TrackedText maxValue = new TrackedText(composite, support, SWT.BORDER); - maxValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_maxValue)); - maxValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_maxValue)); - maxValue.setInputValidator(new DoubleValidator()); - maxValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), maxValue.getWidget()))); - - // number of values - label = new Label(composite, SWT.NONE); - label.setText("Number of values"); - - TrackedText numValues = new TrackedText(composite, support, SWT.BORDER); - numValues.setTextFactory(new IntegerPropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_numValues)); - numValues.addModifyListener(new IntegerPropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_numValues)); + Group parameterPropertyGroup = new Group(content, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(parameterPropertyGroup); + GridLayoutFactory.fillDefaults().applyTo(parameterPropertyGroup); + + parameterProperties = new Composite(parameterPropertyGroup, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).applyTo(parameterProperties); + GridLayoutFactory.fillDefaults().numColumns(2).applyTo(parameterProperties); + + // Label + label = new Label(parameterProperties, SWT.NONE); + label.setText("Variable:"); + GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(label); + + TrackedText variable = new TrackedText(parameterProperties, parameterSupport, SWT.BORDER); + variable.setTextFactory(new StringPropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_Parameter_variable)); + variable.addModifyListener(new VariableNameModifier(variable.getWidget(), parameterSupport, SysdynResource.URIs.SensitivityAnalysisExperiment_Parameter_variable, SysdynResource.URIs.SensitivityAnalysisExperiment_Parameter_indexes)); + variable.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), variable.getWidget()))); + variable.setInputValidator(new VariableExistsValidator(parameterSupport, variable)); + GridDataFactory.fillDefaults().grab(true, false).applyTo(variable.getWidget()); + + label = new Label(parameterProperties, SWT.NONE); + label.setText("Range:"); + GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(label); + + RangeComposite rangeComposite = new RangeComposite(parameterProperties, context, parameterSupport, SWT.NONE) { + @Override + protected Resource getIndexRelation(ReadGraph graph) { + return SysdynResource.getInstance(graph).SensitivityAnalysisExperiment_Parameter_indexes; + } + }; + GridDataFactory.fillDefaults().grab(true, false).applyTo(rangeComposite); + +// TrackedText variable = new TrackedText(parameterProperties, parameterSupport, SWT.BORDER); +// variable.setTextFactory(new StringPropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_Parameter_variable)); +// variable.addModifyListener(new StringPropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_Parameter_variable)); +// variable.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), variable.getWidget()))); +// GridDataFactory.fillDefaults().grab(true, false).applyTo(variable.getWidget()); + + label = new Label(parameterProperties, SWT.NONE); + label.setText("Number of values:"); + GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(label); + + + TrackedText numValues = new TrackedText(parameterProperties, parameterSupport, SWT.BORDER); + numValues.setTextFactory(new IntegerPropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_Parameter_numberOfValues)); + numValues.addModifyListener(new IntegerPropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_Parameter_numberOfValues)); numValues.setInputValidator(new IntegerValidator()); numValues.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), numValues.getWidget()))); + GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).hint(80, SWT.DEFAULT).applyTo(numValues.getWidget()); + + label = new Label(parameterProperties, SWT.NONE); + label.setText("Distribution:"); + GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(label); + + TrackedCombo distributionSelector = new TrackedCombo(parameterProperties, parameterSupport, SWT.DROP_DOWN); + distributionSelector.setItemFactory(new ReadFactoryImpl>() { + + @Override + public Map perform(ReadGraph graph, Resource input) throws DatabaseException { + SysdynResource SR = SysdynResource.getInstance(graph); + Map items = new HashMap(); + + items.put("Normal", SR.NormalDistribution); + items.put("Uniform", SR.UniformDistribution); + items.put("Interval", SR.Interval); + + return items; + } + + }); + + distributionSelector.setSelectionFactory(new ReadFactoryImpl() { + + @Override + public String perform(ReadGraph graph, Resource parameter) throws DatabaseException { + SysdynResource SR = SysdynResource.getInstance(graph); + Resource distribution = graph.getPossibleObject(parameter, SR.SensitivityAnalysisExperiment_Parameter_propabilityDistribution); + if(distribution == null) + return null; + + if(graph.isInstanceOf(distribution, SR.UniformDistribution)) + return "Uniform"; + else if(graph.isInstanceOf(distribution, SR.NormalDistribution)) + return "Normal"; + else if(graph.isInstanceOf(distribution, SR.Interval)) + return "Interval"; + else + return ""; + + } + }); + + distributionSelector.addModifyListener(new ComboModifyListenerImpl() { + + @Override + public void applyText(WriteGraph graph, Resource input, String text) + throws DatabaseException { + if(text == null || text.isEmpty()) + return; + + SysdynResource SR = SysdynResource.getInstance(graph); + + Resource type = SR.UniformDistribution; + + if("Normal".equals(text)) + type = SR.NormalDistribution; + else if("Interval".equals(text)) + type = SR.Interval; + + graph.deny(input, SR.SensitivityAnalysisExperiment_Parameter_propabilityDistribution); + + GraphUtils.create2(graph, type, + SR.SensitivityAnalysisExperiment_Parameter_propabilityDistribution_Inverse, input); + + dirty = true; + } + }); + + distributionSelector.addModifyListener(new TextModifyListener() { + + @Override + public void modifyText(TrackedModifyEvent e) { + if(dirty) { + parameterSupport.update(); + dirty = false; + propertyContainer.setContent(content); + Point size = content.computeSize(SWT.DEFAULT, SWT.DEFAULT); + propertyContainer.setMinSize(size); + } + } + }); + + label = new Label(parameterProperties, SWT.NONE); + + DistributionPropertyWidget dpw = new DistributionPropertyWidget(parameterProperties, context, parameterSupport, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).applyTo(dpw); + + + Composite buttonComposite = new Composite(content, SWT.NONE); + GridDataFactory.fillDefaults().applyTo(buttonComposite); + GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonComposite); + + + Button addVariable = new Button(buttonComposite, support, SWT.NONE); + addVariable.setText("Add parameter"); + addVariable.addSelectionListener(new SelectionListenerImpl(context) { + + @Override + public void apply(WriteGraph graph, Resource input) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + Layer0 L0 = Layer0.getInstance(graph); + + Resource distribution = GraphUtils.create2(graph, sr.UniformDistribution, + sr.UniformDistribution_minValue, 0.0, + sr.UniformDistribution_maxValue, 10.0); + + Resource parameter = GraphUtils.create2(graph, sr.SensitivityAnalysisExperiment_Parameter, + sr.SensitivityAnalysisExperiment_Parameter_propabilityDistribution, distribution, + sr.SensitivityAnalysisExperiment_Parameter_variable, "", + sr.SensitivityAnalysisExperiment_Parameter_numberOfValues, 10, + L0.PartOf, input); + + Resource parameterList = graph.getPossibleObject(input, sr.SensitivityAnalysisExperiment_parameterList); + ListUtils.insertBack(graph, parameterList, Collections.singleton(parameter)); + } + }); + + remove = new Button(buttonComposite, parameterSupport, SWT.NONE); + remove.setText("Remove"); + remove.addSelectionListener(new SelectionListenerImpl(context) { + + @Override + public void apply(WriteGraph graph, Resource input) throws DatabaseException { + if(input == null) + return; + + SysdynResource sr = SysdynResource.getInstance(graph); + Layer0 L0 = Layer0.getInstance(graph); + + Resource experiment = graph.getPossibleObject(input, L0.PartOf); + Resource parameterList = graph.getPossibleObject(experiment, sr.SensitivityAnalysisExperiment_parameterList); + + if(ListUtils.toList(graph, parameterList).size() > 1) + ListUtils.removeElement(graph, parameterList, input); + + } + + }); + + + propertyContainer.setContent(content); + Point size = content.computeSize(SWT.DEFAULT, SWT.DEFAULT); + propertyContainer.setMinSize(size); + + } + + + /** + * Updates the content of propertyContainer + * @param context + */ + private void updateSelection(ISessionContext context) { + ISelectionProvider selectionProvider = (ISelectionProvider)explorer.getAdapter(ISelectionProvider.class); + IStructuredSelection selection = (IStructuredSelection)selectionProvider.getSelection(); + parameterSupport.fireInput(context, selection); + + propertyContainer.setContent(content); + Point size = content.computeSize(SWT.DEFAULT, SWT.DEFAULT); + propertyContainer.setMinSize(size); + } + + + @Override + public void setInput(ISessionContext context, Object input) { + experiment = AdaptionUtils.adaptToSingle(input, Resource.class); + if(contentListener == null) { + contentListener = new DisposableListener>() { + + @Override + public void execute(Collection result) { + if(remove != null && !remove.getWidget().isDisposed() && result != null) { + remove.getWidget().getDisplay().asyncExec(new RunnableWithObject(result) { + @Override + public void run() { + if(!remove.getWidget().isDisposed()) { + @SuppressWarnings("unchecked") + Collection result = (Collection) getObject(); + if(result.size() > 1) + remove.getWidget().setEnabled(true); + else + remove.getWidget().setEnabled(false); + } + } + }); + + } + } + + @Override + public void exception(Throwable t) { + t.printStackTrace(); + } + }; + + SimanticsUI.getSession().asyncRequest(new Read> () { + + @SuppressWarnings("unchecked") + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + return (Collection) new ParameterChildRule().getChildren(graph, experiment); + } + + }, contentListener); + } + - // mean - label = new Label(composite, SWT.NONE); - label.setText("Mean"); - - TrackedText mean = new TrackedText(composite, support, SWT.BORDER); - mean.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_mean)); - mean.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_mean)); - mean.setInputValidator(new DoubleValidator()); - mean.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), mean.getWidget()))); - - // standard deviation - label = new Label(composite, SWT.NONE); - label.setText("Standard deviation"); - - TrackedText stdDeviation = new TrackedText(composite, support, SWT.BORDER); - stdDeviation.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.SensitivityAnalysisExperiment_stdDeviation)); - stdDeviation.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.SensitivityAnalysisExperiment_stdDeviation)); - stdDeviation.setInputValidator(new DoubleValidator()); - stdDeviation.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), stdDeviation.getWidget()))); - - // Scrolled composite settings - sc.setContent(composite); - sc.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); - sc.setExpandHorizontal(true); - sc.setExpandVertical(true); - - } + } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/DistributionPropertyWidget.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/DistributionPropertyWidget.java new file mode 100644 index 00000000..93a62b2d --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/DistributionPropertyWidget.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.HashMap; + +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.simantics.browsing.ui.swt.widgets.impl.Widget; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.db.request.Read; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.ui.SimanticsUI; +import org.simantics.utils.ui.AdaptionUtils; + +public class DistributionPropertyWidget extends Composite implements Widget { + + private IDistributionProperties distributionProperties = null; + private WidgetSupportImpl distributionSupport = new WidgetSupportImpl(); + private HashMap cache = new HashMap(); + private Resource oldInput; + + public DistributionPropertyWidget(Composite parent, ISessionContext context, WidgetSupport support, int style) { + super(parent, style); + GridLayoutFactory.fillDefaults().applyTo(this); + support.register(this); + } + + @Override + public void setInput(ISessionContext context, Object input) { + final Resource resource = AdaptionUtils.adaptToSingle(input, Resource.class); + if(resource == null) + return; + + IDistributionProperties newProperties = null; + Resource distribution = null; + try { + distribution = SimanticsUI.getSession().syncRequest(new Read() { + + @Override + public Resource perform(ReadGraph graph) throws DatabaseException { + Resource distribution = graph.getPossibleObject( + resource, + SysdynResource.getInstance(graph).SensitivityAnalysisExperiment_Parameter_propabilityDistribution + ); + + return distribution; + } + + }); + + newProperties = SimanticsUI.getSession().syncRequest(new Read() { + + @Override + public IDistributionProperties perform(ReadGraph graph) throws DatabaseException { + Resource distribution = graph.getPossibleObject( + resource, + SysdynResource.getInstance(graph).SensitivityAnalysisExperiment_Parameter_propabilityDistribution + ); + + return graph.adapt(distribution, IDistributionProperties.class); + } + + }); + + // Create a PropertyComposite for the selected node + if(newProperties != null) { + + if(distributionProperties != null) + distributionProperties.getCachedValues(cache); + + if(oldInput != null && !oldInput.equals(resource)) + cache.clear(); + + oldInput = resource; + + for(Control child : this.getChildren()) { + child.dispose(); + } + + newProperties.createContent(this, context, distributionSupport); + distributionProperties = newProperties; + + SimanticsUI.getSession().syncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + distributionProperties.applyCache(graph, cache); + + } + }); + + distributionSupport.fireInput(context, new StructuredSelection(distribution)); + } + + } catch (DatabaseException e) { + e.printStackTrace(); + } + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IDistributionProperties.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IDistributionProperties.java new file mode 100644 index 00000000..0c628e95 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IDistributionProperties.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.HashMap; + +import org.eclipse.swt.widgets.Composite; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; + +public interface IDistributionProperties { + + public Composite createContent(Composite parent, ISessionContext context, WidgetSupport support); + + public void getCachedValues(HashMap cachedValues); + + public void applyCache(WriteGraph graph, HashMap cachedValues) throws DatabaseException; + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IntervalProperties.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IntervalProperties.java new file mode 100644 index 00000000..9f3b7bc4 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/IntervalProperties.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.HashMap; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.simantics.browsing.ui.swt.widgets.TrackedText; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.databoard.Bindings; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.jfreechart.chart.properties.DoubleValidator; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.properties.SysdynBasicColorProvider; +import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyFactory; +import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyModifier; + +public class IntervalProperties implements IDistributionProperties { + + private Resource resource; + private TrackedText minValue; + private TrackedText maxValue; + + public IntervalProperties(Resource resource) { + this.resource = resource; + } + + @Override + public Composite createContent(Composite parent, ISessionContext context, WidgetSupport support) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite); + + // minValue + Label label = new Label(composite, SWT.NONE); + label.setText("Min value"); + GridDataFactory.fillDefaults().applyTo(label); + + minValue = new TrackedText(composite, support, SWT.BORDER); + minValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.Interval_minValue)); + minValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.Interval_minValue)); + minValue.setInputValidator(new DoubleValidator(false)); + minValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), minValue.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(minValue.getWidget()); + + // maxValue + label = new Label(composite, SWT.NONE); + label.setText("Max value"); + GridDataFactory.fillDefaults().applyTo(label); + + maxValue = new TrackedText(composite, support, SWT.BORDER); + maxValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.Interval_maxValue)); + maxValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.Interval_maxValue)); + maxValue.setInputValidator(new DoubleValidator(false)); + maxValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), maxValue.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(maxValue.getWidget()); + + return composite; + } + + + @Override + public void getCachedValues(HashMap cahcedValues) { + if(!minValue.isDisposed()) + cahcedValues.put(SensitivityDistributionKeys.MIN, minValue.getText()); + if(!maxValue.isDisposed()) + cahcedValues.put(SensitivityDistributionKeys.MAX, maxValue.getText()); + } + + + + @Override + public void applyCache(WriteGraph graph, HashMap cachedValues) throws DatabaseException { + if(resource == null) + return; + + SysdynResource SR = SysdynResource.getInstance(graph); + + String min = cachedValues.get(SensitivityDistributionKeys.MIN); + if(min != null) { + try { + Double d = Double.parseDouble(min); + graph.claimLiteral(resource, SR.Interval_minValue, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + + String max = cachedValues.get(SensitivityDistributionKeys.MAX); + if(max != null) { + try { + Double d = Double.parseDouble(max); + graph.claimLiteral(resource, SR.Interval_maxValue, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + } + + + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/NormalDistributionProperties.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/NormalDistributionProperties.java new file mode 100644 index 00000000..20c3f76f --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/NormalDistributionProperties.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.HashMap; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.simantics.browsing.ui.swt.widgets.TrackedText; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.databoard.Bindings; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.jfreechart.chart.properties.DoubleValidator; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.properties.SysdynBasicColorProvider; +import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyFactory; +import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyModifier; + +public class NormalDistributionProperties implements IDistributionProperties { + + private TrackedText minValue; + private TrackedText maxValue; + private TrackedText mean; + private TrackedText stdDeviation; + private Resource resource; + + public NormalDistributionProperties(Resource resource) { + this.resource = resource; + } + + @Override + public Composite createContent(Composite parent, ISessionContext context, WidgetSupport support) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite); + + // minValue + Label label = new Label(composite, SWT.NONE); + label.setText("Min value"); + GridDataFactory.fillDefaults().applyTo(label); + + minValue = new TrackedText(composite, support, SWT.BORDER); + minValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.NormalDistribution_minValue)); + minValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.NormalDistribution_minValue)); + minValue.setInputValidator(new DoubleValidator(true)); + minValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), minValue.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(minValue.getWidget()); + + // maxValue + label = new Label(composite, SWT.NONE); + label.setText("Max value"); + GridDataFactory.fillDefaults().applyTo(label); + + maxValue = new TrackedText(composite, support, SWT.BORDER); + maxValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.NormalDistribution_maxValue)); + maxValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.NormalDistribution_maxValue)); + maxValue.setInputValidator(new DoubleValidator(true)); + maxValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), maxValue.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(maxValue.getWidget()); + + // mean + label = new Label(composite, SWT.NONE); + label.setText("Mean"); + GridDataFactory.fillDefaults().applyTo(label); + + mean = new TrackedText(composite, support, SWT.BORDER); + mean.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.NormalDistribution_mean)); + mean.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.NormalDistribution_mean)); + mean.setInputValidator(new DoubleValidator(true)); + mean.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), mean.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(mean.getWidget()); + + // stdDeviation + label = new Label(composite, SWT.NONE); + label.setText("Standard deviation"); + GridDataFactory.fillDefaults().applyTo(label); + + stdDeviation = new TrackedText(composite, support, SWT.BORDER); + stdDeviation.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.NormalDistribution_stdDeviation)); + stdDeviation.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.NormalDistribution_stdDeviation)); + stdDeviation.setInputValidator(new DoubleValidator(true)); + stdDeviation.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), stdDeviation.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(stdDeviation.getWidget()); + + return composite; + } + + @Override + public void getCachedValues(HashMap cahcedValues) { + if(!minValue.isDisposed()) + cahcedValues.put(SensitivityDistributionKeys.MIN, minValue.getText()); + if(!maxValue.isDisposed()) + cahcedValues.put(SensitivityDistributionKeys.MAX, maxValue.getText()); + if(!mean.isDisposed()) + cahcedValues.put(SensitivityDistributionKeys.MEAN, mean.getText()); + if(!stdDeviation.isDisposed()) + cahcedValues.put(SensitivityDistributionKeys.STD_DEVIATION, stdDeviation.getText()); + } + + + + @Override + public void applyCache(WriteGraph graph, HashMap cachedValues) throws DatabaseException { + if(resource == null) + return; + + SysdynResource SR = SysdynResource.getInstance(graph); + + String min = cachedValues.get(SensitivityDistributionKeys.MIN); + if(min != null) { + try { + Double d = Double.parseDouble(min); + graph.claimLiteral(resource, SR.NormalDistribution_minValue, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + + String max = cachedValues.get(SensitivityDistributionKeys.MAX); + if(max != null) { + try { + Double d = Double.parseDouble(max); + graph.claimLiteral(resource, SR.NormalDistribution_maxValue, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + + String mean = cachedValues.get(SensitivityDistributionKeys.MEAN); + if(mean != null) { + try { + Double d = Double.parseDouble(mean); + graph.claimLiteral(resource, SR.NormalDistribution_mean, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + + String stdDeviation = cachedValues.get(SensitivityDistributionKeys.STD_DEVIATION); + if(stdDeviation != null) { + try { + Double d = Double.parseDouble(stdDeviation); + graph.claimLiteral(resource, SR.NormalDistribution_stdDeviation, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterChildRule.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterChildRule.java new file mode 100644 index 00000000..30274d60 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterChildRule.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.Collection; +import java.util.Collections; + +import org.simantics.browsing.ui.model.children.ChildRule; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.utils.ListUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.SysdynResource; + +public class ParameterChildRule implements ChildRule { + + @Override + public boolean isCompatible(Class contentType) { + return contentType.equals(Resource.class); + } + + @Override + public Collection getChildren(ReadGraph graph, Object parent) throws DatabaseException { + SysdynResource SR = SysdynResource.getInstance(graph); + + Resource experiment = (Resource) parent; + Resource parameterList = graph.getPossibleObject(experiment, SR.SensitivityAnalysisExperiment_parameterList); + + if(parameterList != null) + return ListUtils.toList(graph, parameterList); + else + return Collections.emptyList(); + } + + @Override + public Collection getParents(ReadGraph graph, Object child) throws DatabaseException { + return Collections.emptyList(); + } + + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterLabelRule.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterLabelRule.java new file mode 100644 index 00000000..87984f7e --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterLabelRule.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.HashMap; +import java.util.Map; + +import org.simantics.browsing.ui.common.ColumnKeys; +import org.simantics.browsing.ui.model.labels.LabelRule; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.SysdynResource; + +public class ParameterLabelRule implements LabelRule { + + @Override + public boolean isCompatible(Class contentType) { + return contentType.equals(Resource.class); + } + + @Override + public Map getLabel(ReadGraph graph, Object content) throws DatabaseException { + HashMap result = new HashMap(); + + String variable = graph.getPossibleRelatedValue((Resource)content, SysdynResource.getInstance(graph).SensitivityAnalysisExperiment_Parameter_variable); + Integer n = graph.getPossibleRelatedValue((Resource)content, SysdynResource.getInstance(graph).SensitivityAnalysisExperiment_Parameter_numberOfValues); + + StringBuilder sb = new StringBuilder(); + + if(variable != null) + sb.append(variable); + else + sb.append("No variable"); + + sb.append(" (n="); + + if(n != null) + sb.append(n); + else + sb.append("undefined"); + + sb.append(")"); + + result.put(ColumnKeys.SINGLE, sb.toString()) ; + + return result; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterProposalProvider.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterProposalProvider.java new file mode 100644 index 00000000..84b0c212 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/ParameterProposalProvider.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +public class ParameterProposalProvider { + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityDistributionKeys.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityDistributionKeys.java new file mode 100644 index 00000000..d1019b2c --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityDistributionKeys.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +public class SensitivityDistributionKeys { + + public static String MAX = "MAX"; + public static String MIN = "MIN"; + public static String STD_DEVIATION = "STD_DEVIATION"; + public static String MEAN = "MEAN"; + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityRangeHandlerFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityRangeHandlerFactory.java new file mode 100644 index 00000000..aca7931c --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/SensitivityRangeHandlerFactory.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.Map; + +import org.simantics.browsing.ui.swt.widgets.impl.ReadFactoryImpl; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.trend.SysdynRangeHandlerFactory; + +public class SensitivityRangeHandlerFactory extends SysdynRangeHandlerFactory { + + @Override + protected Resource getRVIRelation(ReadGraph graph) { + return SysdynResource.getInstance(graph).SensitivityAnalysisExperiment_Parameter_variable; + } + + @Override + public ReadFactoryImpl> getRangeItemFactory(int index, Resource res) { + return new RangeItemFactory(index, res, false); + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/UniformDistributionProperties.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/UniformDistributionProperties.java new file mode 100644 index 00000000..2b78a9db --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/UniformDistributionProperties.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import java.util.HashMap; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.simantics.browsing.ui.swt.widgets.TrackedText; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.databoard.Bindings; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.jfreechart.chart.properties.DoubleValidator; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.ui.properties.SysdynBasicColorProvider; +import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyFactory; +import org.simantics.sysdyn.ui.properties.widgets.factories.DoublePropertyModifier; + +public class UniformDistributionProperties implements IDistributionProperties{ + + private TrackedText minValue; + private TrackedText maxValue; + private Resource resource; + + public UniformDistributionProperties(Resource resource) { + this.resource = resource; + } + + @Override + public Composite createContent(Composite parent, ISessionContext context, WidgetSupport support) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite); + + // minValue + Label label = new Label(composite, SWT.NONE); + label.setText("Min value"); + GridDataFactory.fillDefaults().applyTo(label); + + minValue = new TrackedText(composite, support, SWT.BORDER); + minValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.UniformDistribution_minValue)); + minValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.UniformDistribution_minValue)); + minValue.setInputValidator(new DoubleValidator(false)); + minValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), minValue.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(minValue.getWidget()); + + // maxValue + label = new Label(composite, SWT.NONE); + label.setText("Max value"); + GridDataFactory.fillDefaults().applyTo(label); + + maxValue = new TrackedText(composite, support, SWT.BORDER); + maxValue.setTextFactory(new DoublePropertyFactory(SysdynResource.URIs.UniformDistribution_maxValue)); + maxValue.addModifyListener(new DoublePropertyModifier(context, SysdynResource.URIs.UniformDistribution_maxValue)); + maxValue.setInputValidator(new DoubleValidator(false)); + maxValue.setColorProvider(new SysdynBasicColorProvider(new LocalResourceManager(JFaceResources.getResources(), maxValue.getWidget()))); + GridDataFactory.fillDefaults().grab(true, false).applyTo(maxValue.getWidget()); + + return composite; + } + + @Override + public void getCachedValues(HashMap cahcedValues) { + cahcedValues.put(SensitivityDistributionKeys.MIN, minValue.getText()); + cahcedValues.put(SensitivityDistributionKeys.MAX, maxValue.getText()); + } + + + + @Override + public void applyCache(WriteGraph graph, HashMap cachedValues) throws DatabaseException { + if(resource == null) + return; + + SysdynResource SR = SysdynResource.getInstance(graph); + + String min = cachedValues.get(SensitivityDistributionKeys.MIN); + if(min != null) { + try { + Double d = Double.parseDouble(min); + graph.claimLiteral(resource, SR.UniformDistribution_minValue, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + + String max = cachedValues.get(SensitivityDistributionKeys.MAX); + if(max != null) { + try { + Double d = Double.parseDouble(max); + graph.claimLiteral(resource, SR.UniformDistribution_maxValue, d, Bindings.DOUBLE); + } catch (NumberFormatException e) {} + } + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/VariableNameModifier.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/VariableNameModifier.java new file mode 100644 index 00000000..1b04c098 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/widgets/sensitivity/VariableNameModifier.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2013 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: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.properties.widgets.sensitivity; + +import org.eclipse.jface.bindings.keys.KeyStroke; +import org.eclipse.jface.bindings.keys.ParseException; +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalListener; +import org.eclipse.jface.fieldassist.IContentProposalListener2; +import org.eclipse.jface.fieldassist.TextContentAdapter; +import org.eclipse.swt.widgets.Control; +import org.simantics.browsing.ui.swt.widgets.impl.TextModifyListenerImpl; +import org.simantics.browsing.ui.swt.widgets.impl.TrackedModifyEvent; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.jfreechart.chart.properties.VariableProposalProvider; + +public class VariableNameModifier extends TextModifyListenerImpl { + + private boolean active; + private Control control; + private String variableNameRelationUri; + private String indexUri; + + private char[] alphaNumericCharacters = { + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','å','ä','ö', + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','Å','Ä','Ö', + '1','2','3','4','5','6','7','8','9','0','.','_'}; + + + public VariableNameModifier(Control control, WidgetSupport support, String variableNameRelationUri, String indexUri) { + this.variableNameRelationUri = variableNameRelationUri; + this.indexUri = indexUri; + + this.control = control; + this.active = true; + + KeyStroke keyStroke = null; + try { + keyStroke = KeyStroke.getInstance("Ctrl+Space"); + } catch (ParseException e1) { + e1.printStackTrace(); + } + + //SimpleContentProposalProvider scpp = new VariableProposalProvider(control, support); + VariableProposalProvider scpp = new VariableProposalProvider(control, support); + scpp.setFiltering(true); + + ContentProposalAdapter adapter = new ContentProposalAdapter( + control, new TextContentAdapter(), scpp, keyStroke, alphaNumericCharacters); + adapter.setAutoActivationDelay(0); + adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); + adapter.addContentProposalListener(new IContentProposalListener2() { + + @Override + public void proposalPopupOpened(ContentProposalAdapter adapter) { + if(VariableNameModifier.this != null) + VariableNameModifier.this.deactivate(); + } + + @Override + public void proposalPopupClosed(ContentProposalAdapter adapter) { + if(VariableNameModifier.this != null) + VariableNameModifier.this.activate(); + } + }); + + adapter.addContentProposalListener(new IContentProposalListener() { + + @Override + public void proposalAccepted(IContentProposal proposal) { + if(VariableNameModifier.this.control != null && !VariableNameModifier.this.control.isDisposed()) + VariableNameModifier.this.modifyText(new TrackedModifyEvent(VariableNameModifier.this.control, proposal.getContent())); + } + }); + + + } + + + @Override + public void applyText(WriteGraph graph, Resource resource, String text) throws DatabaseException { + if(active) { + graph.claimLiteral(resource, getVariableNameRelation(graph), text, Bindings.STRING); + graph.deny(resource, getIndexRelation(graph)); + } else { + System.out.println("NÄHÄÄ"); + } + } + + private Resource getVariableNameRelation(ReadGraph graph) throws DatabaseException { + return graph.getResource(variableNameRelationUri); + } + + private Resource getIndexRelation(ReadGraph graph) throws DatabaseException { + return graph.getResource(indexUri); + } + + public void deactivate() { + active = false; + } + + public void activate() { + active = true; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SensitivitySeriesPropertyComposite.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SensitivitySeriesPropertyComposite.java index 28dc7d34..c05d1ae4 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SensitivitySeriesPropertyComposite.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SensitivitySeriesPropertyComposite.java @@ -74,7 +74,7 @@ public class SensitivitySeriesPropertyComposite extends SeriesPropertyComposite public void apply(WriteGraph graph, Resource series) throws DatabaseException { SysdynResource SR = SysdynResource.getInstance(graph); Resource dataset = graph.getPossibleObject(series, Layer0.getInstance(graph).PartOf); - if(dataset != null) + if(dataset == null) return; Boolean result = graph.getPossibleRelatedValue(dataset, SR.Charts_SensitivityDataset_median, Bindings.BOOLEAN); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java index 74bbee09..9f804e55 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/SysdynRangeHandlerFactory.java @@ -22,6 +22,11 @@ import org.simantics.sysdyn.SysdynResource; import org.simantics.utils.datastructures.Quad; public class SysdynRangeHandlerFactory implements RangeHandlerFactory { + + protected Resource getRVIRelation(ReadGraph graph) { + JFreeChartResource jfree = JFreeChartResource.getInstance(graph); + return jfree.variableRVI; + } @Override public Read> getRequest(final Resource series) { @@ -30,14 +35,18 @@ public class SysdynRangeHandlerFactory implements RangeHandlerFactory { @Override public LinkedHashMap perform(ReadGraph graph) throws DatabaseException { - JFreeChartResource jfree = JFreeChartResource.getInstance(graph); SysdynResource sr = SysdynResource.getInstance(graph); String realizationURI = ChartUtils.getCurrentRealizationURI(graph, series); - String rvi = graph.getPossibleRelatedValue(series, jfree.variableRVI); + String rvi = graph.getPossibleRelatedValue(series, getRVIRelation(graph)); if(rvi == null) return null; + rvi = rvi.replace(".", "/"); + + if(!rvi.startsWith("/")) + rvi = "/" + rvi; + try { // Find the variable for the current variableRVI Variable v = Variables.getVariable(graph, realizationURI + rvi.trim()); @@ -103,19 +112,32 @@ public class SysdynRangeHandlerFactory implements RangeHandlerFactory { * @author Teemu Lempinen * */ - private class RangeItemFactory extends ReadFactoryImpl> { + public class RangeItemFactory extends ReadFactoryImpl> { private int index; private Resource enumeration; + private boolean addCollections; + /** * * @param index Index of the enumeration in the variable * @param enumeration The enumeration */ public RangeItemFactory(int index, Resource enumeration) { + this(index, enumeration, true); + } + + /** + * + * @param index Index of the enumeration in the variable + * @param enumeration The enumeration + * @param addCollections add "Sum" and "All" + */ + public RangeItemFactory(int index, Resource enumeration, boolean addCollections) { this.index = index; this.enumeration = enumeration; + this.addCollections = addCollections; } public Object getIdentity(Object inputContents) { @@ -127,13 +149,16 @@ public class SysdynRangeHandlerFactory implements RangeHandlerFactory { LinkedHashMap result = new LinkedHashMap(); Resource enumerationIndexes = graph.getPossibleObject(enumeration, sr.Enumeration_enumerationIndexList); List indexes = ListUtils.toList(graph, enumerationIndexes); - // First add "All" and "Sum", then all of the enumeration indexes in order - result.put("All", "All"); - result.put("Sum", "Sum"); + if(addCollections) { + // First add "All" and "Sum", then all of the enumeration indexes in order + result.put("All", "All"); + result.put("Sum", "Sum"); + } for(Resource index : indexes) { String name = NameUtils.getSafeName(graph, index); result.put(name, name); } + return result; } } diff --git a/org.simantics.sysdyn/adapters.xml b/org.simantics.sysdyn/adapters.xml index 74013f11..e370bc2c 100644 --- a/org.simantics.sysdyn/adapters.xml +++ b/org.simantics.sysdyn/adapters.xml @@ -21,6 +21,38 @@ + + + + + + + + + + + + + + + + + + + + + +