View Javadoc
1   package org.opentrafficsim.core.gtu.behavioralcharacteristics;
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.djunits.value.vdouble.scalar.DoubleScalarInterface;
12  
13  import nl.tudelft.simulation.language.Throw;
14  import nl.tudelft.simulation.language.reflection.ClassUtil;
15  
16  /**
17   * In this class a set of behavioral characteristics in the form of parameters can be stored for use in behavioral models.
18   * <p>
19   * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20   * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
21   * <p>
22   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 13, 2016 <br>
23   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
24   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
25   */
26  public class BehavioralCharacteristics implements Serializable
27  {
28  
29      /** */
30      private static final long serialVersionUID = 20160400L;
31  
32      /**
33       * Object to recognize that no value was set previously.
34       */
35      private static final Empty EMPTY = new Empty();
36  
37      /** Whether to copy internal data on write. */
38      private boolean copyOnWrite = false;
39  
40      /** List of parameters. */
41      private Map<AbstractParameterType<?>, DoubleScalarInterface> parameters;
42  
43      /** List of parameters with values before last set. */
44      private Map<AbstractParameterType<?>, DoubleScalarInterface> previous;
45  
46      /**
47       * Empty constructor.
48       */
49      public BehavioralCharacteristics()
50      {
51          this.parameters = new HashMap<>();
52          this.previous = new HashMap<>();
53      }
54  
55      /**
56       * Constructor which creates a copy of the input set.
57       * @param behavioralCharacteristics input set
58       */
59      public BehavioralCharacteristics(final BehavioralCharacteristics behavioralCharacteristics)
60      {
61          this.parameters = behavioralCharacteristics.parameters;
62          this.previous = behavioralCharacteristics.previous;
63          this.copyOnWrite = true;
64          behavioralCharacteristics.copyOnWrite = true;
65      }
66  
67      /**
68       * Set parameter value of given parameter type.
69       * @param parameterType Parameter type.
70       * @param value Value.
71       * @param <T> Class of value.
72       * @throws ParameterException If the value does not comply with value type constraints.
73       */
74      public final <T extends DoubleScalarInterface> void setParameter(final ParameterType<T> parameterType, final T value)
75              throws ParameterException
76      {
77          Throw.when(value == null, ParameterException.class,
78                  "Parameter of type '%s' was assigned a null value, this is not allowed.", parameterType.getId());
79          parameterType.check(value, this);
80          saveSetParameter(parameterType, value);
81      }
82  
83      /**
84       * Set parameter value of given parameter type.
85       * @param parameterType Parameter type.
86       * @param value Value.
87       */
88      public final void setParameter(final ParameterTypeBoolean parameterType, final boolean value)
89      {
90          try
91          {
92              saveSetParameter(parameterType, new Dimensionless(value ? 1.0 : 0.0, DimensionlessUnit.SI));
93          }
94          catch (ParameterException pe)
95          {
96              // This cannot occur as the ParameterTypeBoolean constructor does not allow a default check.
97              throw new RuntimeException("ParameterTypeBoolean default check throws a ParameterException.", pe);
98          }
99      }
100 
101     /**
102      * Set parameter value of given parameter type.
103      * @param parameterType Parameter type.
104      * @param value Value.
105      * @throws ParameterException If the value does not comply with value type constraints.
106      */
107     public final void setParameter(final ParameterTypeDouble parameterType, final double value) throws ParameterException
108     {
109         parameterType.check(value, this);
110         saveSetParameter(parameterType, new Dimensionless(value, DimensionlessUnit.SI));
111     }
112 
113     /**
114      * Set parameter value of given parameter type.
115      * @param parameterType Parameter type.
116      * @param value Value.
117      * @throws ParameterException If the value does not comply with value type constraints.
118      */
119     public final void setParameter(final ParameterTypeInteger parameterType, final int value) throws ParameterException
120     {
121         parameterType.check(value, this);
122         saveSetParameter(parameterType, new Dimensionless(value, DimensionlessUnit.SI));
123     }
124 
125     /**
126      * Remembers the current value, or if it is not given, for possible reset.
127      * @param parameterType Parameter type.
128      * @param value Value.
129      * @param <T> Class of the value.
130      * @throws ParameterException If the value does not comply with constraints.
131      */
132     private <T extends DoubleScalarInterface> void saveSetParameter(final AbstractParameterType<T> parameterType, final T value)
133             throws ParameterException
134     {
135         parameterType.checkCheck(value);
136         checkCopyOnWrite();
137         if (this.parameters.containsKey(parameterType))
138         {
139             this.previous.put(parameterType, this.parameters.get(parameterType));
140         }
141         else
142         {
143             // remember that there was no value before this set
144             this.previous.put(parameterType, EMPTY);
145         }
146         this.parameters.put(parameterType, value);
147     }
148 
149     /**
150      * Resets the parameter value to the value from before the last set. This goes only a single value back.
151      * @param parameterType Parameter type.
152      * @throws ParameterException If the parameter was never set.
153      */
154     public final void resetParameter(final AbstractParameterType<?> parameterType) throws ParameterException
155     {
156         Throw.when(!this.previous.containsKey(parameterType), ParameterException.class,
157                 "Reset on parameter of type '%s' could not be performed, it was not set.", parameterType.getId());
158         checkCopyOnWrite();
159         if (this.previous.get(parameterType) instanceof Empty)
160         {
161             // no value was set before last set, so make parameter type not set
162             this.parameters.remove(parameterType);
163         }
164         else
165         {
166             this.parameters.put(parameterType, this.previous.get(parameterType));
167         }
168         this.previous.remove(parameterType); // prevent consecutive resets
169     }
170 
171     /**
172      * Check if internal data needs to be copied.
173      */
174     private void checkCopyOnWrite()
175     {
176         if (this.copyOnWrite)
177         {
178             this.parameters = new HashMap<>(this.parameters);
179             this.previous = new HashMap<>(this.previous);
180             this.copyOnWrite = false;
181         }
182     }
183 
184     /**
185      * Get parameter of given type.
186      * @param parameterType Parameter type.
187      * @param <T> Class of value.
188      * @return Parameter of given type.
189      * @throws ParameterException If parameter was never set.
190      */
191     @SuppressWarnings("checkstyle:designforextension")
192     public <T extends DoubleScalarInterface> T getParameter(final ParameterType<T> parameterType) throws ParameterException
193     {
194         checkContains(parameterType);
195         @SuppressWarnings("unchecked")
196         // set methods guarantee matching of parameter type and value
197         T result = (T) this.parameters.get(parameterType);
198         return result;
199     }
200 
201     /**
202      * Get parameter of given type.
203      * @param parameterType Parameter type.
204      * @return Parameter of given type.
205      * @throws ParameterException If parameter was never set.
206      */
207     public final boolean getParameter(final ParameterTypeBoolean parameterType) throws ParameterException
208     {
209         checkContains(parameterType);
210         return this.parameters.get(parameterType).getSI() != 0.0;
211     }
212 
213     /**
214      * Get parameter of given type.
215      * @param parameterType Parameter type.
216      * @return Parameter of given type.
217      * @throws ParameterException If parameter was never set.
218      */
219     public final int getParameter(final ParameterTypeInteger parameterType) throws ParameterException
220     {
221         checkContains(parameterType);
222         return (int) this.parameters.get(parameterType).getSI();
223     }
224 
225     /**
226      * Get parameter of given type.
227      * @param parameterType Parameter type.
228      * @return Parameter of given type.
229      * @throws ParameterException If parameter was never set.
230      */
231     public final double getParameter(final ParameterTypeDouble parameterType) throws ParameterException
232     {
233         checkContains(parameterType);
234         return this.parameters.get(parameterType).getSI();
235     }
236 
237     /**
238      * Check whether parameter has been set.
239      * @param parameterType Parameter type.
240      * @throws ParameterException If parameter is not present.
241      */
242     private void checkContains(final AbstractParameterType<?> parameterType) throws ParameterException
243     {
244         Throw.when(!contains(parameterType), ParameterException.class,
245                 "Could not get parameter of type '%s' as it was not set.", parameterType.getId());
246     }
247 
248     /**
249      * Whether the given parameter type has been set.
250      * @param parameterType Parameter type.
251      * @return Whether the given parameter type has been set.
252      */
253     public final boolean contains(final AbstractParameterType<?> parameterType)
254     {
255         return this.parameters.containsKey(parameterType);
256     }
257 
258     /**
259      * Returns a safe copy of the parameters.
260      * @return Safe copy of the parameters, e.g., for printing.
261      */
262     public final Map<AbstractParameterType<?>, DoubleScalarInterface> getParameters()
263     {
264         return new HashMap<>(this.parameters);
265     }
266 
267     /**
268      * Sets the default value of a parameter.
269      * @param parameter parameter to set the default value of
270      * @param <T> Class of the value.
271      * @return this set of behavioral characteristics (for method chaining)
272      * @throws ParameterException if the parameter type has no default value
273      */
274     @SuppressWarnings("unchecked")
275     public final <T extends DoubleScalarInterface> BehavioralCharacteristics setDefaultParameter(
276             final AbstractParameterType<T> parameter) throws ParameterException
277     {
278         T defaultValue;
279         if (parameter.getDefaultValue() instanceof DoubleScalarInterface)
280         {
281             // all types based on DJUNITS
282             defaultValue = (T) parameter.getDefaultValue();
283         }
284         else if (parameter.getDefaultValue() instanceof Boolean)
285         {
286             // boolean
287             defaultValue = (T) new Dimensionless((boolean) parameter.getDefaultValue() ? 1.0 : 0.0, DimensionlessUnit.SI);
288         }
289         else
290         {
291             // double or integer
292             defaultValue = (T) new Dimensionless(((Number) parameter.getDefaultValue()).doubleValue(), DimensionlessUnit.SI);
293         }
294         try
295         {
296             saveSetParameter(parameter, defaultValue);
297         }
298         catch (ParameterException pe)
299         {
300             // should not happen, default value and parameter type are connected
301             throw new RuntimeException(pe);
302         }
303         return this;
304     }
305 
306     /**
307      * Sets the default values of all accessible parameters defined in the given class.
308      * @param clazz class with parameters
309      * @return this set of behavioral characteristics (for method chaining)
310      */
311     public final BehavioralCharacteristics setDefaultParameters(final Class<?> clazz)
312     {
313         return setDefaultParametersLocal(clazz);
314     }
315 
316     /**
317      * Sets the default values of all accessible parameters defined in the given class.
318      * @param clazz class with parameters
319      * @param <T> Class of the value.
320      * @return this set of behavioral characteristics (for method chaining)
321      */
322     @SuppressWarnings("unchecked")
323     private <T extends DoubleScalarInterface> BehavioralCharacteristics setDefaultParametersLocal(final Class<?> clazz)
324     {
325         // set all default values using reflection
326         Set<Field> fields = ClassUtil.getAllFields(clazz);
327 
328         for (Field field : fields)
329         {
330             if (AbstractParameterType.class.isAssignableFrom(field.getType()))
331             {
332                 try
333                 {
334                     field.setAccessible(true);
335                     AbstractParameterType<T> p = (AbstractParameterType<T>) field.get(clazz);
336                     T defaultValue;
337                     if (p.getDefaultValue() instanceof DoubleScalarInterface)
338                     {
339                         // all types based on DJUNITS
340                         defaultValue = (T) p.getDefaultValue();
341                     }
342                     else if (p.getDefaultValue() instanceof Boolean)
343                     {
344                         // boolean
345                         defaultValue = (T) new Dimensionless((boolean) p.getDefaultValue() ? 1.0 : 0.0, DimensionlessUnit.SI);
346                     }
347                     else
348                     {
349                         // double or integer
350                         defaultValue =
351                                 (T) new Dimensionless(((Number) p.getDefaultValue()).doubleValue(), DimensionlessUnit.SI);
352                     }
353                     saveSetParameter(p, defaultValue);
354                 }
355                 catch (IllegalArgumentException iare)
356                 {
357                     // should not happen, field and clazz are related
358                     throw new RuntimeException(iare);
359                 }
360                 catch (IllegalAccessException iace)
361                 {
362                     // parameter type not public
363                 }
364                 catch (ParameterException pe)
365                 {
366                     // do not set parameter without default value
367                 }
368             }
369         }
370 
371         return this;
372     }
373 
374     /**
375      * Sets all behavioral characteristics from the given set in this set.
376      * @param behavioralCharacteristics set of behavioral characteristics to include in this set
377      */
378     public final void setAll(final BehavioralCharacteristics behavioralCharacteristics)
379     {
380         for (AbstractParameterType<?> key : behavioralCharacteristics.parameters.keySet())
381         {
382             this.parameters.put(key, behavioralCharacteristics.parameters.get(key));
383         }
384     }
385 
386     /** {@inheritDoc} */
387     public final String toString()
388     {
389         StringBuilder out = new StringBuilder("BehavioralCharacteristics [");
390         String sep = "";
391         for (AbstractParameterType<?> apt : this.parameters.keySet())
392         {
393             try
394             {
395                 out.append(sep).append(apt.getId()).append("=").append(apt.printValue(this));
396                 sep = ", ";
397             }
398             catch (ParameterException pe)
399             {
400                 // We know the parameter has been set as we get the keySet from parameters
401             }
402         }
403         out.append("]");
404         return out.toString();
405     }
406 
407     /**
408      * Class to put in a HashMap to recognize that no value was set at some point.
409      * <p>
410      * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
411      * <br>
412      * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
413      * <p>
414      * @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 14, 2016 <br>
415      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
416      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
417      */
418     private static class Empty extends Dimensionless
419     {
420         /** */
421         private static final long serialVersionUID = 20160414L;
422 
423         /**
424          * Empty constructor.
425          */
426         Empty()
427         {
428             super(Double.NaN, DimensionlessUnit.SI);
429         }
430     }
431 
432 }