1 package org.opentrafficsim.base.parameters;
2
3 import java.lang.reflect.Field;
4 import java.util.LinkedHashMap;
5 import java.util.Map;
6 import java.util.Optional;
7 import java.util.Set;
8
9 import org.djunits.unit.DimensionlessUnit;
10 import org.djunits.value.vdouble.scalar.Dimensionless;
11 import org.djutils.exceptions.Throw;
12 import org.djutils.reflection.ClassUtil;
13 import org.opentrafficsim.base.OtsRuntimeException;
14
15
16
17
18
19
20
21
22
23
24 public class ParameterSet implements Parameters
25 {
26
27
28 private static final Empty EMPTY = new Empty();
29
30
31 private boolean copyOnWrite = false;
32
33
34 private Map<ParameterType<?>, Object> parameters;
35
36
37 private Map<ParameterType<?>, Object> keys = new LinkedHashMap<>();
38
39
40 private Map<ParameterType<?>, Object> previous;
41
42
43
44
45 public ParameterSet()
46 {
47 this.parameters = new LinkedHashMap<>();
48 this.previous = new LinkedHashMap<>();
49 }
50
51
52
53
54
55 public ParameterSet(final Parameters parameters)
56 {
57 if (parameters instanceof ParameterSet)
58 {
59 ParameterSet parameterSet = (ParameterSet) parameters;
60 this.parameters = parameterSet.parameters;
61 this.previous = parameterSet.previous;
62 this.copyOnWrite = true;
63 parameterSet.copyOnWrite = true;
64 }
65 else
66 {
67 parameters.setAllIn(this);
68 }
69 }
70
71 @Override
72 public <T> void setParameter(final ParameterType<T> parameterType, final T value) throws ParameterException
73 {
74 Throw.whenNull(parameterType, "parameterType");
75 Throw.when(value == null, ParameterException.class,
76 "Parameter of type '%s' was assigned a null value, this is not allowed.", parameterType.getId());
77 Object key = this.keys.get(parameterType);
78 Throw.when(key != null, ParameterException.class, "Parameter of type '%s' is set, but it is claimed by '%s'.",
79 parameterType.getId(), key);
80 saveSetParameter(parameterType, value, false);
81 }
82
83 @Override
84 public <T> void setClaimedParameter(final ParameterType<T> parameterType, final T value, final Object key)
85 throws ParameterException
86 {
87 Throw.whenNull(parameterType, "parameterType");
88 Throw.when(value == null, ParameterException.class,
89 "Parameter of type '%s' was assigned a null value, this is not allowed.", parameterType.getId());
90 Throw.whenNull(key, "key");
91 Object previousKey = this.keys.putIfAbsent(parameterType, key);
92 Throw.when(previousKey != null && !key.equals(previousKey), ParameterException.class,
93 "Parameter of type '%s' was assigned trying to claim from '%s' but was claimed by '%s'.", parameterType.getId(),
94 key, previousKey);
95 saveSetParameter(parameterType, value, false);
96 }
97
98 @Override
99 public <T> void setParameterResettable(final ParameterType<T> parameterType, final T value) throws ParameterException
100 {
101 saveSetParameter(parameterType, value, true);
102 }
103
104
105
106
107
108
109
110
111
112 private <T> void saveSetParameter(final ParameterType<T> parameterType, final T value, final boolean resettable)
113 throws ParameterException
114 {
115 parameterType.check(value, this);
116 parameterType.checkConstraint(value);
117 checkCopyOnWrite();
118 if (resettable)
119 {
120 Object prevValue = this.parameters.get(parameterType);
121 if (prevValue == null)
122 {
123
124 this.previous.put(parameterType, EMPTY);
125 }
126 else
127 {
128 this.previous.put(parameterType, prevValue);
129 }
130 }
131 else
132 {
133
134 this.previous.remove(parameterType);
135 }
136 this.parameters.put(parameterType, value);
137 }
138
139 @Override
140 public void resetParameter(final ParameterType<?> parameterType) throws ParameterException
141 {
142 checkCopyOnWrite();
143 Object prevValue = this.previous.remove(parameterType);
144 Throw.when(prevValue == null, ParameterException.class,
145 "Reset on parameter of type '%s' could not be performed, it was not set resettable.", parameterType.getId());
146 if (prevValue instanceof Empty)
147 {
148
149 this.parameters.remove(parameterType);
150 }
151 else
152 {
153 this.parameters.put(parameterType, prevValue);
154 }
155 }
156
157
158
159
160 private void checkCopyOnWrite()
161 {
162 if (this.copyOnWrite)
163 {
164 this.parameters = new LinkedHashMap<>(this.parameters);
165 this.previous = new LinkedHashMap<>(this.previous);
166 this.copyOnWrite = false;
167 }
168 }
169
170 @Override
171 public <T> T getParameter(final ParameterType<T> parameterType) throws ParameterException
172 {
173 @SuppressWarnings("unchecked")
174
175 T result = (T) this.parameters.get(parameterType);
176 Throw.when(result == null, ParameterException.class, "Could not get parameter of type '%s' as it was not set.",
177 parameterType.getId());
178 return result;
179 }
180
181 @Override
182 @SuppressWarnings("unchecked")
183 public <T> Optional<T> getOptionalParameter(final ParameterType<T> parameterType)
184 {
185
186 return Optional.ofNullable((T) this.parameters.get(parameterType));
187 }
188
189 @Override
190 public boolean contains(final ParameterType<?> parameterType)
191 {
192 return this.parameters.containsKey(parameterType);
193 }
194
195
196
197
198
199 public Map<ParameterType<?>, Object> getParameters()
200 {
201 return new LinkedHashMap<>(this.parameters);
202 }
203
204
205
206
207
208
209
210
211 public <T> ParameterSet setDefaultParameter(final ParameterType<T> parameter) throws ParameterException
212 {
213 T defaultValue = parameter.getDefaultValue();
214 try
215 {
216 saveSetParameter(parameter, defaultValue, false);
217 }
218 catch (ParameterException pe)
219 {
220
221 throw new OtsRuntimeException(pe);
222 }
223 return this;
224 }
225
226
227
228
229
230
231
232 public ParameterSet setDefaultParameters(final Class<?> clazz)
233 {
234 return setDefaultParametersLocal(clazz);
235 }
236
237
238
239
240
241
242
243 @SuppressWarnings("unchecked")
244 private <T> ParameterSet setDefaultParametersLocal(final Class<?> clazz)
245 {
246
247 Set<Field> fields = ClassUtil.getAllFields(clazz);
248
249 for (Field field : fields)
250 {
251 if (ParameterType.class.isAssignableFrom(field.getType()))
252 {
253 try
254 {
255 field.setAccessible(true);
256 ParameterType<T> p = (ParameterType<T>) field.get(clazz);
257 saveSetParameter(p, p.getDefaultValue(), false);
258 }
259 catch (IllegalArgumentException iare)
260 {
261
262 throw new OtsRuntimeException(iare);
263 }
264 catch (IllegalAccessException iace)
265 {
266
267 throw new OtsRuntimeException(iace);
268 }
269 catch (ParameterException pe)
270 {
271
272 throw new OtsRuntimeException(pe);
273 }
274 }
275 }
276 return this;
277 }
278
279 @Override
280 public void setAllIn(final Parameters params)
281 {
282 if (params instanceof ParameterSet)
283 {
284 ParameterSet parameterSet = (ParameterSet) params;
285 parameterSet.checkCopyOnWrite();
286 parameterSet.parameters.putAll(this.parameters);
287 }
288 else
289 {
290 setAllOneByOne(params);
291 }
292 }
293
294
295
296
297
298
299 @SuppressWarnings("unchecked")
300 private <T> void setAllOneByOne(final Parameters params)
301 {
302 for (ParameterType<?> parameterType : this.parameters.keySet())
303 {
304 try
305 {
306 params.setParameter((ParameterType<T>) parameterType, (T) this.parameters.get(parameterType));
307 }
308 catch (ParameterException exception)
309 {
310 throw new OtsRuntimeException(exception);
311 }
312 }
313 }
314
315 @Override
316 public String toString()
317 {
318 StringBuilder out = new StringBuilder("Parameters [");
319 String sep = "";
320 for (ParameterType<?> apt : this.parameters.keySet())
321 {
322 try
323 {
324 out.append(sep).append(apt.getId()).append("=").append(apt.printValue(this));
325 sep = ", ";
326 }
327 catch (ParameterException pe)
328 {
329
330 throw new OtsRuntimeException(pe);
331 }
332 }
333 out.append("]");
334 return out.toString();
335 }
336
337
338
339
340 private static class Empty extends Dimensionless
341 {
342
343 private static final long serialVersionUID = 20160414L;
344
345
346
347
348 Empty()
349 {
350 super(Double.NaN, DimensionlessUnit.SI);
351 }
352
353 @Override
354 public String toString()
355 {
356 return "Empty []";
357 }
358
359 }
360
361 }