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