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 + ")";
}
}