package won.protocol.rest;
import org.apache.jena.query.Dataset;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.NodeIterator;
import org.apache.jena.rdf.model.Property;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import won.protocol.exception.IncorrectPropertyCountException;
import won.protocol.util.RdfUtils;
import won.protocol.vocabulary.CNT;
import won.protocol.vocabulary.WONMSG;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
/**
* User: fsalcher
* Date: 15.09.2014
*/
public class RdfDatasetAttachmentConverter extends AbstractHttpMessageConverter<Dataset>
{
private static final Logger logger = LoggerFactory.getLogger(RdfDatasetAttachmentConverter.class);
private static final MediaType[] supportedMediaTypes = {
MediaType.ALL // we can do this because we have placed a hack in the LinkedDataWebController that
//looks into the dataset and determines the concrete MediaType of the base64-encoded content in the
//dataset. Thus, spring's content negotiation will choose this class as a compatible converter and the
//concrete media type as the response media type.
};
public RdfDatasetAttachmentConverter() {
this(supportedMediaTypes);
}
public RdfDatasetAttachmentConverter(MediaType supportedMediaType) {
super(supportedMediaType);
}
public RdfDatasetAttachmentConverter(MediaType... supportedMediaTypes) {
super(supportedMediaTypes);
}
@Override
protected boolean supports(Class<?> clazz) {
return Dataset.class.isAssignableFrom(clazz);
}
@Override
protected Dataset readInternal(Class<? extends Dataset> aClass, HttpInputMessage httpInputMessage) throws IOException,
HttpMessageNotReadableException {
throw new UnsupportedOperationException("Cannot convert arbitrary data to RDF dataset yet.");
}
@Override
protected void writeInternal(Dataset dataset, HttpOutputMessage httpOutputMessage) throws IOException,
HttpMessageNotWritableException {
ContentAndMimeType content = RdfUtils.findOne(dataset, new RdfUtils.ModelVisitor<ContentAndMimeType>() {
@Override
public ContentAndMimeType visit(Model model) {
String content = getObjectOfPropertyAsString(model, CNT.BYTES);
if (content == null) return null;
String contentType = getObjectOfPropertyAsString(model, WONMSG.CONTENT_TYPE);
return new ContentAndMimeType(content, contentType);
}
}, false);
if (content.content == null) throw new IncorrectPropertyCountException("expected one property cnt:bytes", 1,0);
if (content.mimeType == null) throw new IncorrectPropertyCountException("expected one property msg:contentType",
1,0);
//TODO: here, we decode the base64 content only to have it encoded again by the framework. Can we tell the webMVC framework not to encode in base64 somehow?
httpOutputMessage.getHeaders().setContentType(MediaType.valueOf(content.mimeType));
OutputStream body = httpOutputMessage.getBody();
body.write(Base64.getDecoder().decode(content.content));
body.flush();
}
private String getObjectOfPropertyAsString(Model model, Property property){
NodeIterator nodeIteratr = model.listObjectsOfProperty(property);
if (!nodeIteratr.hasNext()) return null;
String ret = nodeIteratr.next().asLiteral().getString();
if (nodeIteratr.hasNext()) {
throw new IncorrectPropertyCountException("found more than one property of cnt:bytes", 1, 2);
}
return ret;
}
public List<MediaType> getSupportedMediaTypes(){
return Arrays.asList(supportedMediaTypes);
}
private class ContentAndMimeType{
public String content;
public String mimeType;
public ContentAndMimeType(final String content, final String mimeType) {
this.content = content;
this.mimeType = mimeType;
}
}
}