/* 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.updater;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.opentripplanner.routing.patch.Alert;
import org.opentripplanner.routing.patch.AlertPatch;
import org.opentripplanner.routing.patch.TimePeriod;
import org.opentripplanner.routing.patch.TranslatedString;
import org.opentripplanner.routing.services.PatchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.transit.realtime.GtfsRealtime;
import com.google.transit.realtime.GtfsRealtime.EntitySelector;
import com.google.transit.realtime.GtfsRealtime.FeedEntity;
import com.google.transit.realtime.GtfsRealtime.FeedMessage;
import com.google.transit.realtime.GtfsRealtime.TimeRange;
import com.google.transit.realtime.GtfsRealtime.TranslatedString.Translation;
/**
* This presently only includes GTFS-Realtime Service Alert feeds;
* we hope to eventually include Trip Updates as well.
* @author novalis
*
*/
public class UpdateHandler {
private static final Logger log = LoggerFactory.getLogger(UpdateHandler.class);
private String defaultAgencyId;
private Set<String> patchIds = new HashSet<String>();
private PatchService patchService;
/** How long before the posted start of an event it should be displayed to users */
private long earlyStart;
public UpdateHandler() {
}
public void update(FeedMessage message) {
patchService.expire(patchIds);
patchIds.clear();
for (FeedEntity entity : message.getEntityList()) {
if (!entity.hasAlert()) {
continue;
}
GtfsRealtime.Alert alert = entity.getAlert();
String id = entity.getId();
handleAlert(id, alert);
}
}
private void handleAlert(String id, GtfsRealtime.Alert alert) {
Alert alertText = new Alert();
alertText.alertDescriptionText = deBuffer(alert.getDescriptionText());
alertText.alertHeaderText = deBuffer(alert.getHeaderText());
alertText.alertUrl = deBuffer(alert.getUrl());
ArrayList<TimePeriod> periods = new ArrayList<TimePeriod>();
ArrayList<TimePeriod> displayPeriods = new ArrayList<TimePeriod>();
long bestStartTime = Long.MAX_VALUE;
for (TimeRange activePeriod : alert.getActivePeriodList()) {
final long start = activePeriod.hasStart() ? activePeriod.getStart() - earlyStart : 0;
final long realStart = activePeriod.hasStart() ? activePeriod.getStart() : 0;
if (realStart > 0 && realStart < bestStartTime) {
bestStartTime = realStart;
}
final long end = activePeriod.hasEnd() ? activePeriod.getEnd() : Long.MAX_VALUE;
periods.add(new TimePeriod(realStart, end));
if(earlyStart > 0 && start != realStart)
displayPeriods.add(new TimePeriod(start, realStart));
}
if (bestStartTime != Long.MAX_VALUE) {
alertText.effectiveStartDate = new Date(bestStartTime * 1000);
}
for (EntitySelector informed : alert.getInformedEntityList()) {
String patchId = createId(id, informed);
String routeId = null;
if (informed.hasRouteId()) {
routeId = informed.getRouteId();
}
// TODO: The other elements of a TripDescriptor are ignored...
String tripId = null;
if (informed.hasTrip() && informed.getTrip().hasTripId()) {
tripId = informed.getTrip().getTripId();
}
String stopId = null;
if (informed.hasStopId()) {
stopId = informed.getStopId();
}
String agencyId = informed.getAgencyId();
if (informed.hasAgencyId()) {
agencyId = informed.getAgencyId().intern();
} else {
agencyId = defaultAgencyId;
}
if (agencyId == null) {
log.error("Empty agency id (and no default set) in feed; other ids are route "
+ routeId + " and stop " + stopId);
continue;
}
AlertPatch patch = new AlertPatch();
if (routeId != null) {
patch.setRoute(new AgencyAndId(agencyId, routeId));
}
if (tripId != null) {
patch.setTrip(new AgencyAndId(agencyId, tripId));
}
if (stopId != null) {
patch.setStop(new AgencyAndId(agencyId, stopId));
}
if(agencyId != null && routeId == null && tripId == null && stopId == null) {
patch.setAgencyId(agencyId);
}
patch.setCancelled(alert.getEffect() == GtfsRealtime.Alert.Effect.NO_SERVICE);
patch.setTimePeriods(periods);
patch.setDisplayTimePeriods(displayPeriods);
patch.setAlert(alertText);
patch.setId(patchId);
patchIds.add(patchId);
patchService.apply(patch);
}
}
private String createId(String id, EntitySelector informed) {
return id + " "
+ (informed.hasAgencyId () ? informed.getAgencyId () : " null ") + " "
+ (informed.hasRouteId () ? informed.getRouteId () : " null ") + " "
+ (informed.hasRouteType () ? informed.getRouteType () : " null ") + " "
+ (informed.hasStopId () ? informed.getStopId () : " null ") + " "
+ (informed.hasTrip() ? informed.getTrip().getTripId() : " null ");
}
/**
* convert a protobuf TranslatedString to a OTP TranslatedString
*
* @return
*/
private TranslatedString deBuffer(GtfsRealtime.TranslatedString buffered) {
TranslatedString result = new TranslatedString();
for (Translation translation : buffered.getTranslationList()) {
String language = translation.getLanguage();
String string = translation.getText();
result.addTranslation(language, string);
}
return result;
}
public void setDefaultAgencyId(String defaultAgencyId) {
if(defaultAgencyId != null)
this.defaultAgencyId = defaultAgencyId.intern();
}
public void setPatchService(PatchService patchService) {
this.patchService = patchService;
}
public long getEarlyStart() {
return earlyStart;
}
public void setEarlyStart(long earlyStart) {
this.earlyStart = earlyStart;
}
}