/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (props, at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package org.opentripplanner.routing.services.notes;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.opentripplanner.common.model.T2;
import org.opentripplanner.routing.alertpatch.Alert;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.edgetype.PartialStreetEdge;
import org.opentripplanner.routing.graph.Edge;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
/**
* A notes source of static notes, usually created at graph building stage and not modified
* thereafter.
*
* @author laurent
*/
public class StaticStreetNotesSource implements StreetNotesSource, Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(StaticStreetNotesSource.class);
/**
* Notes for street edges. No need to synchronize access to the map as they will not be
* concurrent write access (no notes for temporary edges, we use notes from parent).
*/
private final SetMultimap<Edge, MatcherAndAlert> notesForEdge = HashMultimap
.<Edge, MatcherAndAlert> create();
/**
* Set of unique matchers, kept during building phase, used for interning (lots of note/matchers
* are identical).
*/
private transient Map<T2<NoteMatcher, Alert>, MatcherAndAlert> uniqueMatchers = new HashMap<>();
StaticStreetNotesSource() {
}
void addNote(Edge edge, Alert note, NoteMatcher matcher) {
if (LOG.isDebugEnabled())
LOG.debug("Adding note {} to {} with matcher {}", note, edge, matcher);
notesForEdge.put(edge, buildMatcherAndAlert(matcher, note));
}
/**
* Return the set of notes applicable for this state / backedge pair.
* @return The set of notes or null if empty.
*/
@Override
public Set<MatcherAndAlert> getNotes(Edge edge) {
/* If the edge is temporary, we look for notes in it's parent edge. */
if (edge instanceof PartialStreetEdge) {
edge = ((PartialStreetEdge) edge).getParentEdge();
}
Set<MatcherAndAlert> maas = notesForEdge.get(edge);
if (maas == null || maas.isEmpty()) {
return null;
}
return maas;
}
/**
* Remove all notes attached to this edge. NOTE: this should only be called within a graph
* building context (or unit testing).
*
* @param edge
*/
void removeNotes(Edge edge) {
if (LOG.isDebugEnabled())
LOG.debug("Removing notes for edge: {}", edge);
notesForEdge.removeAll(edge);
}
/**
* Create a MatcherAndAlert, interning it if the note and matcher pair is already created. Note:
* we use the default Object.equals() for matchers, as they are mostly already singleton
* instances.
*
* @param noteMatcher
* @param note
* @return
*/
private MatcherAndAlert buildMatcherAndAlert(NoteMatcher noteMatcher, Alert note) {
T2<NoteMatcher, Alert> key = new T2<>(noteMatcher, note);
MatcherAndAlert interned = uniqueMatchers.get(key);
if (interned != null) {
return interned;
}
MatcherAndAlert ret = new MatcherAndAlert(noteMatcher, note);
uniqueMatchers.put(key, ret);
return ret;
}
}