View Javadoc
1   package org.opentrafficsim.road.network.factory.xml.parser;
2   
3   import java.io.Serializable;
4   import java.lang.reflect.Constructor;
5   import java.lang.reflect.InvocationTargetException;
6   import java.util.ArrayList;
7   import java.util.LinkedHashMap;
8   import java.util.LinkedHashSet;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Map.Entry;
12  import java.util.Set;
13  import java.util.function.BiFunction;
14  
15  import org.djunits.unit.Unit;
16  import org.djunits.value.vdouble.scalar.AbstractDoubleScalarRel;
17  import org.djunits.value.vdouble.scalar.Acceleration;
18  import org.djunits.value.vdouble.scalar.Frequency;
19  import org.djunits.value.vdouble.scalar.Length;
20  import org.djunits.value.vdouble.scalar.LinearDensity;
21  import org.djunits.value.vdouble.scalar.Speed;
22  import org.djutils.reflection.ClassUtil;
23  import org.opentrafficsim.base.parameters.ParameterException;
24  import org.opentrafficsim.base.parameters.ParameterSet;
25  import org.opentrafficsim.base.parameters.ParameterType;
26  import org.opentrafficsim.base.parameters.ParameterTypeDouble;
27  import org.opentrafficsim.base.parameters.ParameterTypeNumeric;
28  import org.opentrafficsim.base.parameters.Parameters;
29  import org.opentrafficsim.core.distributions.Generator;
30  import org.opentrafficsim.core.distributions.ProbabilityException;
31  import org.opentrafficsim.core.gtu.GTUException;
32  import org.opentrafficsim.core.gtu.GTUType;
33  import org.opentrafficsim.core.gtu.perception.DirectEgoPerception;
34  import org.opentrafficsim.core.gtu.perception.PerceptionCategory;
35  import org.opentrafficsim.core.parameters.InputParameters;
36  import org.opentrafficsim.core.parameters.ParameterFactory;
37  import org.opentrafficsim.core.parameters.ParameterFactoryByType;
38  import org.opentrafficsim.core.units.distributions.ContinuousDistDoubleScalar;
39  import org.opentrafficsim.road.gtu.lane.LaneBasedGTU;
40  import org.opentrafficsim.road.gtu.lane.perception.CategoricalLanePerception;
41  import org.opentrafficsim.road.gtu.lane.perception.LanePerception;
42  import org.opentrafficsim.road.gtu.lane.perception.PerceptionFactory;
43  import org.opentrafficsim.road.gtu.lane.perception.categories.AnticipationTrafficPerception;
44  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectBusStopPerception;
45  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectInfrastructurePerception;
46  import org.opentrafficsim.road.gtu.lane.perception.categories.DirectIntersectionPerception;
47  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.Anticipation;
48  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.DirectNeighborsPerception;
49  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.Estimation;
50  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType;
51  import org.opentrafficsim.road.gtu.lane.perception.categories.neighbors.HeadwayGtuType.PerceivedHeadwayGtuType;
52  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationHeadway;
53  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSituationalAwareness;
54  import org.opentrafficsim.road.gtu.lane.perception.mental.AdaptationSpeed;
55  import org.opentrafficsim.road.gtu.lane.perception.mental.Fuller;
56  import org.opentrafficsim.road.gtu.lane.perception.mental.Fuller.BehavioralAdaptation;
57  import org.opentrafficsim.road.gtu.lane.perception.mental.Mental;
58  import org.opentrafficsim.road.gtu.lane.perception.mental.Task;
59  import org.opentrafficsim.road.gtu.lane.perception.mental.TaskManager;
60  import org.opentrafficsim.road.gtu.lane.perception.mental.TaskManager.SummativeTaskManager;
61  import org.opentrafficsim.road.gtu.lane.tactical.LaneBasedTacticalPlannerFactory;
62  import org.opentrafficsim.road.gtu.lane.tactical.following.AbstractIDM;
63  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModel;
64  import org.opentrafficsim.road.gtu.lane.tactical.following.CarFollowingModelFactory;
65  import org.opentrafficsim.road.gtu.lane.tactical.following.DesiredHeadwayModel;
66  import org.opentrafficsim.road.gtu.lane.tactical.following.DesiredSpeedModel;
67  import org.opentrafficsim.road.gtu.lane.tactical.following.IDM;
68  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlus;
69  import org.opentrafficsim.road.gtu.lane.tactical.following.IDMPlusFactory;
70  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationBusStop;
71  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationConflicts;
72  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationIncentive;
73  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationNoRightOvertake;
74  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationSpeedLimitTransition;
75  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.AccelerationTrafficLights;
76  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.DefaultLMRSPerceptionFactory;
77  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveBusStop;
78  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveCourtesy;
79  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveDummy;
80  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveGetInLane;
81  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveKeep;
82  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveRoute;
83  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveSocioSpeed;
84  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveSpeedWithCourtesy;
85  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.IncentiveStayRight;
86  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRSFactory;
87  import org.opentrafficsim.road.gtu.lane.tactical.lmrs.SocioDesiredSpeed;
88  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Cooperation;
89  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.GapAcceptance;
90  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.MandatoryIncentive;
91  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Synchronization;
92  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.Tailgating;
93  import org.opentrafficsim.road.gtu.lane.tactical.util.lmrs.VoluntaryIncentive;
94  import org.opentrafficsim.road.gtu.strategical.LaneBasedStrategicalPlannerFactory;
95  import org.opentrafficsim.road.gtu.strategical.route.LaneBasedStrategicalRoutePlannerFactory;
96  import org.opentrafficsim.road.network.OTSRoadNetwork;
97  import org.opentrafficsim.road.network.factory.xml.XmlParserException;
98  import org.opentrafficsim.road.network.factory.xml.utils.ParseDistribution;
99  import org.opentrafficsim.road.network.factory.xml.utils.StreamInformation;
100 import org.opentrafficsim.xml.generated.CARFOLLOWINGMODELHEADWAYSPEEDTYPE;
101 import org.opentrafficsim.xml.generated.CARFOLLOWINGMODELHEADWAYSPEEDTYPE.DESIREDHEADWAYMODEL;
102 import org.opentrafficsim.xml.generated.CARFOLLOWINGMODELTYPE;
103 import org.opentrafficsim.xml.generated.DESIREDSPEEDMODELTYPE;
104 import org.opentrafficsim.xml.generated.MODELTYPE;
105 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.ACCELERATION;
106 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.ACCELERATIONDIST;
107 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.BOOLEAN;
108 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.CLASS;
109 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.DOUBLE;
110 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.DOUBLEDIST;
111 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.FRACTION;
112 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.FREQUENCY;
113 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.FREQUENCYDIST;
114 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.INTEGER;
115 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.INTEGERDIST;
116 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.LENGTH;
117 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.LENGTHDIST;
118 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.LINEARDENSITY;
119 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.LINEARDENSITYDIST;
120 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.SPEED;
121 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.SPEEDDIST;
122 import org.opentrafficsim.xml.generated.MODELTYPE.MODELPARAMETERS.STRING;
123 import org.opentrafficsim.xml.generated.MODELTYPE.TACTICALPLANNER.LMRS;
124 import org.opentrafficsim.xml.generated.MODELTYPE.TACTICALPLANNER.LMRS.ACCELERATIONINCENTIVES;
125 import org.opentrafficsim.xml.generated.MODELTYPE.TACTICALPLANNER.LMRS.MANDATORYINCENTIVES;
126 import org.opentrafficsim.xml.generated.MODELTYPE.TACTICALPLANNER.LMRS.VOLUNTARYINCENTIVES;
127 import org.opentrafficsim.xml.generated.PERCEPTIONTYPE;
128 import org.opentrafficsim.xml.generated.PERCEPTIONTYPE.CATEGORY;
129 import org.opentrafficsim.xml.generated.PERCEPTIONTYPE.HEADWAYGTUTYPE.PERCEIVED;
130 import org.opentrafficsim.xml.generated.PERCEPTIONTYPE.MENTAL.FULLER;
131 import org.opentrafficsim.xml.generated.PERCEPTIONTYPE.MENTAL.FULLER.BEHAVIORALADAPTATION;
132 
133 import nl.tudelft.simulation.dsol.model.inputparameters.InputParameter;
134 import nl.tudelft.simulation.jstats.distributions.DistContinuous;
135 import nl.tudelft.simulation.jstats.distributions.DistDiscrete;
136 
137 /**
138  * Parser of the {@code MODEL} tags. Returns a map of strategical planner factories by model ID for use in demand parsing.
139  * <p>
140  * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
141  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
142  * <p>
143  * @version $Revision$, $LastChangedDate$, by $Author$, initial version Mar 29, 2019 <br>
144  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
145  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
146  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
147  */
148 public class ModelParser
149 {
150     /** */
151     private ModelParser()
152     {
153         // static class
154     }
155 
156     /**
157      * Parse parameter factories.
158      * @param otsNetwork OTSRoadNetwork; network
159      * @param models List&lt;MODEL&gt;; models
160      * @param inputParameters InputParameters; input parameters
161      * @param parameterTypes Map&lt;String, ParameterType&lt;?&gt;&gt;; parameter types
162      * @param streamMap Map&lt;String, StreamInformation&gt;; stream information
163      * @param <U> a unit
164      * @param <T> a scalar type
165      * @param <K> a parameter type value
166      * @return Map&lt;String, ParameterFactory&lt;?&gt;&gt;; parameter factories by model ID
167      * @throws XmlParserException unknown value, missing constructor, etc.
168      */
169     @SuppressWarnings("unchecked")
170     public static <U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>, K> Map<String, ParameterFactory> parseParameters(
171             final OTSRoadNetwork otsNetwork, final List<MODELTYPE> models, final InputParameters inputParameters,
172             final Map<String, ParameterType<?>> parameterTypes, final Map<String, StreamInformation> streamMap)
173             throws XmlParserException
174     {
175         Map<String, ParameterFactory> map = new LinkedHashMap<>();
176         for (MODELTYPE model : models)
177         {
178             // Parameter factory
179             ParameterFactoryByType paramFactory = new ParameterFactoryByType();
180             // set model parameters
181             if (model.getMODELPARAMETERS() != null)
182             {
183                 for (Serializable parameter : model.getMODELPARAMETERS().getSTRINGOrACCELERATIONOrACCELERATIONDIST())
184                 {
185                     if (parameter instanceof STRING)
186                     {
187                         STRING p = (STRING) parameter;
188                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
189                                 (ParameterType<String>) parameterTypes.get(p.getID()), p.getValue());
190                     }
191                     else if (parameter instanceof ACCELERATION)
192                     {
193                         ACCELERATION p = (ACCELERATION) parameter;
194                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
195                                 (ParameterTypeNumeric<Acceleration>) parameterTypes.get(p.getID()), p.getValue());
196                     }
197                     else if (parameter instanceof ACCELERATIONDIST)
198                     {
199                         ACCELERATIONDIST p = (ACCELERATIONDIST) parameter;
200                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
201                                 (ParameterTypeNumeric<Acceleration>) parameterTypes.get(p.getID()),
202                                 ParseDistribution.parseAccelerationDist(streamMap, p));
203                     }
204                     else if (parameter instanceof BOOLEAN)
205                     {
206                         BOOLEAN p = (BOOLEAN) parameter;
207                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
208                                 (ParameterType<Boolean>) parameterTypes.get(p.getID()), p.isValue());
209                     }
210                     else if (parameter instanceof CLASS)
211                     {
212                         CLASS p = (CLASS) parameter;
213                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
214                                 (ParameterType<Class<?>>) parameterTypes.get(p.getID()), p.getValue());
215                     }
216                     else if (parameter instanceof DOUBLE)
217                     {
218                         DOUBLE p = (DOUBLE) parameter;
219                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
220                                 (ParameterType<Double>) parameterTypes.get(p.getID()), p.getValue());
221                     }
222                     else if (parameter instanceof DOUBLEDIST)
223                     {
224                         DOUBLEDIST p = (DOUBLEDIST) parameter;
225                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
226                                 (ParameterType<Double>) parameterTypes.get(p.getID()),
227                                 ParseDistribution.makeDistContinuous(streamMap, p));
228                     }
229                     else if (parameter instanceof FRACTION)
230                     {
231                         FRACTION p = (FRACTION) parameter;
232                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
233                                 (ParameterType<Double>) parameterTypes.get(p.getID()), p.getValue());
234                     }
235                     else if (parameter instanceof FREQUENCY)
236                     {
237                         FREQUENCY p = (FREQUENCY) parameter;
238                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
239                                 (ParameterTypeNumeric<Frequency>) parameterTypes.get(p.getID()), p.getValue());
240                     }
241                     else if (parameter instanceof FREQUENCYDIST)
242                     {
243                         FREQUENCYDIST p = (FREQUENCYDIST) parameter;
244                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
245                                 (ParameterTypeNumeric<Frequency>) parameterTypes.get(p.getID()),
246                                 ParseDistribution.parseFrequencyDist(streamMap, p));
247                     }
248                     else if (parameter instanceof INTEGER)
249                     {
250                         INTEGER p = (INTEGER) parameter;
251                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
252                                 (ParameterType<Integer>) parameterTypes.get(p.getID()), p.getValue());
253                     }
254                     else if (parameter instanceof INTEGERDIST)
255                     {
256                         INTEGERDIST p = (INTEGERDIST) parameter;
257                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
258                                 (ParameterType<Integer>) parameterTypes.get(p.getID()),
259                                 ParseDistribution.makeDistDiscrete(streamMap, p));
260                     }
261                     else if (parameter instanceof LENGTH)
262                     {
263                         LENGTH p = (LENGTH) parameter;
264                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
265                                 (ParameterType<Length>) parameterTypes.get(p.getID()), p.getValue());
266                     }
267                     else if (parameter instanceof LENGTHDIST)
268                     {
269                         LENGTHDIST p = (LENGTHDIST) parameter;
270                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
271                                 (ParameterTypeNumeric<Length>) parameterTypes.get(p.getID()),
272                                 ParseDistribution.parseLengthDist(streamMap, p));
273                     }
274                     else if (parameter instanceof LINEARDENSITY)
275                     {
276                         LINEARDENSITY p = (LINEARDENSITY) parameter;
277                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
278                                 (ParameterType<LinearDensity>) parameterTypes.get(p.getID()), p.getValue());
279                     }
280                     else if (parameter instanceof LINEARDENSITYDIST)
281                     {
282                         LINEARDENSITYDIST p = (LINEARDENSITYDIST) parameter;
283                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
284                                 (ParameterTypeNumeric<LinearDensity>) parameterTypes.get(p.getID()),
285                                 ParseDistribution.parseLinearDensityDist(streamMap, p));
286                     }
287                     else if (parameter instanceof SPEED)
288                     {
289                         SPEED p = (SPEED) parameter;
290                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
291                                 (ParameterType<Speed>) parameterTypes.get(p.getID()), p.getValue());
292                     }
293                     else if (parameter instanceof SPEEDDIST)
294                     {
295                         SPEEDDIST p = (SPEEDDIST) parameter;
296                         paramFactory.addParameter(getGtuType(p.getGTUTYPE(), otsNetwork),
297                                 (ParameterTypeNumeric<Speed>) parameterTypes.get(p.getID()),
298                                 ParseDistribution.parseSpeedDist(streamMap, p));
299                     }
300                 }
301             }
302             // set input parameters, these may override the above parameters
303             for (GTUType gtuType : inputParameters.getObjects(GTUType.class))
304             {
305                 for (Entry<String, InputParameter<?, ?>> entry : inputParameters.getInputParameters(gtuType).entrySet())
306                 {
307                     if (parameterTypes.containsKey(entry.getKey()))
308                     {
309                         ParameterType<?> parameterType = parameterTypes.get(entry.getKey());
310                         InputParameter<?, ?> inputParameter = entry.getValue();
311                         if (inputParameter.getValue() instanceof DistContinuous)
312                         {
313                             if (parameterType instanceof ParameterTypeDouble)
314                             {
315                                 paramFactory.addParameter((ParameterTypeDouble) parameterType,
316                                         (DistContinuous) inputParameter.getValue());
317                             }
318                             else
319                             {
320                                 paramFactory.addParameter((ParameterTypeNumeric<T>) parameterType,
321                                         (ContinuousDistDoubleScalar.Rel<T, U>) inputParameter.getValue());
322                             }
323                         }
324                         else if (inputParameter.getValue() instanceof DistDiscrete)
325                         {
326                             paramFactory.addParameter(gtuType, (ParameterType<Integer>) parameterType,
327                                     (DistDiscrete) inputParameter.getValue());
328                         }
329                         else
330                         {
331                             paramFactory.addParameter(gtuType, (ParameterType<K>) parameterType, (K) inputParameter.getValue());
332                         }
333                     }
334                 }
335             }
336         }
337         return map;
338     }
339 
340     /**
341      * Creates strategical planner factories for models.
342      * @param otsNetwork OTSRoadNetwork; network
343      * @param models List&lt;MODEL&gt;; models
344      * @param inputParameters InputParameters; input parameters
345      * @param parameterTypes Map&lt;String, ParameterType&lt;?&gt;&gt;; parameter types
346      * @param streamMap Map&lt;String, StreamInformation&gt;; stream information
347      * @param parameterFactories Map&lt;String, ParameterFactory&gt;; parameter factories
348      * @param <U> a unit
349      * @param <T> a scalar type
350      * @param <K> a parameter type value
351      * @return Map&lt;String, LaneBasedStrategicalPlannerFactory&lt;?&gt;&gt;; strategical planner factories by model ID
352      * @throws XmlParserException unknown value, missing constructor, etc.
353      */
354     public static <U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>, K> Map<String, LaneBasedStrategicalPlannerFactory<?>> parseModel(
355             final OTSRoadNetwork otsNetwork, final List<MODELTYPE> models, final InputParameters inputParameters,
356             final Map<String, ParameterType<?>> parameterTypes, final Map<String, StreamInformation> streamMap,
357             final Map<String, ParameterFactory> parameterFactories) throws XmlParserException
358     {
359         Map<String, LaneBasedStrategicalPlannerFactory<?>> factories = new LinkedHashMap<>();
360         for (MODELTYPE model : models)
361         {
362             // Parameter factory
363             ParameterFactory paramFactory = parameterFactories.get(model.getID());
364 
365             // Tactical planner
366             LaneBasedTacticalPlannerFactory<?> tacticalPlannerFactory;
367             if (model.getTACTICALPLANNER() != null)
368             {
369                 if (model.getTACTICALPLANNER().getLMRS() != null)
370                 {
371                     tacticalPlannerFactory = parseLmrs(model.getTACTICALPLANNER().getLMRS());
372                 }
373                 else
374                 {
375                     throw new XmlParserException("Tactical planner has unsupported value.");
376                 }
377             }
378             else
379             {
380                 // default
381                 try
382                 {
383                     tacticalPlannerFactory = new LMRSFactory(new IDMPlusFactory(streamMap.get("generation").getStream()),
384                             new DefaultLMRSPerceptionFactory());
385                 }
386                 catch (GTUException exception)
387                 {
388                     throw new XmlParserException(exception);
389                 }
390             }
391 
392             LaneBasedStrategicalPlannerFactory<?> strategicalPlannerFactory;
393             if (model.getSTRATEGICALPLANNER() == null || model.getSTRATEGICALPLANNER().getROUTE() != null)
394             {
395                 // TODO: RouteGeneratorOD as third argument, which may however be based on demand
396                 strategicalPlannerFactory = new LaneBasedStrategicalRoutePlannerFactory(tacticalPlannerFactory, paramFactory);
397             }
398             else
399             {
400                 throw new XmlParserException("Strategical planner has unsupported value.");
401             }
402 
403             factories.put(model.getID(), strategicalPlannerFactory);
404         }
405         return factories;
406     }
407 
408     @SuppressWarnings("unchecked")
409     private static <T> ParameterType<T> getParameterType(final String field, final Class<T> type)
410     {
411         int dot = field.lastIndexOf(".");
412         String className = field.substring(0, dot);
413         String fieldName = field.substring(dot + 1);
414         try
415         {
416             return (ParameterType<T>) ClassUtil.resolveField(Class.forName(className), fieldName).get(null);
417         }
418         catch (NoSuchFieldException | ClassCastException | IllegalArgumentException | IllegalAccessException
419                 | ClassNotFoundException exception)
420         {
421             throw new RuntimeException(
422                     "Unable to find parameter " + field + ", it may not be accessible or it is not a parameter of type " + type,
423                     exception);
424         }
425     }
426 
427     /**
428      * @param field a field
429      * @param type a class
430      * @param <T> a number type
431      * @return the ParameterTypeNumeric belonging to the field
432      */
433     @SuppressWarnings("unchecked")
434     private static <T extends Number> ParameterTypeNumeric<T> getParameterTypeNumeric(final String field, final Class<T> type)
435     {
436         int dot = field.lastIndexOf(".");
437         String className = field.substring(0, dot);
438         String fieldName = field.substring(dot + 1);
439         try
440         {
441             return (ParameterTypeNumeric<T>) ClassUtil.resolveField(Class.forName(className), fieldName).get(null);
442         }
443         catch (NoSuchFieldException | ClassCastException | IllegalArgumentException | IllegalAccessException
444                 | ClassNotFoundException exception)
445         {
446             throw new RuntimeException(
447                     "Unable to find parameter " + field + ", it may not be accessible or it is not a parameter of type " + type,
448                     exception);
449         }
450     }
451 
452     /**
453      * @param gtuTypeId the gtu type
454      * @param network the network
455      * @return the GTUType belonging to the id
456      */
457     private static GTUType getGtuType(final String gtuTypeId, final OTSRoadNetwork network)
458     {
459         if (gtuTypeId == null)
460         {
461             return null;
462         }
463         return network.getGtuType(gtuTypeId);
464     }
465 
466     /**
467      * Parse LMRS model.
468      * @param lmrs LMRS; LMRS information
469      * @return LaneBasedTacticalPlannerFactory&lt;LMRS&gt;; LMRS factory
470      * @throws XmlParserException unknown value, missing constructor, etc.
471      */
472     private static LaneBasedTacticalPlannerFactory<org.opentrafficsim.road.gtu.lane.tactical.lmrs.LMRS> parseLmrs(
473             final LMRS lmrs) throws XmlParserException
474     {
475         // Synchronization
476         Synchronization synchronization;
477         switch (lmrs.getSYNCHRONIZATION() != null ? lmrs.getSYNCHRONIZATION() : "PASSIVE")
478         {
479             case "DEADEND":
480                 synchronization = Synchronization.DEADEND;
481                 break;
482             case "PASSIVE":
483                 synchronization = Synchronization.PASSIVE;
484                 break;
485             case "PASSIVEMOVING":
486                 synchronization = Synchronization.PASSIVE_MOVING;
487                 break;
488             case "ALIGNGAP":
489                 synchronization = Synchronization.ALIGN_GAP;
490                 break;
491             case "ACTIVE":
492                 synchronization = Synchronization.ACTIVE;
493                 break;
494             default:
495                 throw new XmlParserException("Synchronization " + lmrs.getSYNCHRONIZATION() + " is unknown.");
496         }
497 
498         // Cooperation
499         Cooperation cooperation;
500         switch (lmrs.getCOOPERATION() != null ? lmrs.getCOOPERATION() : "PASSIVE")
501         {
502             case "PASSIVE":
503                 cooperation = Cooperation.PASSIVE;
504                 break;
505             case "PASSIVEMOVING":
506                 cooperation = Cooperation.PASSIVE_MOVING;
507                 break;
508             case "ACTIVE":
509                 cooperation = Cooperation.ACTIVE;
510                 break;
511             default:
512                 throw new XmlParserException("Cooperation " + lmrs.getCOOPERATION() + " is unknown.");
513         }
514 
515         // Gap-acceptance
516         GapAcceptance gapAcceptance;
517         switch (lmrs.getGAPACCEPTANCE() != null ? lmrs.getGAPACCEPTANCE() : "INFORMED")
518         {
519             case "INFORMED":
520                 gapAcceptance = GapAcceptance.INFORMED;
521                 break;
522             case "EGOHEADWAY":
523                 gapAcceptance = GapAcceptance.EGO_HEADWAY;
524                 break;
525             default:
526                 throw new XmlParserException("GapAcceptance " + lmrs.getGAPACCEPTANCE() + " is unknown.");
527         }
528 
529         // Tailgating
530         Tailgating tailgating;
531         switch (lmrs.getTAILGATING() != null ? lmrs.getTAILGATING() : "NONE")
532         {
533             case "NONE":
534                 tailgating = Tailgating.NONE;
535                 break;
536             case "RHOONLY":
537                 tailgating = Tailgating.RHO_ONLY;
538                 break;
539             case "PRESSURE":
540                 tailgating = Tailgating.PRESSURE;
541                 break;
542             default:
543                 throw new XmlParserException("Tailgating " + lmrs.getTAILGATING() + " is unknown.");
544         }
545 
546         // Mandatory incentives
547         Set<MandatoryIncentive> mandatoryIncentives = new LinkedHashSet<>();
548         for (MANDATORYINCENTIVES.INCENTIVE incentive : lmrs.getMANDATORYINCENTIVES().getINCENTIVE())
549         {
550             switch (incentive.getValue())
551             {
552                 case "ROUTE":
553                     mandatoryIncentives.add(new IncentiveRoute());
554                     break;
555                 case "GETINLANE":
556                     mandatoryIncentives.add(new IncentiveGetInLane());
557                     break;
558                 case "BUSSTOP":
559                     mandatoryIncentives.add(new IncentiveBusStop());
560                     break;
561                 case "CLASS":
562                     try
563                     {
564                         mandatoryIncentives.add((MandatoryIncentive) ClassUtil
565                                 .resolveConstructor(incentive.getCLASS(), new Class<?>[0]).newInstance());
566                     }
567                     catch (InstantiationException | IllegalAccessException | IllegalArgumentException
568                             | InvocationTargetException | NoSuchMethodException | ClassCastException exception)
569                     {
570                         throw new XmlParserException("Class " + incentive.getCLASS()
571                                 + " does not have a valid empty contructor, or is not a MandatoryIncentive.");
572                     }
573                     break;
574                 default:
575                     throw new XmlParserException("MandatoryIncentive " + incentive.getValue() + " is unknown.");
576             }
577         }
578         if (mandatoryIncentives.isEmpty())
579         {
580             mandatoryIncentives.add(new IncentiveDummy());
581         }
582 
583         // Voluntary incentives
584         Set<VoluntaryIncentive> voluntaryIncentives = new LinkedHashSet<>();
585         for (VOLUNTARYINCENTIVES.INCENTIVE incentive : lmrs.getVOLUNTARYINCENTIVES().getINCENTIVE())
586         {
587             switch (incentive.getValue())
588             {
589                 case "KEEP":
590                     voluntaryIncentives.add(new IncentiveKeep());
591                     break;
592                 case "SPEEDWITHCOURTESY":
593                     voluntaryIncentives.add(new IncentiveSpeedWithCourtesy());
594                     break;
595                 case "COURTESY":
596                     voluntaryIncentives.add(new IncentiveCourtesy());
597                     break;
598                 case "SOCIOSPEED":
599                     voluntaryIncentives.add(new IncentiveSocioSpeed());
600                     break;
601                 case "STAYRIGHT":
602                     voluntaryIncentives.add(new IncentiveStayRight());
603                     break;
604                 case "CLASS":
605                     try
606                     {
607                         voluntaryIncentives.add((VoluntaryIncentive) ClassUtil
608                                 .resolveConstructor(incentive.getCLASS(), new Class<?>[0]).newInstance());
609                     }
610                     catch (InstantiationException | IllegalAccessException | IllegalArgumentException
611                             | InvocationTargetException | NoSuchMethodException | ClassCastException exception)
612                     {
613                         throw new XmlParserException("Class " + incentive.getCLASS()
614                                 + " does not have a valid empty contructor, or is not a VoluntaryIncentive.");
615                     }
616                     break;
617                 default:
618                     throw new XmlParserException("VoluntaryIncentive " + incentive.getValue() + " is unknown.");
619             }
620         }
621 
622         // Acceleration incentives
623         Set<AccelerationIncentive> accelerationIncentives = new LinkedHashSet<>();
624         for (ACCELERATIONINCENTIVES.INCENTIVE incentive : lmrs.getACCELERATIONINCENTIVES().getINCENTIVE())
625         {
626             switch (incentive.getValue())
627             {
628                 case "BUSSTOP":
629                     accelerationIncentives.add(new AccelerationBusStop());
630                     break;
631                 case "CONFLICTS":
632                     accelerationIncentives.add(new AccelerationConflicts());
633                     break;
634                 case "SPEEDLIMITTRANSITION":
635                     accelerationIncentives.add(new AccelerationSpeedLimitTransition());
636                     break;
637                 case "TRAFFICLIGHTS":
638                     accelerationIncentives.add(new AccelerationTrafficLights());
639                     break;
640                 case "NORIGHTOVERTAKE":
641                     accelerationIncentives.add(new AccelerationNoRightOvertake());
642                     break;
643                 case "CLASS":
644                     try
645                     {
646                         accelerationIncentives.add((AccelerationIncentive) ClassUtil
647                                 .resolveConstructor(incentive.getCLASS(), new Class<?>[0]).newInstance());
648                     }
649                     catch (InstantiationException | IllegalAccessException | IllegalArgumentException
650                             | InvocationTargetException | NoSuchMethodException | ClassCastException exception)
651                     {
652                         throw new XmlParserException("Class " + incentive.getCLASS()
653                                 + " does not have a valid empty contructor, or is not a AccelerationIncentive.");
654                     }
655                     break;
656                 default:
657                     throw new XmlParserException("AccelerationIncentive " + incentive.getValue() + " is unknown.");
658             }
659         }
660 
661         // Perception
662         PerceptionFactory perceptionFactory = parsePerception(lmrs.getPERCEPTION());
663         // in helper method
664 
665         // Car-following model
666         CarFollowingModelFactory<? extends CarFollowingModel> carFollowingModelFactory =
667                 parseCarFollowingModel(lmrs.getCARFOLLOWINGMODEL());
668 
669         // LMRS factory
670         return new LMRSFactory(carFollowingModelFactory, perceptionFactory, synchronization, cooperation, gapAcceptance,
671                 tailgating, mandatoryIncentives, voluntaryIncentives, accelerationIncentives);
672     }
673 
674     /**
675      * Parse car-following model.
676      * @param carFollowingModel CARFOLLOWINGMODELTYPE; car-following model information
677      * @return CarFollowingModelFactory&lt;? extends CarFollowingModel&gt;; car-following model factory
678      * @throws XmlParserException unknown value, missing constructor, etc.
679      */
680     private static CarFollowingModelFactory<? extends CarFollowingModel> parseCarFollowingModel(
681             final CARFOLLOWINGMODELTYPE carFollowingModel) throws XmlParserException
682     {
683         CarFollowingModelFactory<? extends CarFollowingModel> carFollowingModelFactory;
684         if (carFollowingModel.getIDM() != null)
685         {
686             carFollowingModelFactory =
687                     parseCarFollowingModelHeadwaySpeed(carFollowingModel.getIDM(), (headway, speed) -> new IDM(headway, speed));
688         }
689         else if (carFollowingModel.getIDMPLUS() != null)
690         {
691             carFollowingModelFactory = parseCarFollowingModelHeadwaySpeed(carFollowingModel.getIDMPLUS(),
692                     (headway, speed) -> new IDMPlus(headway, speed));
693         }
694         else
695         {
696             throw new XmlParserException("Car-following model has unsupported value.");
697         }
698         return carFollowingModelFactory;
699     }
700 
701     /**
702      * Parse a car-following model that accepts a desired headway and desired speed model in a constructor. These are specified
703      * in xsd as a {@code CARFOLLOWINGMODELHEADWAYSPEEDTYPE}. The argument {@code function} can be specified as a lambda
704      * function:
705      * 
706      * <pre>
707      * (headway, speed) -&gt; new MyCarFollowingModel(headway, speed)
708      * </pre>
709      * 
710      * @param carFollowingModelHeadwaySpeed CARFOLLOWINGMODELHEADWAYSPEEDTYPE; information about desired headway and speed model
711      * @param function BiFunction&lt;DesiredHeadwayModel, DesiredSpeedModel, T>&gt;; function to instantiate the model
712      * @param <T> car-following model type
713      * @return CarFollowingModelFactory&lt;T&gt; car-following model factory
714      * @throws XmlParserException unknown value, missing constructor, etc.
715      */
716     private static <T extends CarFollowingModel> CarFollowingModelFactory<T> parseCarFollowingModelHeadwaySpeed(
717             final CARFOLLOWINGMODELHEADWAYSPEEDTYPE carFollowingModelHeadwaySpeed,
718             final BiFunction<DesiredHeadwayModel, DesiredSpeedModel, T> function) throws XmlParserException
719     {
720         Generator<DesiredHeadwayModel> generatorDesiredHeadwayModel =
721                 parseDesiredHeadwayModel(carFollowingModelHeadwaySpeed.getDESIREDHEADWAYMODEL());
722         Generator<DesiredSpeedModel> generatorDesiredSpeedModel =
723                 parseDesiredSpeedModel(carFollowingModelHeadwaySpeed.getDESIREDSPEEDMODEL());
724         return new CarFollowingModelFactory<T>()
725         {
726             /** {@inheritDoc} */
727             @Override
728             public Parameters getParameters() throws ParameterException
729             {
730                 // in this generic setting, we cannot predefine parameters, they have to be specified in xml
731                 return new ParameterSet();
732             }
733 
734             /** {@inheritDoc} */
735             @Override
736             public T generateCarFollowingModel()
737             {
738                 try
739                 {
740                     return function.apply(generatorDesiredHeadwayModel.draw(), generatorDesiredSpeedModel.draw());
741                 }
742                 catch (ProbabilityException | ParameterException exception)
743                 {
744                     throw new RuntimeException("Exception while drawing desired headway or speed model.");
745                 }
746             }
747         };
748     }
749 
750     /**
751      * Parse desired headway model.
752      * @param desiredHeadwayModel DESIREDHEADWAYMODEL; desired headway model information
753      * @return Generator&lt;DesiredHeadwayModel&gt;; generator for desired headway model
754      * @throws XmlParserException unknown value, missing constructor, etc.
755      */
756     @SuppressWarnings("unchecked")
757     private static Generator<DesiredHeadwayModel> parseDesiredHeadwayModel(final DESIREDHEADWAYMODEL desiredHeadwayModel)
758             throws XmlParserException
759     {
760         if (desiredHeadwayModel.getIDM() != null)
761         {
762             return new Generator<DesiredHeadwayModel>()
763             {
764                 /** {@inheritDoc} */
765                 @Override
766                 public DesiredHeadwayModel draw() throws ProbabilityException, ParameterException
767                 {
768                     return AbstractIDM.HEADWAY;
769                 }
770             };
771         }
772         else if (desiredHeadwayModel.getCLASS() != null)
773         {
774             Constructor<? extends DesiredHeadwayModel> constructor;
775             try
776             {
777                 constructor = (Constructor<? extends DesiredHeadwayModel>) ClassUtil
778                         .resolveConstructor(desiredHeadwayModel.getCLASS(), new Object[0]);
779             }
780             catch (NoSuchMethodException exception)
781             {
782                 throw new XmlParserException(
783                         "Class " + desiredHeadwayModel.getCLASS() + " does not have a valid empty constructor.", exception);
784             }
785             return new Generator<DesiredHeadwayModel>()
786             {
787 
788                 /** {@inheritDoc} */
789                 @Override
790                 public DesiredHeadwayModel draw() throws ProbabilityException, ParameterException
791                 {
792                     try
793                     {
794                         return constructor.newInstance();
795                     }
796                     catch (InstantiationException | IllegalAccessException | IllegalArgumentException
797                             | InvocationTargetException exception)
798                     {
799                         throw new RuntimeException("Exception while instantiating a desired headway model of class "
800                                 + desiredHeadwayModel.getCLASS(), exception);
801                     }
802                 }
803 
804             };
805         }
806         else
807         {
808             throw new XmlParserException("Desired headway model has unsupported value.");
809         }
810     }
811 
812     /**
813      * Parse desired speed model.
814      * @param desiredSpeedModel DESIREDSPEEDMODELTYPE; desired speed model information
815      * @return Generator&lt;DesiredSpeedModel&gt;; generator for desired speed model
816      * @throws XmlParserException unknown value, missing constructor, etc.
817      */
818     @SuppressWarnings("unchecked")
819     private static Generator<DesiredSpeedModel> parseDesiredSpeedModel(final DESIREDSPEEDMODELTYPE desiredSpeedModel)
820             throws XmlParserException
821     {
822         if (desiredSpeedModel.getIDM() != null)
823         {
824             return new Generator<DesiredSpeedModel>()
825             {
826                 /** {@inheritDoc} */
827                 @Override
828                 public DesiredSpeedModel draw() throws ProbabilityException, ParameterException
829                 {
830                     return AbstractIDM.DESIRED_SPEED;
831                 }
832             };
833         }
834         else if (desiredSpeedModel.getSOCIO() != null)
835         {
836             // SOCIO is itself a DESIREDSPEEDMODELTYPE, as it wraps another desired speed model
837             Generator<DesiredSpeedModel> wrappedGenerator = parseDesiredSpeedModel(desiredSpeedModel.getSOCIO());
838             return new Generator<DesiredSpeedModel>()
839             {
840 
841                 /** {@inheritDoc} */
842                 @Override
843                 public DesiredSpeedModel draw() throws ProbabilityException, ParameterException
844                 {
845                     return new SocioDesiredSpeed(wrappedGenerator.draw());
846                 }
847             };
848         }
849         else if (desiredSpeedModel.getCLASS() != null)
850         {
851             Constructor<? extends DesiredSpeedModel> constructor;
852             try
853             {
854                 constructor = (Constructor<? extends DesiredSpeedModel>) ClassUtil
855                         .resolveConstructor(desiredSpeedModel.getCLASS(), new Object[0]);
856             }
857             catch (NoSuchMethodException exception)
858             {
859                 throw new XmlParserException(
860                         "Class " + desiredSpeedModel.getCLASS() + " does not have a valid empty constructor.", exception);
861             }
862             return new Generator<DesiredSpeedModel>()
863             {
864                 /** {@inheritDoc} */
865                 @Override
866                 public DesiredSpeedModel draw() throws ProbabilityException, ParameterException
867                 {
868                     try
869                     {
870                         return constructor.newInstance();
871                     }
872                     catch (InstantiationException | IllegalAccessException | IllegalArgumentException
873                             | InvocationTargetException exception)
874                     {
875                         throw new RuntimeException(
876                                 "Exception while instantiating a desired speed model of class " + desiredSpeedModel.getCLASS(),
877                                 exception);
878                     }
879                 }
880             };
881         }
882         else
883         {
884             throw new XmlParserException("Desired speed model has unsupported value.");
885         }
886     }
887 
888     /**
889      * Parse perception for any tactical planner that has PERCEPTIONTYPE to support perception.
890      * @param perception PERCEPTIONTYPE; perception xml information
891      * @return PerceptionFactory; parsed perception factory
892      * @throws XmlParserException unknown value, missing constructor, etc.
893      */
894     @SuppressWarnings("unchecked")
895     private static PerceptionFactory parsePerception(final PERCEPTIONTYPE perception) throws XmlParserException
896     {
897         // Headway GTU type
898         HeadwayGtuType headwayGtuType;
899         if (perception.getHEADWAYGTUTYPE().getWRAP() != null)
900         {
901             headwayGtuType = HeadwayGtuType.WRAP;
902         }
903         else if (perception.getHEADWAYGTUTYPE().getPERCEIVED() != null)
904         {
905             PERCEIVED perceived = perception.getHEADWAYGTUTYPE().getPERCEIVED();
906             // Estimation
907             Estimation estimation;
908             switch (perceived.getESTIMATION())
909             {
910                 case "NONE":
911                     estimation = Estimation.NONE;
912                     break;
913                 case "UNDERESTIMATION":
914                     estimation = Estimation.UNDERESTIMATION;
915                     break;
916                 case "OVERESTIMATION":
917                     estimation = Estimation.OVERESTIMATION;
918                     break;
919                 default:
920                     throw new XmlParserException("Estimation " + perceived.getESTIMATION() + " is unknown.");
921             }
922             // Anticipation
923             Anticipation anticipation;
924             switch (perceived.getANTICIPATION())
925             {
926                 case "NONE":
927                     anticipation = Anticipation.NONE;
928                     break;
929                 case "CONSTANTSPEED":
930                     anticipation = Anticipation.CONSTANT_SPEED;
931                     break;
932                 case "CONSTANTACCELERATON":
933                     anticipation = Anticipation.CONSTANT_ACCELERATION;
934                     break;
935                 default:
936                     throw new XmlParserException("Anticipation " + perceived.getANTICIPATION() + " is unknown.");
937             }
938             headwayGtuType = new PerceivedHeadwayGtuType(estimation, anticipation);
939         }
940         else
941         {
942             throw new XmlParserException("HeadwayGtuType is unknown.");
943         }
944 
945         // Categories
946         List<Constructor<? extends PerceptionCategory<?, ?>>> categoryConstructorsPerception = new ArrayList<>();
947         List<Constructor<? extends PerceptionCategory<?, ?>>> categoryConstructorsPerceptionHeadway = new ArrayList<>();
948         Class<?>[] perceptionConstructor = new Class[] { LanePerception.class };
949         Class<?>[] perceptionHeadwayConstructor = new Class[] { LanePerception.class, HeadwayGtuType.class };
950         for (CATEGORY category : perception.getCATEGORY())
951         {
952             try
953             {
954                 switch (category.getValue())
955                 {
956                     case "EGO":
957                         categoryConstructorsPerception.add((Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
958                                 .resolveConstructor(DirectEgoPerception.class, perceptionConstructor));
959                         break;
960                     case "BUSSTOP":
961                         categoryConstructorsPerception.add((Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
962                                 .resolveConstructor(DirectBusStopPerception.class, perceptionConstructor));
963                         break;
964                     case "INFRASTRUCTURE":
965                         categoryConstructorsPerception.add((Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
966                                 .resolveConstructor(DirectInfrastructurePerception.class, perceptionConstructor));
967                         break;
968                     case "INTERSECTION":
969                         categoryConstructorsPerceptionHeadway.add((Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
970                                 .resolveConstructor(DirectIntersectionPerception.class, perceptionHeadwayConstructor));
971                         break;
972                     case "NEIGHBORS":
973                         categoryConstructorsPerceptionHeadway.add((Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
974                                 .resolveConstructor(DirectNeighborsPerception.class, perceptionHeadwayConstructor));
975                         break;
976                     case "TRAFFIC":
977                         categoryConstructorsPerception.add((Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
978                                 .resolveConstructor(AnticipationTrafficPerception.class, perceptionConstructor));
979                         break;
980                     case "CLASS":
981                         Constructor<? extends PerceptionCategory<?, ?>> constructor;
982                         try
983                         {
984                             constructor = (Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
985                                     .resolveConstructor(category.getCLASS(), perceptionHeadwayConstructor);
986                             categoryConstructorsPerceptionHeadway.add(constructor);
987                         }
988                         catch (NoSuchMethodException exception)
989                         {
990                             constructor = (Constructor<? extends PerceptionCategory<?, ?>>) ClassUtil
991                                     .resolveConstructor(category.getCLASS(), perceptionConstructor);
992                             categoryConstructorsPerception.add(constructor);
993                         }
994                         catch (NullPointerException exception)
995                         {
996                             throw new XmlParserException(
997                                     "Perception category defined with value CLASS but no class is specified.", exception);
998                         }
999                         break;
1000                     default:
1001                         throw new XmlParserException("Perception category " + category.getValue() + " is unknown.");
1002                 }
1003             }
1004             catch (NoSuchMethodException | ClassCastException exception)
1005             {
1006                 throw new XmlParserException(
1007                         "Unexpected problem while resolving constructor for perception category " + category.getValue());
1008             }
1009         }
1010 
1011         // Mental
1012         Mental mental;
1013         if (perception.getMENTAL().getFULLER() != null)
1014         {
1015             FULLER fuller = perception.getMENTAL().getFULLER();
1016 
1017             // Tasks
1018             Set<Task> tasks = new LinkedHashSet<>();
1019             for (Class<?> taskClass : fuller.getTASK())
1020             {
1021                 try
1022                 {
1023                     tasks.add((Task) ClassUtil.resolveConstructor(taskClass, new Class<?>[0]).newInstance());
1024                 }
1025                 catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
1026                         | NoSuchMethodException exception)
1027                 {
1028                     throw new XmlParserException(
1029                             "Could not instantiate task of class " + taskClass + " through an empty constructor.", exception);
1030                 }
1031             }
1032 
1033             // Behavioural adaptations
1034             Set<BehavioralAdaptation> behavioralAdapatations = new LinkedHashSet<>();
1035             for (BEHAVIORALADAPTATION behavioralAdaptation : fuller.getBEHAVIORALADAPTATION())
1036             {
1037                 switch (behavioralAdaptation.getValue())
1038                 {
1039                     case "SITUATIONALAWARENESS":
1040                         behavioralAdapatations.add(new AdaptationSituationalAwareness());
1041                         break;
1042                     case "HEADWAY":
1043                         behavioralAdapatations.add(new AdaptationHeadway());
1044                         break;
1045                     case "SPEED":
1046                         behavioralAdapatations.add(new AdaptationSpeed());
1047                         break;
1048                     case "CLASS":
1049                         try
1050                         {
1051                             behavioralAdapatations.add((BehavioralAdaptation) ClassUtil
1052                                     .resolveConstructor(behavioralAdaptation.getCLASS(), new Object[0]).newInstance());
1053                         }
1054                         catch (NullPointerException exception)
1055                         {
1056                             throw new XmlParserException(
1057                                     "Behavioral adpatation defined with value CLASS but no class is specified.", exception);
1058                         }
1059                         catch (InstantiationException | IllegalAccessException | IllegalArgumentException
1060                                 | InvocationTargetException | NoSuchMethodException exception)
1061                         {
1062                             throw new XmlParserException("Unable to instantiate class " + behavioralAdaptation.getCLASS()
1063                                     + " through an empty constructor.", exception);
1064                         }
1065                         break;
1066                     default:
1067                         throw new XmlParserException(
1068                                 "Behavioral adapatation " + behavioralAdaptation.getValue() + " is unknown.");
1069                 }
1070             }
1071 
1072             // Task manager
1073             TaskManager taskManager;
1074             if (fuller.getTASKMANAGER() == null)
1075             {
1076                 taskManager = null;
1077             }
1078             else
1079             {
1080                 switch (fuller.getTASKMANAGER())
1081                 {
1082                     case "SUMMATIVE":
1083                         taskManager = new SummativeTaskManager();
1084                         break;
1085                     case "ANTICIPATIONRELIANCE":
1086                         // TODO: support once more consolidated
1087                         throw new XmlParserException("Task manager ANTICIPATIONRELIANCE is not yet supported.");
1088                     default:
1089                         throw new XmlParserException("Task manager " + fuller.getTASKMANAGER() + " is unknown.");
1090                 }
1091             }
1092 
1093             // Fuller
1094             mental = new Fuller(tasks, behavioralAdapatations, taskManager);
1095         }
1096         else
1097         {
1098             mental = null;
1099         }
1100 
1101         // Perception factory
1102         return new PerceptionFactory()
1103         {
1104             /** {@inheritDoc} */
1105             @Override
1106             public Parameters getParameters() throws ParameterException
1107             {
1108                 // in this generic setting, we cannot predefine parameters, they have to be specified in xml
1109                 return new ParameterSet();
1110             }
1111 
1112             /** {@inheritDoc} */
1113             @Override
1114             public LanePerception generatePerception(final LaneBasedGTU gtu)
1115             {
1116                 CategoricalLanePerception lanePerception = new CategoricalLanePerception(gtu, mental);
1117                 try
1118                 {
1119                     for (Constructor<? extends PerceptionCategory<?, ?>> constructor : categoryConstructorsPerception)
1120                     {
1121                         lanePerception.addPerceptionCategory(constructor.newInstance(lanePerception));
1122                     }
1123                     for (Constructor<? extends PerceptionCategory<?, ?>> constructor : categoryConstructorsPerceptionHeadway)
1124                     {
1125                         lanePerception.addPerceptionCategory(constructor.newInstance(lanePerception, headwayGtuType));
1126                     }
1127                 }
1128                 catch (InvocationTargetException | InstantiationException | IllegalAccessException
1129                         | IllegalArgumentException exception)
1130                 {
1131                     throw new RuntimeException("Exception while creating new instance of perception category.", exception);
1132                 }
1133                 return lanePerception;
1134             }
1135         };
1136     }
1137 }