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