/*
* Copyright (c) 2011. Gridshore
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.gridshore.nosapi.impl;
import nl.gridshore.nosapi.*;
import nl.gridshore.nosapi.mapping.*;
import nl.gridshore.nosapi.mapping.DayGuide;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
/**
* <p>The default and for now only implementation of the DataProvider interface. This implementation is based on the
* RESTTemplate from the spring framework. You can provide your own instance of the RESTTemplate. The default
* RESTTemplate makes use of a specific ErrorHandler <code>NosApiResponseErrorHandler</code>.</p>
* <p>This class is based on the NOS api version v1</p>
*
* @author Jettro Coenradie
*/
public class DataProviderImpl implements DataProvider {
private final static Logger logger = LoggerFactory.getLogger(DataProviderImpl.class);
private final static DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
private String serverBaseUrl = "http://open.nos.nl/v2/";
private final String apiKey;
private final RestTemplate restTemplate;
public DataProviderImpl(String apiKey) {
this(apiKey, new RestTemplate());
restTemplate.setErrorHandler(new NosApiResponseErrorHandler());
}
public DataProviderImpl(String apiKey, RestTemplate restTemplate) {
Assert.hasText(apiKey);
Assert.notNull(restTemplate);
this.restTemplate = restTemplate;
this.apiKey = apiKey;
}
@Override
public nl.gridshore.nosapi.Version obtainVersion() {
VersionWrapper versionWrapper = restTemplate.getForObject(
serverBaseUrl + "index/version/key/{apikey}/output/json", VersionWrapper.class, apiKey);
return new nl.gridshore.nosapi.Version(
versionWrapper.getVersions().get(0).getBuild(),
versionWrapper.getVersions().get(0).getVersion());
}
@Override
public List<nl.gridshore.nosapi.Article> obtainLatestNewsArticles() {
return obtainLatestItemPerCategory("nieuws/", "article", LatestArticle.class);
}
@Override
public List<nl.gridshore.nosapi.Article> obtainLatestSportsNewsArticles() {
return obtainLatestItemPerCategory("sport/", "article", LatestArticle.class);
}
@Override
public List<nl.gridshore.nosapi.Article> obtainLatestNewsVideos() {
return obtainLatestItemPerCategory("nieuws/", "video", LatestVideo.class);
}
@Override
public List<nl.gridshore.nosapi.Article> obtainLatestSportsNewsVideos() {
return obtainLatestItemPerCategory("sport/", "video", LatestVideo.class);
}
@Override
public List<nl.gridshore.nosapi.Article> obtainLatestNewsAudio() {
return obtainLatestItemPerCategory("nieuws/", "audio", LatestAudio.class);
}
@Override
public List<nl.gridshore.nosapi.Article> obtainLatestSportsNewsAudio() {
return obtainLatestItemPerCategory("sport/", "audio", LatestAudio.class);
}
@Override
public SearchResults searchForDocuments(String queryString) {
return this.searchForDocuments(queryString,null);
}
@Override
public SearchResults searchForDocuments(String queryString, SearchSort sort) {
Assert.notNull(queryString);
String searchSort;
if (sort == null) {
searchSort = SearchSort.SCORE.toString().toLowerCase();
} else {
searchSort = sort.toString().toLowerCase();
}
String url = serverBaseUrl + "search/query/key/{apikey}/output/json/q/{search}/sort/{sort}";
SearchWrapper searchResults;
try {
searchResults = restTemplate.getForObject(url, SearchWrapper.class, apiKey, queryString,searchSort);
} catch (HttpMessageNotReadableException e) {
logger.error("There might be a problem on the server while searching for documents. Usually this exception " +
"is thrown if the json returned has a wrong format. The used query is {}",
queryString, e.getMostSpecificCause());
throw new UnknownClientException("Most probably an incorrect response was received from the nos data api",
e.getMostSpecificCause());
}
ArrayList<nl.gridshore.nosapi.mapping.Document> documents = searchResults.getSearch().get(0).getDocuments();
List<nl.gridshore.nosapi.Document> foundDocuments = new ArrayList<nl.gridshore.nosapi.Document>();
for (nl.gridshore.nosapi.mapping.Document document : documents) {
foundDocuments.add(new nl.gridshore.nosapi.Document(
document.getId(),
document.getType(),
document.getTitle(),
document.getDescription(),
document.getCategory(),
document.getSubCategory(),
document.getPublished(),
document.getLastUpdate(),
document.getLink(),
document.getThumbnail(),
document.getScore(),
document.getKeywords()
));
}
ArrayList<nl.gridshore.nosapi.mapping.Keyword> keywords = searchResults.getSearch().get(0).getKeywords();
List<nl.gridshore.nosapi.Keyword> foundKeywords = new ArrayList<nl.gridshore.nosapi.Keyword>();
for (nl.gridshore.nosapi.mapping.Keyword keyword : keywords) {
foundKeywords.add(new nl.gridshore.nosapi.Keyword(keyword.getTag(), keyword.getCount()));
}
return new SearchResults(foundDocuments, foundKeywords);
}
@Override
public List<nl.gridshore.nosapi.DayGuide> obtainTVGuide() {
return obtainGuide("tv");
}
@Override
public List<nl.gridshore.nosapi.DayGuide> obtainRadioGuide() {
return obtainGuide("radio");
}
@Override
public List<nl.gridshore.nosapi.DayGuide> obtainTVGuide(TVChannel channel, String startDate, String endDate) {
String channelName = (channel != null)?channel.toString():null;
return obtainGuide(channelName, startDate, endDate, "tv");
}
@Override
public List<nl.gridshore.nosapi.DayGuide> obtainRadioGuide(RadioChannel channel, String startDate, String endDate) {
String channelName = (channel != null)?channel.toString():null;
return obtainGuide(channelName, startDate, endDate, "radio");
}
private List<nl.gridshore.nosapi.DayGuide> obtainGuide(String channel, String startDate, String endDate, String type) {
validateStartEndDate(startDate, endDate);
StringBuilder builder = new StringBuilder();
builder.append(serverBaseUrl);
builder.append("guide/{type}/key/{apikey}/output/json");
List<Comparable> arguments = new ArrayList<Comparable>();
arguments.add(type);
arguments.add(apiKey);
if (channel != null) {
builder.append("/channel/{channel}");
arguments.add(channel);
}
if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
builder.append("/start/{startdate}");
arguments.add(startDate);
builder.append("/end/{enddate}");
arguments.add(endDate);
}
Guide guide = restTemplate.getForObject(builder.toString(), Guide.class, arguments.toArray());
return mapJSONGuideToOurGuide(guide);
}
private void validateStartEndDate(String startDate, String endDate) {
if (StringUtils.hasText(startDate) || StringUtils.hasText(endDate)) {
if (!StringUtils.hasText(startDate) || !StringUtils.hasText(endDate)) {
throw new IllegalArgumentException(
"When providing a start or end data, the other one needs to be provided as well.");
}
DateTime startTime = formatter.parseDateTime(startDate);
DateTime endTime = formatter.parseDateTime(endDate);
if (endTime.isBefore(startTime)) {
throw new IllegalArgumentException("Start date must be before end date");
}
}
}
private List<nl.gridshore.nosapi.DayGuide> obtainGuide(String type) {
String url = serverBaseUrl + "guide/{type}/key/{apikey}/output/json";
Guide guide = restTemplate.getForObject(url, Guide.class, type, apiKey);
return mapJSONGuideToOurGuide(guide);
}
private List<nl.gridshore.nosapi.DayGuide> mapJSONGuideToOurGuide(Guide guide) {
ArrayList<DayGuide> dayGuides = guide.getGuide().get(0);
List<nl.gridshore.nosapi.DayGuide> days = new ArrayList<nl.gridshore.nosapi.DayGuide>();
for (DayGuide dayGuide : dayGuides) {
List<Program> programs = new ArrayList<Program>();
for (GuideItem guideItem :dayGuide.getGuide()) {
programs.add(new Program(guideItem.getId(),
guideItem.getTitle(),
guideItem.getType(),
guideItem.getGenre(),
guideItem.getDescription(),
guideItem.getStartTime(),
guideItem.getEndTime(),
new Channel(guideItem.getChannelCode(),guideItem.getChannelIcon(),guideItem.getChannelName())
));
}
days.add(new nl.gridshore.nosapi.DayGuide(dayGuide.getDate(),programs));
}
return days;
}
private List<nl.gridshore.nosapi.Article> obtainLatestItemPerCategory(String category, String type, Class<? extends LatestItem> clazz) {
String url = serverBaseUrl + "latest/" + type + "/key/{apikey}/output/json/category/" + category;
LatestItem latestItem = restTemplate.getForObject(url, clazz, apiKey);
List<nl.gridshore.nosapi.Article> articles = new ArrayList<nl.gridshore.nosapi.Article>();
for (nl.gridshore.nosapi.mapping.Article article : latestItem.getItems().get(0)) {
articles.add(new nl.gridshore.nosapi.Article(
article.getId(),
article.getType(),
article.getTitle(),
article.getDescription(),
article.getLink(),
article.getKeywords(),
article.getLastUpdate(),
article.getPublished(),
article.getThumbnail_m(),
article.getThumbnail_s(),
article.getThumbnail_xs(),
article.getEmbedCode()
));
}
return articles;
}
}