1 package org.opentrafficsim.core.compatibility;
2
3 import java.util.LinkedHashMap;
4 import java.util.Map;
5 import java.util.Objects;
6
7 import org.djutils.exceptions.Throw;
8 import org.opentrafficsim.base.HierarchicalType;
9 import org.opentrafficsim.base.OtsRuntimeException;
10 import org.opentrafficsim.core.gtu.GtuType;
11
12 /**
13 * Compatibility between a GtuType and a certain type of infrastructure. Infrastructure can be any hierarchical structure: a
14 * LinkType, a LaneType, a SensorType, etc.
15 * <p>
16 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
18 * </p>
19 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
20 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
21 * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
22 * @param <T> infrastructure type, e.g. LinkType or LaneType, or water way type
23 */
24 public class GtuCompatibility<T extends HierarchicalType<T, ?> & Compatibility<GtuType, T>> implements Compatibility<GtuType, T>
25 {
26 /** The map of GtuTypes that define on this infrastructure level what GtuTypes are compatible or not. */
27 private final Map<GtuType, Boolean> levelCompatibilityMap = new LinkedHashMap<>();
28
29 /** The cached map of GtuTypes that have been resolved. */
30 private final Map<GtuType, Boolean> cachedCompatibilityMap = new LinkedHashMap<>();
31
32 /** Infrastructure for which this compatibility definition holds, e.g. a LinkType, a LaneType, or a SensorType. */
33 private final T infrastructure;
34
35 /**
36 * Construct a new Compatibility object with empty compatible and forbidden sets.
37 * @param infrastructure I; the infrastructure type, e.g. LinkType, LaneType, SensorType.
38 */
39 public GtuCompatibility(final T infrastructure)
40 {
41 Throw.whenNull(infrastructure, "infrastructure cannot be null");
42 this.infrastructure = infrastructure;
43 }
44
45 /**
46 * Construct a new Compatibility and deep copy the compatible and forbidden sets from an existing Compatibility.
47 * @param original GtuCompatibility<I>; the Compatibility from which the compatible and forbidden sets will be copied
48 */
49 public GtuCompatibility(final GtuCompatibility<T> original)
50 {
51 this.infrastructure = original.infrastructure;
52 this.levelCompatibilityMap.putAll(original.levelCompatibilityMap);
53 }
54
55 /**
56 * Determine if this Compatibility allows or denies a particular GtuType.
57 * @param gtuType GtuType; the GtuType to check
58 * @return boolean; true if the GtuType is compatible; false if the GtuType is not compatible
59 */
60 @Override
61 public boolean isCompatible(final GtuType gtuType)
62 {
63 if (!this.cachedCompatibilityMap.containsKey(gtuType))
64 {
65 boolean foundTrue = false;
66 boolean foundFalse = false;
67 T infra = this.infrastructure;
68 while (infra != null)
69 {
70 GtuType gType = gtuType;
71 while (gType != null)
72 {
73 if (infra.isCompatibleOnInfraLevel(gType) != null)
74 {
75 if (infra.isCompatibleOnInfraLevel(gType))
76 {
77 foundTrue = true;
78 }
79 else
80 {
81 foundFalse = true;
82 }
83 }
84 gType = gType.getParent();
85 }
86 infra = infra.getParent();
87 }
88 if (foundFalse)
89 {
90 this.cachedCompatibilityMap.put(gtuType, false);
91 }
92 else if (foundTrue)
93 {
94 this.cachedCompatibilityMap.put(gtuType, true);
95 }
96 else
97 {
98 this.cachedCompatibilityMap.put(gtuType, false);
99 }
100 }
101 return this.cachedCompatibilityMap.get(gtuType);
102 }
103
104 /** {@inheritDoc} */
105 @Override
106 public Boolean isCompatibleOnInfraLevel(final GtuType gtuType)
107 {
108 return this.levelCompatibilityMap.get(gtuType);
109 }
110
111 /**
112 * Add an compatible GtuType to this GtuCompatibility.
113 * @param gtuType GtuType; the GtuType to add to the compatible set of this Compatibility
114 * @return GTUCompatibility<I>; this GtuCompatibility for method call chaining
115 * @throws NullPointerException when <code>gtuType</code> is null
116 * @throws OtsRuntimeException when changes are made to compatibility after results have been cached
117 */
118 public final GtuCompatibility<T> addCompatibleGtuType(final GtuType gtuType) throws NullPointerException
119 {
120 Throw.whenNull(gtuType, "gtuType may not be null");
121 clearCompatibilityCache();
122 this.levelCompatibilityMap.put(gtuType, true);
123 return this;
124 }
125
126 /**
127 * Add a incompatible GtuType to this GtuCompatibility.
128 * @param gtuType GtuType; the GtuType to add to the incompatible set of this Compatibility
129 * @return GTUCompatibility<I>; this GtuCompatibility for method call chaining
130 * @throws NullPointerException when <code>gtuType</code> is null
131 * @throws OtsRuntimeException when changes are made to compatibility after results have been cached
132 */
133 public final GtuCompatibility<T> addIncompatibleGtuType(final GtuType gtuType) throws NullPointerException
134 {
135 Throw.whenNull(gtuType, "gtuType may not be null");
136 clearCompatibilityCache();
137 this.levelCompatibilityMap.put(gtuType, false);
138 return this;
139 }
140
141 /** {@inheritDoc} */
142 @Override
143 public T getInfrastructure()
144 {
145 return this.infrastructure;
146 }
147
148 /** {@inheritDoc} */
149 @Override
150 public void clearCompatibilityCache()
151 {
152 this.cachedCompatibilityMap.clear();
153 for (T infra: getInfrastructure().getChildren())
154 {
155 infra.clearCompatibilityCache();
156 }
157 }
158
159 /** {@inheritDoc} */
160 @Override
161 public int hashCode()
162 {
163 return Objects.hash(this.infrastructure, this.levelCompatibilityMap);
164 }
165
166 /** {@inheritDoc} */
167 @Override
168 @SuppressWarnings("checkstyle:needbraces")
169 public boolean equals(final Object obj)
170 {
171 if (this == obj)
172 return true;
173 if (obj == null)
174 return false;
175 if (getClass() != obj.getClass())
176 return false;
177 GtuCompatibility<?> other = (GtuCompatibility<?>) obj;
178 return Objects.equals(this.infrastructure, other.infrastructure)
179 && Objects.equals(this.levelCompatibilityMap, other.levelCompatibilityMap);
180 }
181
182 /** {@inheritDoc} */
183 @Override
184 public final String toString()
185 {
186 return "GtuCompatibility [levelCompatibilityMap=" + this.levelCompatibilityMap + "]";
187 }
188
189 }