/*
* 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:
* Anahide Tchertchian
*/
package org.eclipse.ecr.core.api.model.impl.primitives;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.ecr.core.api.Blob;
import org.eclipse.ecr.core.api.DocumentException;
import org.eclipse.ecr.core.api.blobholder.BlobHolderAdapterService;
import org.eclipse.ecr.core.api.model.InvalidPropertyValueException;
import org.eclipse.ecr.core.api.model.Property;
import org.eclipse.ecr.core.api.model.PropertyConversionException;
import org.eclipse.ecr.core.api.model.PropertyException;
import org.eclipse.ecr.core.api.model.ReadOnlyPropertyException;
import org.eclipse.ecr.core.api.model.impl.MapProperty;
import org.eclipse.ecr.core.schema.types.Field;
import org.eclipse.ecr.runtime.api.Framework;
/**
* Property handling an external blob: create/edit is done from a map, and the
* value returned is a blob.
* <p>
* Create/edit from a blob is not handled, and the blob uri cannot be retrieved
* from the blob (no api for now).
*
* @author Anahide Tchertchian
*/
public class ExternalBlobProperty extends MapProperty {
private static final long serialVersionUID = 1L;
// constants based on core-types.xsd fields. XXX Should be in model
public static final String ENCODING = "encoding";
public static final String MIME_TYPE = "mime-type";
public static final String FILE_NAME = "name";
public static final String DIGEST = "digest";
public static final String LENGTH = "length";
public static final String URI = "uri";
public ExternalBlobProperty(Property parent, Field field, int flags) {
super(parent, field, flags);
}
public ExternalBlobProperty(Property parent, Field field) {
super(parent, field);
}
@Override
@SuppressWarnings("unchecked")
public void init(Serializable value) throws PropertyException {
if (value == null) {
// IGNORE null values - properties will be
// considered PHANTOMS
return;
}
Map<String, Serializable> map;
if (value instanceof Map) {
map = (Map<String, Serializable>) value;
} else if (value instanceof Blob) {
// XXX: workaround: get the uri from the local prop because it's not on
// the Blob
map = getMapFromBlobWithUri((Blob) value);
} else {
throw new PropertyException(
"Invalid value for external blob (map or blob needed): "
+ value);
}
for (Entry<String, Serializable> entry : map.entrySet()) {
Property property = get(entry.getKey());
property.init(entry.getValue());
}
removePhantomFlag();
}
@Override
@SuppressWarnings("unchecked")
public Serializable internalGetValue() throws PropertyException {
Object mapValue = super.internalGetValue();
if (mapValue instanceof Map) {
Blob blob = getBlobFromMap((Map) mapValue);
if (blob != null && !(blob instanceof Serializable)) {
throw new PropertyException("Blob is not serializable: " + blob);
}
return (Serializable) blob;
} else if (mapValue != null) {
throw new PropertyException(
"Invalid value for external blob (map needed): " + mapValue);
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public <T> T getValue(Class<T> type) throws PropertyException {
Object value = super.internalGetValue();
if (value == null) {
return null;
}
if (value instanceof Map) {
if (Map.class.isAssignableFrom(type)) {
return (T) value;
}
if (Blob.class.isAssignableFrom(type)) {
return (T) getBlobFromMap((Map) value);
}
}
throw new PropertyConversionException(value.getClass(), type);
}
@Override
public Serializable getValueForWrite() throws PropertyException {
return (Serializable) getValue(Map.class);
}
/**
* Overridden to be able to set a blob from a given map.
* <p>
* Take care of not overriding the uri if set as this information is not on
* the blob.
*
* @throws PropertyException if one of the sub properties throws an
* exception or if trying to set values to a blob without any
* already existing uri set.
*/
@Override
@SuppressWarnings("unchecked")
public void setValue(Object value) throws PropertyException {
if (!isContainer()) { // if not a container use default setValue()
super.setValue(value);
return;
}
if (isReadOnly()) {
throw new ReadOnlyPropertyException(getPath());
}
if (value == null) {
remove();
return; // TODO how to treat nulls?
}
if (value instanceof Blob) {
Property property = get(URI);
Object uri = property.getValue();
if (uri == null) {
throw new PropertyException(
"Cannot set blob properties without "
+ "an existing uri set");
}
// only update additional properties
Map<String, Serializable> map = getMapFromBlob((Blob) value);
for (Entry<String, Serializable> entry : map.entrySet()) {
String entryKey = entry.getKey();
if (entryKey != URI) {
property = get(entryKey);
property.setValue(entry.getValue());
}
}
return;
}
if (!(value instanceof Map)) {
throw new InvalidPropertyValueException(getPath());
}
Map<String, Object> map = (Map<String, Object>) value;
for (Entry<String, Object> entry : map.entrySet()) {
Property property = get(entry.getKey());
property.setValue(entry.getValue());
}
}
public static Blob getBlobFromMap(Map<String, Object> mapValue) {
if (mapValue == null) {
return null;
}
String uri = (String) mapValue.get(URI);
if (uri == null || "".equals(uri)) {
return null;
}
String filename = (String) mapValue.get(FILE_NAME);
String mimeType = (String) mapValue.get(MIME_TYPE);
String encoding = (String) mapValue.get(ENCODING);
String digest = (String) mapValue.get(DIGEST);
try {
BlobHolderAdapterService service = Framework.getService(BlobHolderAdapterService.class);
if (service == null) {
throw new DocumentException(
"BlobHolderAdapterService not found");
}
Blob blob = service.getExternalBlobForUri(uri);
if (filename != null) {
blob.setFilename(filename);
}
blob.setMimeType(mimeType);
blob.setEncoding(encoding);
// TODO maybe check if digest is still a match to the retrieved blob
blob.setDigest(digest);
return blob;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Map<String, Serializable> getMapFromBlobWithUri(Blob blob)
throws PropertyException {
Map<String, Serializable> map = getMapFromBlob(blob);
Property property = get(URI);
Serializable uri = property.getValue();
map.put(URI, uri);
return map;
}
public static Map<String, Serializable> getMapFromBlob(Blob blob) {
Map<String, Serializable> map = new HashMap<String, Serializable>();
if (blob == null) {
map.put(FILE_NAME, null);
map.put(MIME_TYPE, null);
map.put(ENCODING, null);
map.put(LENGTH, null);
map.put(DIGEST, null);
} else {
// cannot return uri for blob for now: no edit implemented
map.put(FILE_NAME, blob.getFilename());
map.put(MIME_TYPE, blob.getMimeType());
map.put(ENCODING, blob.getEncoding());
map.put(LENGTH, blob.getLength());
map.put(DIGEST, blob.getDigest());
}
return map;
}
}