package org.opentripplanner.updater.bike_rental; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.opentripplanner.routing.bike_rental.BikeRentalStation; import org.opentripplanner.util.HttpUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.map.ObjectMapper; /** * Fetch Bike Rental JSON feeds and pass each record on to the specific rental subclass * * @see BikeRentalDataSource */ public abstract class GenericJSONBikeRentalDataSource implements BikeRentalDataSource { private static final Logger log = LoggerFactory.getLogger(GenericJSONBikeRentalDataSource.class); private String url; private String jsonParsePath; ArrayList<BikeRentalStation> stations = new ArrayList<BikeRentalStation>(); /** * Construct superclass * * @param JSON path to get from enclosing elements to nested rental list. * Separate path levels with '/' For example "d/list" * */ public GenericJSONBikeRentalDataSource(String jsonPath) { jsonParsePath = jsonPath; } /** * Construct superclass where rental list is on the top level of JSON code * */ public GenericJSONBikeRentalDataSource() { jsonParsePath = ""; } @Override public boolean update() { try { InputStream data = HttpUtils.getData(url); if (data == null) { log.warn("Failed to get data from url " + url); return false; } parseJSON(data); data.close(); } catch (IllegalArgumentException e) { log.warn("Error parsing bike rental feed from " + url, e); return false; } catch (JsonProcessingException e) { log.warn("Error parsing bike rental feed from " + url + "(bad JSON of some sort)", e); return false; } catch (IOException e) { log.warn("Error reading bike rental feed from " + url, e); return false; } return true; } private void parseJSON(InputStream dataStream) throws JsonProcessingException, IllegalArgumentException, IOException { ArrayList<BikeRentalStation> out = new ArrayList<BikeRentalStation>(); String rentalString = convertStreamToString(dataStream); ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(rentalString); if (!jsonParsePath.equals("")) { String delimiter = "/"; String[] parseElement = jsonParsePath.split(delimiter); for(int i =0; i < parseElement.length ; i++) { rootNode = rootNode.path(parseElement[i]); } if (rootNode.isMissingNode()) { throw new IllegalArgumentException("Could not find jSON elements " + jsonParsePath); } } for (int i = 0; i < rootNode.size(); i++) { JsonNode node = rootNode.get(i); if (node == null) { continue; } BikeRentalStation brstation = makeStation(node); if (brstation != null) out.add(brstation); } synchronized(this) { stations = out; } } private String convertStreamToString(java.io.InputStream is) { java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; } @Override public synchronized List<BikeRentalStation> getStations() { return stations; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public abstract BikeRentalStation makeStation(JsonNode rentalStationNode); public String toString() { return getClass().getName() + "(" + url + ")"; } }