MATSIM
DisallowedNextLinks.java
Go to the documentation of this file.
1 package org.matsim.core.network.turnRestrictions;
2 
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.HashMap;
7 import java.util.HashSet;
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.stream.Collectors;
14 
15 import javax.annotation.Nullable;
16 
17 import org.apache.logging.log4j.LogManager;
18 import org.apache.logging.log4j.Logger;
19 import org.matsim.api.core.v01.Id;
21 
22 import com.google.common.collect.ImmutableList;
23 
29 public class DisallowedNextLinks {
30 
31  private static final Logger LOG = LogManager.getLogger(DisallowedNextLinks.class);
32 
33  private static boolean warnedAboutNotConsideredInRouting = false; // ! remove, if routing considers this
34 
35  // Actually, it could be Map<String, Set<List<Id<Link>>>>, as the order of the
36  // next links sequences does not matter. However, we choose to store them in a
37  // list in favor of a smaller memory footprint.
38  private final Map<String, List<List<Id<Link>>>> linkIdSequencesMap = new HashMap<>();
39 
45  public static class Builder {
47 
48  public Builder withDisallowedLinkSequence(String mode, List<Id<Link>> linkSequence) {
49  instance.addDisallowedLinkSequence(mode, linkSequence);
50  return this;
51  }
52 
60  return instance;
61  }
62  }
63 
64  public DisallowedNextLinks() { // ! remove constructor, if routing considers this
65  if (!warnedAboutNotConsideredInRouting) {
66  warnedAboutNotConsideredInRouting = true;
67  LOG.warn("Considering DisallowedNextLinks in routing is only implemented by SpeedyDijkstra and SpeedyALT!");
68  }
69  }
70 
79  public boolean addDisallowedLinkSequence(String mode, List<Id<Link>> linkSequence) {
80  List<List<Id<Link>>> linkSequences = this.linkIdSequencesMap.computeIfAbsent(mode, m -> new ArrayList<>());
81 
82  // prevent adding empty/duplicate link id sequences, or duplicate link ids
83  if (linkSequence.isEmpty() || new HashSet<>(linkSequence).size() != linkSequence.size()
84  || linkSequences.contains(linkSequence)) {
85  return false;
86  }
87 
88  if (!linkSequences.add(ImmutableList.copyOf(linkSequence))) {
89  return false;
90  }
91 
92  // Semantically, the order does not matter. But despite using a list, we want
93  // DisallowedNextLinks objects to have a working equal method. To ensure, that
94  // DisallowedNextLinks are equal, even if links are added in a different order,
95  // we sort the internal list.
96  Collections.sort(linkSequences, (l, r) -> l.toString().compareTo(r.toString()));
97  return true;
98  }
99 
100  public List<List<Id<Link>>> getDisallowedLinkSequences(String mode) {
101  List<List<Id<Link>>> sequences = this.linkIdSequencesMap.get(mode);
102  return sequences != null ? Collections.unmodifiableList(sequences) : Collections.emptyList();
103  }
104 
108  public Collection<List<Id<Link>>> getMergedDisallowedLinkSequences() {
109  // Implementation note: we do not want duplicates, so the logical choice is a set,
110  // as the argument for memory footprint does not hold here (short lived object).
111  // This makes the return types inconsistent, but one could argue that all methods should
112  // return Collections rather than Lists (ordering is not meaningful)
113  // We use a LinkedHashSet to preserve iteration order, to be consistent with other methods in this class
114  // td 04.24
115  final Set<List<Id<Link>>> sequences = new LinkedHashSet<>();
116 
117  for (List<List<Id<Link>>> sequencesForMode : linkIdSequencesMap.values()) {
118  sequences.addAll(sequencesForMode);
119  }
120 
121  return Collections.unmodifiableSet(sequences);
122  }
123 
124  @Nullable
125  public List<List<Id<Link>>> removeDisallowedLinkSequences(String mode) {
126  return this.linkIdSequencesMap.remove(mode);
127  }
128 
129  public Map<String, List<List<Id<Link>>>> getAsMap() {
130  return this.linkIdSequencesMap.entrySet().stream()
131  .collect(Collectors.toMap(Entry::getKey, e -> Collections.unmodifiableList(e.getValue())));
132  }
133 
134  public DisallowedNextLinks copyOnlyModes(final Collection<String> modes) {
135  final DisallowedNextLinks newInstance = new DisallowedNextLinks();
136 
137  for (String mode : modes) {
138  for (List<Id<Link>> sequence : getDisallowedLinkSequences(mode)) {
139  newInstance.addDisallowedLinkSequence(mode, sequence);
140  }
141  }
142 
143  return newInstance;
144  }
145 
146  public void clear() {
147  this.linkIdSequencesMap.clear();
148  }
149 
150  public boolean isEmpty() {
151  return this.linkIdSequencesMap.isEmpty();
152  }
153 
154  @Override
155  public boolean equals(Object obj) {
156  if (obj instanceof DisallowedNextLinks dnl
157  // both linkIdSequences have same modes
158  && this.linkIdSequencesMap.keySet().equals(dnl.linkIdSequencesMap.keySet())) {
159  for (Entry<String, List<List<Id<Link>>>> entry : this.linkIdSequencesMap.entrySet()) {
160  String mode = entry.getKey();
161  List<List<Id<Link>>> linkSequences = entry.getValue();
162  List<List<Id<Link>>> otherLinkSequences = dnl.linkIdSequencesMap.get(mode);
163  // because we store next link sequences in a list, even though their order has
164  // no meaning, we need to ignore the order when comparing objects.
165  if (linkSequences.size() != otherLinkSequences.size()
166  || !linkSequences.containsAll(otherLinkSequences)
167  || !otherLinkSequences.containsAll(linkSequences)) {
168  return false;
169  }
170  }
171  return true;
172  }
173  return false;
174  }
175 
176  @Override
177  public int hashCode() {
178  return this.linkIdSequencesMap.hashCode();
179  }
180 
181  @Override
182  public String toString() {
183  return "DisallowedNextLinks [linkIdSequencesMap=" + linkIdSequencesMap + "]";
184  }
185 
186 }