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