package org.deri.grefine.reconcile.sindice;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.deri.grefine.reconcile.model.AbstractReconciliationService;
import org.deri.grefine.reconcile.model.ReconciliationCandidate;
import org.deri.grefine.reconcile.model.ReconciliationRequest;
import org.deri.grefine.reconcile.model.ReconciliationResponse;
import org.deri.grefine.reconcile.model.SearchResultItem;
import org.deri.grefine.reconcile.rdf.endpoints.QueryEndpoint;
import org.deri.grefine.reconcile.rdf.endpoints.QueryEndpointFactory;
import org.deri.grefine.reconcile.rdf.factories.PreviewResourceCannedQuery;
import org.deri.grefine.reconcile.util.GRefineJsonUtilities;
import org.deri.grefine.reconcile.util.GRefineJsonUtilitiesImpl;
import org.deri.grefine.reconcile.util.RdfUtilities;
import org.deri.grefine.reconcile.util.RdfUtilitiesImpl;
import org.json.JSONException;
import org.json.JSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.hp.hpl.jena.rdf.model.Model;
public class SindiceService extends AbstractReconciliationService{
final static Logger logger = LoggerFactory.getLogger("SindiceService");
private QueryEndpointFactory queryEndpointFactory;
private GRefineJsonUtilities jsonUtilities;
private SindiceBroker broker;
private String domain;
private RdfUtilities rdfUtilities;
protected PreviewResourceCannedQuery previewResourceCannedQuery;
public SindiceService(String id, String name,String domain){
this(id,name,domain,new GRefineJsonUtilitiesImpl(), new RdfUtilitiesImpl(), new SindiceBroker(), new QueryEndpointFactory());
}
public SindiceService(String id, String name,String domain,GRefineJsonUtilities jsonUtilities, RdfUtilities rdfUtilities, SindiceBroker broker,QueryEndpointFactory queryEndpointFactory) {
super(id, name);
this.queryEndpointFactory = queryEndpointFactory;
this.domain = domain;
this.jsonUtilities = jsonUtilities;
this.broker = broker;
this.rdfUtilities = rdfUtilities;
try{
//TODO hard-coded for now.. accept as parameter if you need services to use different properties for perviewing
InputStream in = this.getClass().getResourceAsStream("/files/preview_properties.properties");
this.previewResourceCannedQuery = new PreviewResourceCannedQuery(in);
}catch (IOException e) {
}
}
@Override
public ReconciliationResponse reconcile(ReconciliationRequest request) {
// String type = (request.getTypes()!=null && request.getTypes().length>0)? (request.getTypes()[0]) : null;
try {
//Type is not pushed to Sindice anymore as Sindice does not handle full URI(e.g. http://xmlns.com/foaf/0.1/Person) for type properly
int sindiceDocumentsLimit = domain==null?DEFAULT_SEARCH_LIMIT:DOMAIN_SPECIFIED_SEARCH_LIMIT;
LinkedHashSet<String[]> urlPairs = broker.getUrlsForSimpleTermSearch(request.getQueryString(),domain,null, sindiceDocumentsLimit,jsonUtilities);
ImmutableList<String> empty = ImmutableList.of();
Set<ReconciliationCandidate> candidates = new LinkedHashSet<ReconciliationCandidate>();
int limit = request.getLimit();
for(String[] pair: urlPairs){
Model model = broker.getModelForUrl(pair[0], pair[1],jsonUtilities);
QueryEndpoint endpoint = queryEndpointFactory.getLarqQueryEndpoint(model);
request.setLimit(limit - candidates.size());
candidates.addAll(endpoint.reconcileEntities(request, empty, 0.9));
if(candidates.size()>=limit){
break;
}
}
//successful
return wrapCandidates(new ArrayList<ReconciliationCandidate>(candidates));
} catch (JSONException e) {
throw new RuntimeException("error reconciling " + request.getQueryString() + " using Sindice", e);
} catch (IOException e) {
throw new RuntimeException("error reconciling " + request.getQueryString() + " using Sindice", e);
}
}
@Override
public ImmutableList<SearchResultItem> suggestType(String prefix) {
throw new UnsupportedOperationException("Sindice Reconciliation service cannot suggest type");
}
@Override
public ImmutableList<SearchResultItem> suggestProperty(String prefix) {
throw new UnsupportedOperationException("Sindice Reconciliation service cannot suggest property");
}
@Override
public ImmutableList<SearchResultItem> suggestProperty(String prefix, String subjectTypeId) {
throw new UnsupportedOperationException("Sindice Reconciliation service cannot suggest property");
}
@Override
public String getPreviewHtmlForType(String typeId) throws Exception {
throw new UnsupportedOperationException("Sindice Reconciliation service cannot suggest/view type");
}
@Override
public String getPreviewHtmlForProperty(String propertyId) throws Exception {
throw new UnsupportedOperationException("Sindice Reconciliation service cannot suggest/view property");
}
@Override
public String getPreviewHtmlForResource(String resourceId) throws Exception {
Model model = rdfUtilities.dereferenceUri(resourceId);
QueryEndpoint endpoint = this.queryEndpointFactory.getLarqQueryEndpoint(model);
Multimap<String, String> propertiesMap = endpoint.getResourcePropertiesMap(previewResourceCannedQuery, resourceId);
VelocityContext context = new VelocityContext();
context.put("resourceUri", resourceId);
context.put("propertiesMap", propertiesMap);
return getHtmlFromTemplate("templates/resource_preview.vt", context);
}
@Override
public ImmutableList<SearchResultItem> suggestEntity(String prefix) {
throw new UnsupportedOperationException("Sindice Reconciliation service cannot suggest entity");
}
private ReconciliationResponse wrapCandidates(List<? extends ReconciliationCandidate> candidates){
ReconciliationResponse response = new ReconciliationResponse();
response.setResults(candidates);
return response;
}
protected String getHtmlFromTemplate(String templatePath, VelocityContext context) throws Exception{
StringWriter writer = new StringWriter();
InputStream in = this.getClass().getClassLoader().getResourceAsStream(templatePath);
VelocityEngine templateEngine = new VelocityEngine();
templateEngine.init();
templateEngine.evaluate(context, writer, "rdf-reconcile-extension", new InputStreamReader(in));
writer.close();
String html = writer.toString();
return html;
}
@Override
public void writeAsJson(JSONWriter writer, boolean saveMode)throws JSONException {
if(saveMode && domain==null){
// we do not save the generic Sindice reconciliation service. it gets added at each server startup
return;
}
writer.object();
writer.key("type"); writer.value("sindice");
writer.key("id");writer.value(this.getId());
writer.key("name");writer.value(this.getName());
if(domain!=null){
writer.key("domain");writer.value(domain);
}
writer.endObject();
}
@Override
public void initialize(FileInputStream in) {
//nothing to initialize
}
@Override
public void save(FileOutputStream out) throws IOException {
//nothing to save
}
static final int DEFAULT_SEARCH_LIMIT = 8;
static final int DOMAIN_SPECIFIED_SEARCH_LIMIT = 3;
}