/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bstefanescu
*/
package org.eclipse.ecr.automation.server.jaxrs.io;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.List;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import net.sf.json.JSONArray;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.core.api.Blob;
import org.eclipse.ecr.core.api.DocumentModel;
import org.eclipse.ecr.core.api.Lock;
import org.eclipse.ecr.core.api.model.DocumentPart;
import org.eclipse.ecr.core.api.model.Property;
import org.eclipse.ecr.core.api.model.impl.ArrayProperty;
import org.eclipse.ecr.core.api.model.impl.ComplexProperty;
import org.eclipse.ecr.core.api.model.impl.ListProperty;
import org.eclipse.ecr.core.api.model.impl.primitives.BlobProperty;
import org.eclipse.ecr.core.schema.types.ListType;
import org.eclipse.ecr.core.schema.utils.DateParser;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.nuxeo.common.utils.StringUtils;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
@Provider
@Produces({ "application/json+nxentity", "application/json" })
public class JsonDocumentWriter implements MessageBodyWriter<DocumentModel> {
private static final Log log = LogFactory.getLog(JsonDocumentWriter.class);
@Context
protected HttpHeaders headers;
public long getSize(DocumentModel arg0, Class<?> arg1, Type arg2,
Annotation[] arg3, MediaType arg4) {
return -1L;
}
public boolean isWriteable(Class<?> arg0, Type arg1, Annotation[] arg2,
MediaType arg3) {
return DocumentModel.class.isAssignableFrom(arg0);
}
public void writeTo(DocumentModel doc, Class<?> arg1, Type arg2,
Annotation[] arg3, MediaType arg4,
MultivaluedMap<String, Object> arg5, OutputStream arg6)
throws IOException, WebApplicationException {
try {
// schema names: dublincore, file, ... or *
List<String> props = headers.getRequestHeader("X-NXDocumentProperties");
JSONObject obj = null;
if (props == null || props.isEmpty()) {
obj = getJSON(doc, null);
} else {
obj = getJSON(doc, StringUtils.split(props.get(0), ',', true));
}
arg6.write(obj.toString(2).getBytes("UTF-8"));
} catch (Exception e) {
log.error("Failed to serialize document", e);
throw new WebApplicationException(500);
}
}
public static JSONObject getJSON(DocumentModel doc, String[] schemas)
throws Exception {
JSONObject json = new JSONObject();
json.element("entity-type", "document");
json.element("repository", doc.getRepositoryName());
json.element("uid", doc.getId());
json.element("path", doc.getPathAsString());
json.element("type", doc.getType());
json.element("state", doc.getCurrentLifeCycleState());
json.element("lock", doc.getLock()); // old
Lock lock = doc.getLockInfo();
if (lock != null) {
json.element("lockOwner", lock.getOwner());
json.element(
"lockCreated",
ISODateTimeFormat.dateTime().print(
new DateTime(lock.getCreated())));
}
json.element("title", doc.getTitle());
Calendar cal = (Calendar) doc.getPart("dublincore").getValue("modified");
if (cal != null) {
json.element("lastModified",
DateParser.formatW3CDateTime(cal.getTime()));
}
if (schemas == null || schemas.length == 0) {
return json;
}
JSONObject props = new JSONObject();
if (schemas.length == 1 && "*".equals(schemas[0])) { // full
// document
for (String schema : doc.getSchemas()) {
addSchema(props, doc, schema);
}
} else {
for (String schema : schemas) {
addSchema(props, doc, schema);
}
}
json.element("properties", props);
return json;
}
protected static void addSchema(JSONObject json, DocumentModel doc,
String schema) throws Exception {
DocumentPart part = doc.getPart(schema);
if (part==null) {
return;
}
String prefix = part.getSchema().getNamespace().prefix;
if (prefix == null || prefix.length() == 0) {
prefix = schema;
}
prefix += ':';
String filesBaseUrl = "files/" + doc.getId() + "?path=";
for (Property p : part.getChildren()) {
json.element(prefix + p.getField().getName().getLocalName(),
propertyToJsonValue(filesBaseUrl, p));
}
}
/**
* Converts the given core property to JSON format. The given filesBaseUrl
* is the baseUrl that can be used to locate blob content and is useful to
* generate blob urls.
*/
protected static Object propertyToJsonValue(final String filesBaseUrl,
Property prop) throws Exception {
org.eclipse.ecr.core.schema.types.Type type = prop.getType();
if (prop.isScalar()) {
Object v = prop.getValue();
if (v == null) {
return JSONNull.getInstance();
}
return type.encode(v);
} else if (prop.isList()) {
if (prop instanceof ArrayProperty) {
Object[] ar = (Object[]) prop.getValue();
if (ar == null) {
return new JSONArray();
}
JSONArray jsar = new JSONArray();
for (Object o : ar) {
jsar.add(((ListType)type).getFieldType().encode(o));
}
return jsar;
} else {
ListProperty listp = (ListProperty) prop;
JSONArray jsar = new JSONArray();
for (Property p : listp.getChildren()) {
jsar.add(propertyToJsonValue(filesBaseUrl, p));
}
return jsar;
}
} else {
if (prop.isPhantom()) {
return JSONNull.getInstance();
}
if (prop instanceof BlobProperty) { // a blob
Blob blob = (Blob) ((BlobProperty) prop).getValue();
JSONObject jsob = new JSONObject();
String v = blob.getFilename();
jsob.element("name", v == null ? JSONNull.getInstance() : v);
v = blob.getMimeType();
jsob.element("mime-type", v == null ? JSONNull.getInstance()
: v);
v = blob.getEncoding();
jsob.element("encoding", v == null ? JSONNull.getInstance() : v);
v = blob.getDigest();
jsob.element("digest", v == null ? JSONNull.getInstance() : v);
v = Long.toString(blob.getLength());
jsob.element("length", v);
jsob.element(
"data",
filesBaseUrl
+ URLEncoder.encode(prop.getPath(), "UTF-8"));
return jsob;
} else { // a complex property
ComplexProperty cp = (ComplexProperty) prop;
if (prop.isPhantom()) {
return JSONNull.getInstance();
}
JSONObject jsob = new JSONObject();
for (Property p : cp.getChildren()) {
jsob.put(p.getName(), propertyToJsonValue(filesBaseUrl, p));
}
return jsob;
}
}
}
}