/* 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 (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.alertpatch;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.google.common.collect.Multimap;
import org.onebusaway.gtfs.model.Agency;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.gtfs.model.Route;
import org.onebusaway.gtfs.model.Stop;
import org.onebusaway.gtfs.model.Trip;
import org.opentripplanner.api.adapters.AgencyAndIdAdapter;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.edgetype.PreAlightEdge;
import org.opentripplanner.routing.edgetype.PreBoardEdge;
import org.opentripplanner.routing.edgetype.TripPattern;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.vertextype.TransitStop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This adds a note to all boardings of a given route or stop (optionally, in a given direction)
*
* @author novalis
*
*/
@XmlRootElement(name = "AlertPatch")
public class AlertPatch implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(AlertPatch.class);
private static final long serialVersionUID = 20140319L;
private String id;
private Alert alert;
private List<TimePeriod> timePeriods = new ArrayList<TimePeriod>();
private String agency;
private AgencyAndId route;
private AgencyAndId trip;
private AgencyAndId stop;
/**
* The headsign of the alert
*/
private String direction;
/**
* The id of the feed this patch is intended for.
*/
private String feedId;
/**
* Direction id of the GTFS trips this alert concerns, set to -1 if no direction.
*/
private int directionId = -1;
@XmlElement
public Alert getAlert() {
return alert;
}
public boolean displayDuring(State state) {
for (TimePeriod timePeriod : timePeriods) {
if (state.getTimeSeconds() >= timePeriod.startTime) {
if (state.getStartTimeSeconds() < timePeriod.endTime) {
return true;
}
}
}
return false;
}
@XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void apply(Graph graph) {
Agency agency = null;
if (feedId != null) {
Map<String, Agency> agencies = graph.index.agenciesForFeedId.get(feedId);
agency = this.agency != null ? agencies.get(this.agency) : null;
}
Route route = this.route != null ? graph.index.routeForId.get(this.route) : null;
Stop stop = this.stop != null ? graph.index.stopForId.get(this.stop) : null;
Trip trip = this.trip != null ? graph.index.tripForId.get(this.trip) : null;
if (route != null || trip != null || agency != null) {
Collection<TripPattern> tripPatterns = null;
if (trip != null) {
tripPatterns = new LinkedList<>();
TripPattern tripPattern = graph.index.patternForTrip.get(trip);
if (tripPattern != null) {
tripPatterns.add(tripPattern);
}
} else if (route != null) {
tripPatterns = graph.index.patternsForRoute.get(route);
} else {
// Find patterns for the feed.
tripPatterns = graph.index.patternsForFeedId.get(feedId);
}
if (tripPatterns != null) {
for (TripPattern tripPattern : tripPatterns) {
if (direction != null && !direction.equals(tripPattern.getDirection())) {
continue;
}
if (directionId != -1 && directionId == tripPattern.directionId) {
continue;
}
for (int i = 0; i < tripPattern.stopPattern.stops.length; i++) {
if (stop == null || stop.equals(tripPattern.stopPattern.stops[i])) {
graph.addAlertPatch(tripPattern.boardEdges[i], this);
graph.addAlertPatch(tripPattern.alightEdges[i], this);
}
}
}
}
} else if (stop != null) {
TransitStop transitStop = graph.index.stopVertexForStop.get(stop);
for (Edge edge : transitStop.getOutgoing()) {
if (edge instanceof PreBoardEdge) {
graph.addAlertPatch(edge, this);
break;
}
}
for (Edge edge : transitStop.getIncoming()) {
if (edge instanceof PreAlightEdge) {
graph.addAlertPatch(edge, this);
break;
}
}
}
}
public void remove(Graph graph) {
Agency agency = null;
if (feedId != null) {
Map<String, Agency> agencies = graph.index.agenciesForFeedId.get(feedId);
agency = this.agency != null ? agencies.get(this.agency) : null;
}
Route route = this.route != null ? graph.index.routeForId.get(this.route) : null;
Stop stop = this.stop != null ? graph.index.stopForId.get(this.stop) : null;
Trip trip = this.trip != null ? graph.index.tripForId.get(this.trip) : null;
if (route != null || trip != null || agency != null) {
Collection<TripPattern> tripPatterns = null;
if(trip != null) {
tripPatterns = new LinkedList<TripPattern>();
TripPattern tripPattern = graph.index.patternForTrip.get(trip);
if(tripPattern != null) {
tripPatterns.add(tripPattern);
}
} else if (route != null) {
tripPatterns = graph.index.patternsForRoute.get(route);
} else {
// Find patterns for the feed.
tripPatterns = graph.index.patternsForFeedId.get(feedId);
}
if (tripPatterns != null) {
for (TripPattern tripPattern : tripPatterns) {
if (direction != null && !direction.equals(tripPattern.getDirection())) {
continue;
}
if (directionId != -1 && directionId != tripPattern.directionId) {
continue;
}
for (int i = 0; i < tripPattern.stopPattern.stops.length; i++) {
if (stop == null || stop.equals(tripPattern.stopPattern.stops[i])) {
graph.removeAlertPatch(tripPattern.boardEdges[i], this);
graph.removeAlertPatch(tripPattern.alightEdges[i], this);
}
}
}
}
} else if (stop != null) {
TransitStop transitStop = graph.index.stopVertexForStop.get(stop);
for (Edge edge : transitStop.getOutgoing()) {
if (edge instanceof PreBoardEdge) {
graph.removeAlertPatch(edge, this);
break;
}
}
for (Edge edge : transitStop.getIncoming()) {
if (edge instanceof PreAlightEdge) {
graph.removeAlertPatch(edge, this);
break;
}
}
}
}
public void setAlert(Alert alert) {
this.alert = alert;
}
private void writeObject(ObjectOutputStream os) throws IOException {
if (timePeriods instanceof ArrayList<?>) {
((ArrayList<TimePeriod>) timePeriods).trimToSize();
}
os.defaultWriteObject();
}
public void setTimePeriods(List<TimePeriod> periods) {
timePeriods = periods;
}
public String getAgency() {
return agency;
}
@XmlJavaTypeAdapter(AgencyAndIdAdapter.class)
public AgencyAndId getRoute() {
return route;
}
@XmlJavaTypeAdapter(AgencyAndIdAdapter.class)
public AgencyAndId getTrip() {
return trip;
}
@XmlJavaTypeAdapter(AgencyAndIdAdapter.class)
public AgencyAndId getStop() {
return stop;
}
public void setAgencyId(String agency) {
this.agency = agency;
}
public void setRoute(AgencyAndId route) {
this.route = route;
}
public void setTrip(AgencyAndId trip) {
this.trip = trip;
}
public void setDirection(String direction) {
if (direction != null && direction.equals("")) {
direction = null;
}
this.direction = direction;
}
public void setDirectionId(int direction) {
this.directionId = direction;
}
@XmlElement
public String getDirection() {
return direction;
}
@XmlElement
public int getDirectionId() {
return directionId;
}
public void setStop(AgencyAndId stop) {
this.stop = stop;
}
public String getFeedId() {
return feedId;
}
public void setFeedId(String feedId) {
this.feedId = feedId;
}
public boolean hasTrip() {
return trip != null;
}
public boolean equals(Object o) {
if (!(o instanceof AlertPatch)) {
return false;
}
AlertPatch other = (AlertPatch) o;
if (direction == null) {
if (other.direction != null) {
return false;
}
} else {
if (!direction.equals(other.direction)) {
return false;
}
}
if (directionId != other.directionId) {
return false;
}
if (agency == null) {
if (other.agency != null) {
return false;
}
} else {
if (!agency.equals(other.agency)) {
return false;
}
}
if (trip == null) {
if (other.trip != null) {
return false;
}
} else {
if (!trip.equals(other.trip)) {
return false;
}
}
if (stop == null) {
if (other.stop != null) {
return false;
}
} else {
if (!stop.equals(other.stop)) {
return false;
}
}
if (route == null) {
if (other.route != null) {
return false;
}
} else {
if (!route.equals(other.route)) {
return false;
}
}
if (alert == null) {
if (other.alert != null) {
return false;
}
} else {
if (!alert.equals(other.alert)) {
return false;
}
}
if (id == null) {
if (other.id != null) {
return false;
}
} else {
if (!id.equals(other.id)) {
return false;
}
}
if (timePeriods == null) {
if (other.timePeriods != null) {
return false;
}
} else {
if (!timePeriods.equals(other.timePeriods)) {
return false;
}
}
if (feedId == null) {
if (other.feedId != null) {
return false;
}
} else {
if (!feedId.equals(other.feedId)) {
return false;
}
}
return true;
}
public int hashCode() {
return ((direction == null ? 0 : direction.hashCode()) +
directionId +
(agency == null ? 0 : agency.hashCode()) +
(trip == null ? 0 : trip.hashCode()) +
(stop == null ? 0 : stop.hashCode()) +
(route == null ? 0 : route.hashCode()) +
(alert == null ? 0 : alert.hashCode()) +
(feedId == null ? 0 : feedId.hashCode()));
}
}