1 package org.opentrafficsim.core.compatibility;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 import org.opentrafficsim.base.HierarchicalType;
7 import org.opentrafficsim.core.gtu.GTUDirectionality;
8 import org.opentrafficsim.core.gtu.GTUException;
9 import org.opentrafficsim.core.gtu.GTUType;
10 import org.opentrafficsim.core.logger.SimLogger;
11 import org.opentrafficsim.core.network.LongitudinalDirectionality;
12
13 import nl.tudelft.simulation.language.Throw;
14
15 /**
16 * Directional GTUType dependent compatibility.
17 * <p>
18 * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
19 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
20 * <p>
21 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Aug 25, 2017 <br>
22 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
23 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
24 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
25 * @param <I> infrastructure type, e.g. LinkType or LaneType, or water way type
26 */
27 public class GTUCompatibility<I extends HierarchicalType<I> & Compatibility<GTUType, I>> implements Compatibility<GTUType, I>
28 {
29 /** The map of GTUTypes to permitted directions of movement. */
30 private final Map<GTUType, LongitudinalDirectionality> allowanceMap = new HashMap<>();
31
32 /** Infrastructure type, e.g. LinkType, LaneType, SensorType. */
33 private final I infrastructure;
34
35 /**
36 * Construct a new Compatibility object with empty allowed and forbidden sets for both directions.
37 * @param infrastructure I; the infrastructure type, e.g. LinkType, LaneType, SensorType.
38 */
39 public GTUCompatibility(final I infrastructure)
40 {
41 this.infrastructure = infrastructure;
42 }
43
44 /**
45 * Construct a new Compatibility and deep copy the allowed and forbidden sets for both directions from an existing
46 * Compatibility.
47 * @param original Compatibility; the Compatibility from which the allowed and forbidden sets for both directions will be
48 * copied
49 */
50 public GTUCompatibility(final GTUCompatibility<I> original)
51 {
52 this.infrastructure = original.infrastructure;
53 this.allowanceMap.putAll(original.allowanceMap);
54 }
55
56 /**
57 * Determine if this Compatibility allows or denies a particular GTUType.
58 * @param gtuType GTUType; the GTUType to check
59 * @param directionality GTUDirectionality; the GTUDirectionality in which the GTUType wants to move
60 * @return Boolean; true if the GTUType is compatible; false if the GTUType is not compatible; null if this Compatibility
61 * cannot decide (the Compatibility of a super type should then determine whether the GTUType is compatible)
62 */
63 @Override
64 public final Boolean isCompatible(final GTUType gtuType, final GTUDirectionality directionality)
65 {
66 LongitudinalDirectionality allowedDirections = this.allowanceMap.get(gtuType);
67 if (null == allowedDirections)
68 {
69 return null;
70 }
71 switch (allowedDirections)
72 {
73 case DIR_BOTH:
74 return true;
75 case DIR_MINUS:
76 return GTUDirectionality.DIR_MINUS == directionality;
77 case DIR_NONE:
78 return false;
79 case DIR_PLUS:
80 return GTUDirectionality.DIR_PLUS == directionality;
81 default:
82 SimLogger.always().warn("Unknown type in isCompatible - Cannot happen");
83 return null;
84 }
85 }
86
87 /**
88 * Add a GTUType to this GTUCompatibility.
89 * @param gtuType GTUType; the GTUType to add to the allowed set of this Compatibility
90 * @param directionality LongitudinalDirectionality; directionality for which the GTUType must be added
91 * @return GTYUCompatibility<I>; this GTUCompatibility for method call chaining
92 * @throws NullPointerException when <code>directionality</code> is null
93 */
94 public final GTUCompatibility<I> addAllowedGTUType(final GTUType gtuType, final LongitudinalDirectionality directionality)
95 throws NullPointerException
96 {
97 Throw.whenNull(directionality, "Directionality for GTUType %s may not be null", gtuType);
98 this.allowanceMap.put(gtuType, directionality);
99 return this;
100 }
101
102 /**
103 * Remove a GTUType from the allowed set of this Compatibility. This method cannot fail; no warning is issued when the
104 * GTUType is not currently in the allowed set.
105 * @param gtuType GTUType; the GTUType to remove from the allowed set
106 * @param directionality LongitudinalDirectionality; the longitudinal directionality for which the GTUType must be removed
107 * @return GTYUCompatibility<I>; this GTUCompatibility for method call chaining
108 */
109 public final GTUCompatibility<I> removeAllowedGTUType(final GTUType gtuType,
110 final LongitudinalDirectionality directionality)
111 {
112 this.allowanceMap.remove(gtuType);
113 return this;
114 }
115
116 /** {@inheritDoc} */
117 @Override
118 public final String toString()
119 {
120 return "GTUCompatibility [allowanceMap=" + this.allowanceMap + "]";
121 }
122
123 /** {@inheritDoc} */
124 @Override
125 public final int hashCode()
126 {
127 final int prime = 31;
128 int result = 1;
129 result = prime * result + ((this.allowanceMap == null) ? 0 : this.allowanceMap.hashCode());
130 return result;
131 }
132
133 /** {@inheritDoc} */
134 @Override
135 public final boolean equals(final Object obj)
136 {
137 if (this == obj)
138 {
139 return true;
140 }
141 if (obj == null)
142 {
143 return false;
144 }
145 if (getClass() != obj.getClass())
146 {
147 return false;
148 }
149 GTUCompatibility<?> other = (GTUCompatibility<?>) obj;
150 if (this.allowanceMap == null)
151 {
152 if (other.allowanceMap != null)
153 {
154 return false;
155 }
156 }
157 else if (!this.allowanceMap.equals(other.allowanceMap))
158 {
159 return false;
160 }
161 return true;
162 }
163
164 /**
165 * Check if a GTUCompatibility does not allow things that the GTUCompatibility of a parent object disallows, e.g. a
166 * permitted driving direction on a Lane should not be forbidden on the Link that the Lane is part of.
167 * @param parentCompatibility GTUCompatibility<?>; the GTUCompatibility of the parent object
168 * @param tryParentsOfGTUType whether to try parent GTU types
169 * @throws GTUException if a conflict is found
170 */
171 public final void isCompatibleWith(final Compatibility<GTUType, ?> parentCompatibility, final boolean tryParentsOfGTUType)
172 throws GTUException
173 {
174 for (GTUType gtuType : this.allowanceMap.keySet())
175 {
176 LongitudinalDirectionality ourLD = this.allowanceMap.get(gtuType);
177 LongitudinalDirectionality parentLD = parentCompatibility.getDirectionality(gtuType, true);
178 if (!parentLD.contains(ourLD))
179 {
180 throw new GTUException(String.format("GTUType %s has LongitudinalDirectionality %s on child, but %s on parent",
181 ourLD, parentLD));
182 }
183 }
184 // TODO cleverly check only those in the parent(s) that do not conflict with ours.
185 }
186
187 /** {@inheritDoc} */
188 @Override
189 public final LongitudinalDirectionality getDirectionality(final GTUType gtuType, final boolean tryParentsOfGTUType)
190 {
191 for (GTUType testGTUType = gtuType; null != testGTUType; testGTUType = testGTUType.getParent())
192 {
193 LongitudinalDirectionality result = this.allowanceMap.get(testGTUType);
194 if (null != result)
195 {
196 return result;
197 }
198 if (null != this.infrastructure && null != this.infrastructure.getParent())
199 {
200 result = this.infrastructure.getParent().getDirectionality(testGTUType, false);
201 if (null != result)
202 {
203 return result;
204 }
205 }
206 if (!tryParentsOfGTUType)
207 {
208 break;
209 }
210 }
211 return tryParentsOfGTUType ? LongitudinalDirectionality.DIR_NONE : null;
212 }
213 }