package com.schneeloch.bostonbusmap_library.parser; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.List; import com.schneeloch.bostonbusmap_library.data.CommuterRailPrediction; import com.schneeloch.bostonbusmap_library.data.RoutePool; import com.schneeloch.bostonbusmap_library.database.Schema; import com.schneeloch.bostonbusmap_library.parser.gson.MbtaRealtimeRoot; import com.schneeloch.bostonbusmap_library.parser.gson.Mode; import com.schneeloch.bostonbusmap_library.parser.gson.Route; import com.schneeloch.bostonbusmap_library.parser.gson.Stop; import com.schneeloch.bostonbusmap_library.parser.gson.Trip; import com.schneeloch.bostonbusmap_library.transit.MbtaRealtimeTransitSource; import com.schneeloch.bostonbusmap_library.data.PredictionStopLocationPair; import com.schneeloch.bostonbusmap_library.data.RouteConfig; import com.schneeloch.bostonbusmap_library.data.StopLocation; import com.schneeloch.bostonbusmap_library.data.TimePrediction; import com.schneeloch.bostonbusmap_library.data.TransitSourceTitles; import com.schneeloch.bostonbusmap_library.util.LogUtil; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.gson.Gson; public class MbtaRealtimePredictionsParser { private final TransitSourceTitles routeKeysToTitles; private final ImmutableSet<String> routeNames; private final RoutePool routePool; public MbtaRealtimePredictionsParser(ImmutableSet<String> routeNames, RoutePool routePool, TransitSourceTitles routeKeysToTitles) { this.routeKeysToTitles = routeKeysToTitles; this.routeNames = routeNames; this.routePool = routePool; } public void runParse(Reader data) throws IOException { BufferedReader bufferedReader = new BufferedReader(data, 2048); MbtaRealtimeRoot root = new Gson().fromJson(bufferedReader, MbtaRealtimeRoot.class); clearPredictions(); for (PredictionStopLocationPair pair : parseTree(root)) { pair.stopLocation.addPrediction(pair.prediction); } } private List<PredictionStopLocationPair> parseTree(MbtaRealtimeRoot root) throws IOException { List<PredictionStopLocationPair> pairs = Lists.newArrayList(); if (root.mode == null) { return pairs; } for (Mode mode : root.mode) { for (Route route : mode.route) { String routeName; if (MbtaRealtimeTransitSource.gtfsNameToRouteName.containsKey(route.route_id)) { routeName = MbtaRealtimeTransitSource.gtfsNameToRouteName.get(route.route_id); } else { LogUtil.i("Route id not found: " + route.route_id); continue; } RouteConfig routeConfig = routePool.get(routeName); for (com.schneeloch.bostonbusmap_library.parser.gson.Direction direction : route.direction) { for (Trip trip : direction.trip) { String vehicleId = null; if (trip.vehicle != null) { if (trip.vehicle.vehicle_label != null) { vehicleId = trip.vehicle.vehicle_label; } else if (trip.vehicle.vehicle_id != null) { vehicleId = trip.vehicle.vehicle_id; } } String directionName = trip.trip_headsign; String tripName = trip.trip_name; for (Stop stop : trip.stop) { String stopId = stop.stop_id; if ("N/A".equals(stopId)) { // not sure whether this value is coming from feed or Gson parser continue; } StopLocation stopLocation = routeConfig.getStop(stopId); if (stopLocation != null) { // TODO: make this more thorough boolean isCommuterRail = MbtaRealtimeTransitSource.routeNameToTransitSource.get(routeName) == Schema.Routes.SourceId.CommuterRail; String routeTitle = this.routeKeysToTitles.getTitle(routeName); if (stop.sch_arr_dt != null && isCommuterRail) { // Show scheduled time and delay long scheduledArrivalMillis = Long.parseLong(stop.sch_arr_dt) * 1000; long arrivalTimeMillis; if (stop.pre_dt != null) { long arrivalTimeSeconds = Long.parseLong(stop.pre_dt); arrivalTimeMillis = arrivalTimeSeconds * 1000; } else { arrivalTimeMillis = scheduledArrivalMillis; } int lateness = (int)(arrivalTimeMillis - scheduledArrivalMillis) / 1000; if (lateness < 0) { lateness = 0; } TimePrediction prediction = new CommuterRailPrediction(scheduledArrivalMillis, tripName, directionName, routeName, routeTitle, false, lateness > 5*60, lateness, "", stopId, CommuterRailPrediction.Flag.Arr ); PredictionStopLocationPair pair = new PredictionStopLocationPair(prediction, stopLocation); pairs.add(pair); } else if (stop.pre_dt != null) { long arrivalTimeSeconds = Long.parseLong(stop.pre_dt); long arrivalTimeMillis = arrivalTimeSeconds * 1000; TimePrediction prediction; if (isCommuterRail) { prediction = new CommuterRailPrediction(arrivalTimeMillis, tripName, directionName, routeName, routeTitle, false, false, 0, "", stopId, CommuterRailPrediction.Flag.Arr ); } else { prediction = new TimePrediction(arrivalTimeMillis, vehicleId, directionName, routeName, routeTitle, false, false, 0, null, stopId); } PredictionStopLocationPair pair = new PredictionStopLocationPair(prediction, stopLocation); pairs.add(pair); } } else { LogUtil.i("Unable to find stop " + stopId + " in database"); } } } } } } return pairs; } private void clearPredictions() throws IOException { for (String route : routeNames) { RouteConfig routeConfig = routePool.get(route); if (routeConfig != null) { for (StopLocation stopLocation : routeConfig.getStops()) { stopLocation.clearPredictions(routeConfig); } } } } }