1+ package gov .hhs .aspr .ms .gcm .simulation .nucleus .testsupport ;
2+
3+ import java .util .ArrayList ;
4+ import java .util .LinkedHashMap ;
5+ import java .util .List ;
6+ import java .util .Map ;
7+ import java .util .Objects ;
8+ import java .util .Optional ;
9+
10+ import gov .hhs .aspr .ms .gcm .simulation .nucleus .NucleusError ;
11+ import gov .hhs .aspr .ms .gcm .simulation .nucleus .Plugin ;
12+ import gov .hhs .aspr .ms .gcm .simulation .nucleus .PluginData ;
13+ import gov .hhs .aspr .ms .gcm .simulation .nucleus .PluginId ;
14+ import gov .hhs .aspr .ms .gcm .simulation .nucleus .SimulationState ;
15+ import gov .hhs .aspr .ms .util .errors .ContractException ;
16+
17+ public final class ScenarioContainer {
18+
19+ private static class Data {
20+
21+ private Map <PluginId , Plugin > plugins = new LinkedHashMap <>();
22+ private SimulationState simulationState = SimulationState .builder ().build ();
23+
24+ private boolean locked ;
25+
26+ private Data () {
27+ }
28+
29+ private Data (Data data ) {
30+ plugins .putAll (data .plugins );
31+ simulationState = data .simulationState ;
32+ locked = data .locked ;
33+ }
34+
35+ /**
36+ * Standard implementation consistent with the {@link #equals(Object)} method
37+ */
38+ @ Override
39+ public int hashCode () {
40+ return Objects .hash (plugins , simulationState );
41+ }
42+
43+ /**
44+ * Two {@link Data} instances are equal if and only if their inputs are equal.
45+ */
46+ @ Override
47+ public boolean equals (Object obj ) {
48+ if (this == obj ) {
49+ return true ;
50+ }
51+ if (!(obj instanceof Data )) {
52+ return false ;
53+ }
54+ Data other = (Data ) obj ;
55+ return Objects .equals (plugins , other .plugins ) && Objects .equals (simulationState , other .simulationState );
56+ }
57+
58+ }
59+
60+ private final Data data ;
61+
62+ private ScenarioContainer (Data data ) {
63+ this .data = data ;
64+ }
65+
66+ /**
67+ * Returns a new builder instance
68+ */
69+ public static Builder builder () {
70+ return new Builder (new Data ());
71+ }
72+
73+ /**
74+ * Builder class for ScenarioContainer
75+ */
76+ public static class Builder {
77+ private Data data ;
78+
79+ private void ensureDataMutability () {
80+ if (data .locked ) {
81+ data = new Data (data );
82+ data .locked = false ;
83+ }
84+ }
85+
86+ private void ensureImmutability () {
87+ if (!data .locked ) {
88+ data .locked = true ;
89+ }
90+ }
91+
92+ private Builder (Data data ) {
93+ this .data = data ;
94+ }
95+
96+ /**
97+ * Returns the ScenarioContainer built from the collected data.
98+ */
99+ public ScenarioContainer build () {
100+
101+ if (!data .locked ) {
102+ validateData ();
103+ }
104+ ensureImmutability ();
105+ return new ScenarioContainer (data );
106+ }
107+
108+ /**
109+ * Adds a plugin to this container. Replaces any existing plugin with the same
110+ * plugin id.
111+ *
112+ * @throws ContractException
113+ * <ul>
114+ * <li>{@linkplain NucleusError#NULL_PLUGIN} if the
115+ * plugin is null</li>
116+ * </ul>
117+ */
118+ public Builder addPlugin (final Plugin plugin ) {
119+ ensureDataMutability ();
120+ validatePluginNotNull (plugin );
121+ data .plugins .put (plugin .getPluginId (), plugin );
122+ return this ;
123+ }
124+
125+ /**
126+ * Sets the simulation state. Defaults to the default SimulationState at the
127+ * current execution time.
128+ *
129+ * @throws ContractException
130+ *
131+ * <ul>
132+ * <li>{@linkplain NucleusError#NULL_SIMULATION_STATE}
133+ * if the simulation state is null</li>
134+ * </ul>
135+ */
136+ public Builder setSimulationState (SimulationState simulationState ) {
137+ validateSimulationStateNotNull (simulationState );
138+ ensureDataMutability ();
139+ data .simulationState = simulationState ;
140+ return this ;
141+ }
142+
143+ private void validateData () {
144+ // do nothing
145+ }
146+
147+ private void validateSimulationStateNotNull (SimulationState simulationState ) {
148+ if (simulationState == null ) {
149+ throw new ContractException (NucleusError .NULL_SIMULATION_STATE );
150+ }
151+ }
152+
153+ private void validatePluginNotNull (Plugin plugin ) {
154+ if (plugin == null ) {
155+ throw new ContractException (NucleusError .NULL_PLUGIN );
156+ }
157+ }
158+
159+ }
160+
161+ /**
162+ * Returns the plugin data object compatible with the given plugin data class
163+ * reference.
164+ *
165+ * @throws ContractException
166+ * <ul>
167+ * <li>{@linkplain NucleusError#NULL_PLUGIN_DATA_CLASS}
168+ * if the class reference is null</li>
169+ * <li>{@linkplain NucleusError#AMBIGUOUS_PLUGIN_DATA_CLASS}
170+ * if more than one plugin data object matches the
171+ * class reference</li>
172+ * </ul>
173+ */
174+ @ SuppressWarnings ("unchecked" )
175+ public <T extends PluginData > Optional <T > getPluginData (Class <T > pluginDataClass ) {
176+ if (pluginDataClass == null ) {
177+ throw new ContractException (NucleusError .NULL_PLUGIN_DATA_CLASS );
178+ }
179+ PluginData result = null ;
180+ for (PluginId pluginId : data .plugins .keySet ()) {
181+ Plugin plugin = data .plugins .get (pluginId );
182+ for (PluginData pluginData : plugin .getPluginDatas ()) {
183+ if (pluginData .getClass ().isAssignableFrom (pluginDataClass )) {
184+ if (result == null ) {
185+ result = pluginData ;
186+ } else {
187+ throw new ContractException (NucleusError .AMBIGUOUS_PLUGIN_DATA_CLASS );
188+ }
189+ }
190+ }
191+ }
192+ return Optional .ofNullable ((T ) result );
193+ }
194+
195+ /**
196+ * Returns the plugin data objects associated with the given class reference
197+ *
198+ * @throws ContractException {@linkplain NucleusError#NULL_PLUGIN_DATA_CLASS} if
199+ * the class reference is null
200+ */
201+ @ SuppressWarnings ("unchecked" )
202+ public <T extends PluginData > List <T > getPluginDatas (Class <T > pluginDataClass ) {
203+ if (pluginDataClass == null ) {
204+ throw new ContractException (NucleusError .NULL_PLUGIN_DATA_CLASS );
205+ }
206+ List <T > result = new ArrayList <>();
207+ for (PluginId pluginId : data .plugins .keySet ()) {
208+ Plugin plugin = data .plugins .get (pluginId );
209+ for (PluginData pluginData : plugin .getPluginDatas ()) {
210+ if (pluginData .getClass ().isAssignableFrom (pluginDataClass )) {
211+ result .add ((T ) pluginData );
212+ }
213+ }
214+ }
215+ return result ;
216+ }
217+
218+ /**
219+ * Returns the contained plugin for the given plugin id. Tolerate null.
220+ */
221+ public Optional <Plugin > getPlugin (PluginId pluginId ) {
222+ Plugin plugin = data .plugins .get (pluginId );
223+ return Optional .ofNullable (plugin );
224+ }
225+
226+ /**
227+ * Returns the list of plugins in this container.
228+ */
229+ public List <Plugin > getPlugins () {
230+ return new ArrayList <>(data .plugins .values ());
231+ }
232+
233+ /**
234+ * Returns the simulation state.
235+ *
236+ */
237+ public SimulationState getSimulationState () {
238+ return data .simulationState ;
239+ }
240+
241+ public Builder toBuilder () {
242+ return new Builder (data );
243+ }
244+
245+ /**
246+ * Standard implementation consistent with the {@link #equals(Object)} method
247+ */
248+ @ Override
249+ public int hashCode () {
250+ return Objects .hash (data );
251+ }
252+
253+ /**
254+ * Two {@link ScenarioContainer} instances are equal if and only if their
255+ * inputs are equal.
256+ */
257+ @ Override
258+ public boolean equals (Object obj ) {
259+ if (this == obj ) {
260+ return true ;
261+ }
262+ if (obj == null ) {
263+ return false ;
264+ }
265+ if (getClass () != obj .getClass ()) {
266+ return false ;
267+ }
268+ ScenarioContainer other = (ScenarioContainer ) obj ;
269+ return Objects .equals (data , other .data );
270+ }
271+ }
0 commit comments