/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.server.rest;
import java.util.Arrays;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.fcrepo.common.PID;
import org.fcrepo.server.Context;
import org.fcrepo.server.Server;
import org.fcrepo.server.errors.ServerException;
import org.fcrepo.server.storage.types.RelationshipTuple;
import org.fcrepo.server.storage.types.TupleArrayTripleIterator;
import org.fcrepo.utilities.ReadableCharArrayWriter;
import org.springframework.stereotype.Component;
import org.trippi.RDFFormat;
import org.trippi.TripleIterator;
import org.trippi.TrippiException;
/**
* A rest controller to handle CRUD operations for the Fedora relationships API.
*
* @author Edwin Shin
* @version $Id$
* @since 3.4.0
*/
@Path(BaseRestResource.VALID_PID_PART + "/relationships")
@Component
public class RelationshipResource extends BaseRestResource {
public RelationshipResource(Server server) {
super(server);
}
/**
* Get relationships asserted by the object denoted by <i>pid</i>.
*
* @param pid The pid of the Fedora object, e.g. demo:1.
* @param subject The subject uri. If null, defaults to the URI form of pid,
* e.g. info:fedora/demo:1.
* @param predicate The predicate uri or null to match any predicate.
* @param format one of "rdf/xml", "n-triples", "turtle", or "sparql".
* If null, defaults to rdf/xml.
* @return the relationships in the specified format.
*/
@GET
@Produces({"application/rdf+xml", "text/plain", "application/x-turtle",
"application/sparql-results+xml"})
public Response getRelationships(
@PathParam(RestParam.PID)
String pid,
@QueryParam(RestParam.SUBJECT)
String subject,
@QueryParam(RestParam.PREDICATE)
String predicate,
@QueryParam(RestParam.FORMAT)
@DefaultValue("rdf/xml")
String format,
@QueryParam(RestParam.FLASH)
@DefaultValue("false")
boolean flash) {
Context context = getContext();
if (subject == null) {
// assume the subject is the object as denoted by the pid
subject = PID.toURI(pid);
}
try {
RelationshipTuple[] tuples = m_management.getRelationships(context, subject, predicate);
TripleIterator it = new TupleArrayTripleIterator(Arrays.asList(tuples));
format = format.toLowerCase();
RDFFormat outputFormat;
MediaType mediaType;
if (format.equalsIgnoreCase("xml") || format.equals("rdf/xml")) {
outputFormat = RDFFormat.RDF_XML;
mediaType = new MediaType("application", "rdf+xml");
} else if (format.equals("n-triples") || format.equals("ntriples")) {
outputFormat = RDFFormat.N_TRIPLES;
mediaType = MediaType.TEXT_PLAIN_TYPE;
} else if (format.equals("turtle")) {
outputFormat = RDFFormat.TURTLE;
mediaType = new MediaType("application", "x-turtle");
} else if (format.equals("sparql")) {
outputFormat = RDFFormat.SPARQL;
mediaType = new MediaType("application", "sparql-results+xml");
} else {
throw new IllegalArgumentException("unknown format: " + format);
}
ReadableCharArrayWriter out = new ReadableCharArrayWriter(256 * tuples.length);
it.toStream(out, outputFormat, true);
return Response.ok(out.getString(), mediaType).build();
} catch (ServerException e) {
return handleException(e, flash);
} catch (TrippiException e) {
return handleException(e, flash);
}
}
/**
* Add a relationship.
* <br>
* POST /objects/{pid}/relationships/new ? subject predicate object isLiteral datatype
* <br>
* Successful Response:
* Status: 200 OK
*/
@Path("/new")
@POST
public Response addRelationship(
@PathParam(RestParam.PID)
String pid,
@QueryParam(RestParam.SUBJECT)
String subject,
@QueryParam(RestParam.PREDICATE)
String predicate,
@QueryParam(RestParam.OBJECT)
String object,
@QueryParam(RestParam.IS_LITERAL)
boolean isLiteral,
@QueryParam(RestParam.DATATYPE)
String datatype,
@QueryParam(RestParam.FLASH)
@DefaultValue("false")
boolean flash) {
Context context = getContext();
try {
if (subject == null) {
// assume the subject is the object as denoted by the pid
subject = PID.toURI(pid);
}
boolean result = m_management.addRelationship(context, subject, predicate, object, isLiteral, datatype);
return Response.ok(Boolean.toString(result)).build(); // needs an entity to not be overridden with a 204
} catch (ServerException e) {
return handleException(e, flash);
}
}
/**
* Delete a relationship.
* <br>
* DELETE /objects/{pid}/relationships ? subject predicate object isLiteral datatype
* <br>
* Successful Response:
* Status: 200 OK
*/
@DELETE
public Response purgeRelationship(
@PathParam(RestParam.PID)
String pid,
@QueryParam(RestParam.SUBJECT)
String subject,
@QueryParam(RestParam.PREDICATE)
String predicate,
@QueryParam(RestParam.OBJECT)
String object,
@QueryParam(RestParam.IS_LITERAL)
boolean isLiteral,
@QueryParam(RestParam.DATATYPE)
String datatype,
@QueryParam(RestParam.FLASH)
@DefaultValue("false")
boolean flash) {
Context context = getContext();
try {
if (subject == null) {
// assume the subject is the object as denoted by the pid
subject = PID.toURI(pid);
}
boolean result = m_management.purgeRelationship(context, subject, predicate, object, isLiteral, datatype);
return Response.ok(Boolean.toString(result), MediaType.TEXT_PLAIN_TYPE).build();
} catch (ServerException e) {
return handleException(e, flash);
}
}
}