package org.deri.grefine.reconcile.model;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;
import org.deri.grefine.reconcile.model.ReconciliationRequestContext.PropertyContext;
/**
* @author fadmaa
*
*/
public class ReconciliationRequest {
/**
* the limit used in SPARQL query when types are restricted i.e. the query asks for matches of one of specified types
*/
private static final int DEFAULT_LIMIT = 3;
/**
* the limit used in SPARQL query when types are <b>not</b> restricted. More results is typically needed in this case and this is why
* MAXIMUM_LIMIT > DEFAULT_LIMIT
*/
private static final int MAXIMUM_LIMIT = 10;
private final String queryString;
private int limit;
private String[] types;
private ReconciliationRequestContext context;
public ReconciliationRequest(String query, int limit) {
this.queryString = query;
this.limit = limit;
this.types = new String[]{};
this.context = new ReconciliationRequestContext();
}
public String getQueryString(){
return queryString;
}
public int getLimit() {
return limit;
}
public void setTypes(String[] types) {
this.types = types;
}
public String[] getTypes(){
return types;
}
public ReconciliationRequestContext getContext() {
return context;
}
public void setContext(ReconciliationRequestContext context) {
this.context = context;
}
public void setLimit(int limit) {
this.limit = limit;
}
public static ReconciliationRequest valueOf(String json) throws JsonParseException, JsonMappingException, IOException{
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readValue(json, JsonNode.class);
//get query
String query = node.path("query").getTextValue();
//get types. can be a single string or an array of strings
String[] types;
JsonNode typesNode = node.path("type");
if(! typesNode.isMissingNode()){
if(typesNode.isArray()){
ArrayNode typesNodeArray = (ArrayNode) typesNode;
types = new String[typesNodeArray.size()];
for(int i=0;i<typesNodeArray.size();i++){
types[i] = typesNodeArray.get(i).getTextValue();
}
}else{
types = new String[] {typesNode.getTextValue()};
}
}else{
types = new String[]{};
}
//get limit
int limit = node.path("limit").isMissingNode()?getDefaultLimit(types.length):node.path("limit").getIntValue();
//get type_strict
//TODO this is ignored for now
String type_strict = node.path("type_strict").getTextValue();
//get context i.e. additional properties
JsonNode propertiesNode = node.path("properties");
Set<PropertyContext> props = new HashSet<ReconciliationRequestContext.PropertyContext>();
if(propertiesNode.isArray()){
ArrayNode propertiesArray = (ArrayNode) propertiesNode;
for(int i=0; i<propertiesArray.size();i+=1){
JsonNode propertyNode = propertiesArray.get(0);
JsonNode pidNode = propertyNode.path("pid");
if(pidNode.isMissingNode()){
//only support strongly-identified properties
continue;
}
String pid = pidNode.getTextValue();
//get value
JsonNode valueNode = propertyNode.path("v");
JsonNode valueNodeId = valueNode.path("id");
ReconciliationRequestContext.ValueContext value;
if(valueNodeId.isMissingNode()){
//textual value
value = new ReconciliationRequestContext.TextualValueContext(valueNode.getTextValue());
}else{
//identified value
value = new ReconciliationRequestContext.IdentifiedValueContext(valueNodeId.getTextValue());
}
props.add(new PropertyContext(pid, value));
}
}
ReconciliationRequest request = new ReconciliationRequest(query, limit);
request.setTypes(types);
request.setContext(new ReconciliationRequestContext(props));
return request;
}
@Override
public boolean equals(Object obj) {
if (obj==null) {return false;}
if(!obj.getClass().equals(this.getClass())){ return false;}
ReconciliationRequest otherReq = (ReconciliationRequest)obj;
return this.queryString.equals(otherReq.getQueryString()) && this.getLimit()==otherReq.limit && this.getTypes().equals(otherReq.getTypes());
}
/**
* when types are restricted (i.e. typesLength>0) the query is specific and the default limit needs to be small thus we use DEFAULT_LIMIT
* when types are not restricted (i.e. typesLength==0) the query is more of a an explorative nature and needs to see what types exist for what we are looking for
* and thus we use the MAXIMUM_LIMIT
* @param typesLength
* @return
*/
private static int getDefaultLimit(int typesLength){
return typesLength==0?MAXIMUM_LIMIT:DEFAULT_LIMIT;
}
}