001/*
002 * Copyright (c) 2006-2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Bogdan Stefanescu
011 *     Florent Guillaume
012 */
013package org.nuxeo.ecm.core.api.model.impl.primitives;
014
015import java.io.ByteArrayInputStream;
016import java.io.Serializable;
017import java.util.Map;
018
019import org.apache.commons.lang.ObjectUtils;
020import org.nuxeo.ecm.core.api.Blob;
021import org.nuxeo.ecm.core.api.NuxeoException;
022import org.nuxeo.ecm.core.api.PropertyException;
023import org.nuxeo.ecm.core.api.model.Property;
024import org.nuxeo.ecm.core.api.model.PropertyConversionException;
025import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
026import org.nuxeo.ecm.core.api.model.impl.MapProperty;
027import org.nuxeo.ecm.core.api.model.impl.ScalarProperty;
028import org.nuxeo.ecm.core.schema.types.Field;
029
030/**
031 * Blob property, reading and writing from a {@link Blob} object.
032 */
033public class BlobProperty extends MapProperty {
034
035    private static final long serialVersionUID = 1L;
036
037    private static final String NAME = "name";
038
039    private static final String MIME_TYPE = "mime-type";
040
041    private static final String ENCODING = "encoding";
042
043    private static final String DIGEST = "digest";
044
045    private static final String LENGTH = "length";
046
047    protected Serializable value;
048
049    public BlobProperty(Property parent, Field field, int flags) {
050        super(parent, field, flags);
051    }
052
053    @Override
054    public Serializable getDefaultValue() {
055        return null;
056    }
057
058    @Override
059    public Serializable internalGetValue() throws PropertyException {
060        return value;
061    }
062
063    @Override
064    public void internalSetValue(Serializable value) throws PropertyException {
065        this.value = value;
066    }
067
068    @Override
069    public final Object clone() throws CloneNotSupportedException {
070        return (BlobProperty) super.clone();
071    }
072
073    @Override
074    public boolean isNormalized(Object value) {
075        return value == null || ((value instanceof Blob) && (value instanceof Serializable));
076    }
077
078    @Override
079    public Serializable normalize(Object value) throws PropertyConversionException {
080        if (isNormalized(value)) {
081            // TODO specific blob support?
082            return (Serializable) value;
083        }
084        throw new PropertyConversionException(value.getClass(), Blob.class);
085    }
086
087    @SuppressWarnings("unchecked")
088    @Override
089    public <T> T convertTo(Serializable value, Class<T> toType) throws PropertyConversionException {
090        if (value == null) {
091            return null;
092        }
093        if (Blob.class.isAssignableFrom(toType)) {
094            return (T) value;
095        }
096        throw new PropertyConversionException(value.getClass(), toType);
097    }
098
099    @Override
100    public Object newInstance() {
101        return new ByteArrayInputStream("".getBytes()); // TODO not serializable
102    }
103
104    @Override
105    public boolean isContainer() {
106        return false;
107    }
108
109    @SuppressWarnings("unchecked")
110    @Override
111    public void setValue(Object value) throws PropertyException {
112        if (value instanceof Map) {
113            setMap(getValue(), (Map<String, Object>) value);
114            setIsModified();
115        } else {
116            super.setValue(value);
117        }
118    }
119
120    @Override
121    protected boolean isSameValue(Serializable value1, Serializable value2) {
122        // for now, blob property are considered always as dirty when update - see NXP-16322
123        return false;
124    }
125
126    @Override
127    public void init(Serializable value) throws PropertyException {
128        if (value == null) {
129            // IGNORE null values - properties will be
130            // considered PHANTOMS
131            return;
132        }
133        if (value instanceof Blob) {
134            internalSetValue(value);
135        }
136        removePhantomFlag();
137    }
138
139    @Override
140    public Serializable getValueForWrite() throws PropertyException {
141        return getValue();
142    }
143
144    @Override
145    protected Property internalGetChild(Field field) {
146        return new ScalarMemberProperty(this, field, isPhantom() ? IS_PHANTOM : 0);
147    }
148
149    protected void setMap(Object object, Map<String, Object> value) throws PropertyException {
150        if (object == null) {
151            throw new NuxeoException("Trying to access a member of a null object");
152        }
153        if (!(object instanceof Blob)) {
154            throw new NuxeoException("Not a Blob: " + object);
155        }
156        Blob blob = (Blob) object;
157        for (Entry<String, Object> entry : value.entrySet()) {
158            String name = entry.getKey();
159            Object v = entry.getValue();
160            setMemberValue(blob, name, v);
161        }
162    }
163
164    protected void setMemberValue(Blob blob, String name, Object value) throws PropertyNotFoundException {
165        if (NAME.equals(name)) {
166            blob.setFilename((String) value);
167        } else if (MIME_TYPE.equals(name)) {
168            blob.setMimeType((String) value);
169        } else if (ENCODING.equals(name)) {
170            blob.setEncoding((String) value);
171        } else if (DIGEST.equals(name)) {
172            blob.setDigest((String) value);
173        } else {
174            throw new PropertyNotFoundException(name);
175        }
176    }
177
178    protected Object getMemberValue(Object object, String name) throws PropertyException {
179        if (object == null) {
180            throw new NuxeoException("Trying to access a member of a null object: " + name);
181        }
182        if (!(object instanceof Blob)) {
183            throw new NuxeoException("Not a Blob: " + object);
184        }
185        Blob blob = (Blob) object;
186        if (NAME.equals(name)) {
187            return blob.getFilename();
188        } else if (MIME_TYPE.equals(name)) {
189            return blob.getMimeType();
190        } else if (ENCODING.equals(name)) {
191            return blob.getEncoding();
192        } else if (DIGEST.equals(name)) {
193            return blob.getDigest();
194        } else if (LENGTH.equals(name)) {
195            return Long.valueOf(blob.getLength());
196        } else {
197            throw new PropertyNotFoundException(name);
198        }
199    }
200
201    protected void setMemberValue(Object object, String name, Object value) throws PropertyException {
202        if (object == null) {
203            throw new NuxeoException("Trying to access a member of a null object: " + name);
204        }
205        if (!(object instanceof Blob)) {
206            throw new NuxeoException("Not a Blob: " + object);
207        }
208        Blob blob = (Blob) object;
209        setMemberValue(blob, name, value);
210    }
211
212    public static class ScalarMemberProperty extends ScalarProperty {
213
214        private static final long serialVersionUID = 1L;
215
216        public ScalarMemberProperty(Property parent, Field field, int flags) {
217            super(parent, field, flags);
218        }
219
220        @Override
221        public void internalSetValue(Serializable value) throws PropertyException {
222            ((BlobProperty) parent).setMemberValue(parent.getValue(), getName(), value);
223        }
224
225        @Override
226        public Serializable internalGetValue() throws PropertyException {
227            Object value = ((BlobProperty) parent).getMemberValue(parent.getValue(), getName());
228            if (value != null && !(value instanceof Serializable)) {
229                throw new PropertyException("Non serializable value: " + value);
230            }
231            return (Serializable) value;
232        }
233    }
234
235    @Override
236    public boolean isSameAs(Property property) throws PropertyException {
237        if (!(property instanceof BlobProperty)) {
238            return false;
239        }
240        BlobProperty other = (BlobProperty) property;
241        return ObjectUtils.equals(getValue(), other.getValue());
242    }
243
244}