View Javadoc
1   package org.opentrafficsim.core.compatibility;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   import org.djutils.exceptions.Throw;
7   import org.opentrafficsim.base.HierarchicalType;
8   import org.opentrafficsim.core.gtu.GTUDirectionality;
9   import org.opentrafficsim.core.gtu.GTUException;
10  import org.opentrafficsim.core.gtu.GTUType;
11  import org.opentrafficsim.core.network.LongitudinalDirectionality;
12  
13  import nl.tudelft.simulation.dsol.logger.SimLogger;
14  
15  /**
16   * Directional GTUType dependent compatibility.
17   * <p>
18   * Copyright (c) 2013-2019 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 GTUCompatibility&lt;I&gt;; the Compatibility from which the allowed and forbidden sets for both
48       *            directions will be 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&lt;I&gt;; 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&lt;I&gt;; 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 Compatibility&lt;GTUType, ?&gt;; the GTUCompatibility of the parent object
168      * @param tryParentsOfGTUType boolean; 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 }