001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.core.opencmis.impl.client;
020
021import java.io.InputStream;
022import java.math.BigDecimal;
023import java.math.BigInteger;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.GregorianCalendar;
028import java.util.List;
029import java.util.Map;
030import java.util.Map.Entry;
031import java.util.Set;
032
033import org.apache.chemistry.opencmis.client.api.ChangeEvent;
034import org.apache.chemistry.opencmis.client.api.ChangeEvents;
035import org.apache.chemistry.opencmis.client.api.ObjectFactory;
036import org.apache.chemistry.opencmis.client.api.ObjectType;
037import org.apache.chemistry.opencmis.client.api.OperationContext;
038import org.apache.chemistry.opencmis.client.api.Policy;
039import org.apache.chemistry.opencmis.client.api.Property;
040import org.apache.chemistry.opencmis.client.api.QueryResult;
041import org.apache.chemistry.opencmis.client.api.Rendition;
042import org.apache.chemistry.opencmis.client.api.SecondaryType;
043import org.apache.chemistry.opencmis.client.api.Session;
044import org.apache.chemistry.opencmis.client.runtime.PropertyImpl;
045import org.apache.chemistry.opencmis.client.runtime.RenditionImpl;
046import org.apache.chemistry.opencmis.client.runtime.objecttype.DocumentTypeImpl;
047import org.apache.chemistry.opencmis.client.runtime.objecttype.FolderTypeImpl;
048import org.apache.chemistry.opencmis.client.runtime.objecttype.PolicyTypeImpl;
049import org.apache.chemistry.opencmis.client.runtime.objecttype.RelationshipTypeImpl;
050import org.apache.chemistry.opencmis.client.runtime.objecttype.SecondaryTypeImpl;
051import org.apache.chemistry.opencmis.commons.PropertyIds;
052import org.apache.chemistry.opencmis.commons.data.Ace;
053import org.apache.chemistry.opencmis.commons.data.Acl;
054import org.apache.chemistry.opencmis.commons.data.ContentStream;
055import org.apache.chemistry.opencmis.commons.data.ObjectData;
056import org.apache.chemistry.opencmis.commons.data.ObjectList;
057import org.apache.chemistry.opencmis.commons.data.Properties;
058import org.apache.chemistry.opencmis.commons.data.PropertyData;
059import org.apache.chemistry.opencmis.commons.data.PropertyId;
060import org.apache.chemistry.opencmis.commons.data.RenditionData;
061import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
062import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
063import org.apache.chemistry.opencmis.commons.definitions.FolderTypeDefinition;
064import org.apache.chemistry.opencmis.commons.definitions.PolicyTypeDefinition;
065import org.apache.chemistry.opencmis.commons.definitions.PropertyBooleanDefinition;
066import org.apache.chemistry.opencmis.commons.definitions.PropertyDateTimeDefinition;
067import org.apache.chemistry.opencmis.commons.definitions.PropertyDecimalDefinition;
068import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
069import org.apache.chemistry.opencmis.commons.definitions.PropertyHtmlDefinition;
070import org.apache.chemistry.opencmis.commons.definitions.PropertyIdDefinition;
071import org.apache.chemistry.opencmis.commons.definitions.PropertyIntegerDefinition;
072import org.apache.chemistry.opencmis.commons.definitions.PropertyStringDefinition;
073import org.apache.chemistry.opencmis.commons.definitions.PropertyUriDefinition;
074import org.apache.chemistry.opencmis.commons.definitions.RelationshipTypeDefinition;
075import org.apache.chemistry.opencmis.commons.definitions.SecondaryTypeDefinition;
076import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
077import org.apache.chemistry.opencmis.commons.enums.Cardinality;
078import org.apache.chemistry.opencmis.commons.enums.Updatability;
079import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
080import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
081import org.apache.chemistry.opencmis.commons.impl.dataobjects.BindingsObjectFactoryImpl;
082import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
083import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
084import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
085import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoObjectData;
086
087/**
088 * Factory for {@link NuxeoObject} and its related classes.
089 */
090public class NuxeoObjectFactory implements ObjectFactory {
091
092    private final NuxeoSession session;
093
094    private static final BindingsObjectFactory of = new BindingsObjectFactoryImpl();
095
096    public NuxeoObjectFactory(NuxeoSession session) {
097        this.session = session;
098    }
099
100    @Override
101    public void initialize(Session session, Map<String, String> parameters) {
102        // TODO
103        throw new UnsupportedOperationException();
104    }
105
106    @Override
107    public RepositoryInfo convertRepositoryInfo(RepositoryInfo repositoryInfo) {
108        return repositoryInfo;
109    }
110
111    @Override
112    public NuxeoObject convertObject(ObjectData data, OperationContext context) {
113        if (data == null || data.getProperties() == null || data.getProperties().getProperties() == null) {
114            return null;
115        }
116        // object type
117        PropertyData<?> propData = data.getProperties().getProperties().get(PropertyIds.OBJECT_TYPE_ID);
118        ObjectType type = session.getTypeDefinition((String) propData.getFirstValue());
119        // secondary types
120        propData = data.getProperties().getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS);
121        List<SecondaryType> secondaryTypes;
122        if (propData == null) {
123            secondaryTypes = Collections.emptyList();
124        } else {
125            @SuppressWarnings("unchecked")
126            List<String> sts = (List<String>) propData.getValues();
127            secondaryTypes = new ArrayList<SecondaryType>(sts.size());
128            for (String st : sts) {
129                secondaryTypes.add((SecondaryType) session.getTypeDefinition(st));
130            }
131        }
132        return NuxeoObject.construct(session, (NuxeoObjectData) data, type, secondaryTypes);
133    }
134
135    @Override
136    public ObjectType getTypeFromObjectData(ObjectData objectData) {
137        // TODO Auto-generated method stub
138        throw new UnsupportedOperationException();
139    }
140
141    @Override
142    public Ace createAce(String principal, List<String> permissions) {
143        // TODO Auto-generated method stub
144        throw new UnsupportedOperationException();
145    }
146
147    @Override
148    public Acl createAcl(List<Ace> aces) {
149        // TODO Auto-generated method stub
150        throw new UnsupportedOperationException();
151    }
152
153    @Override
154    public <T> Property<T> createProperty(PropertyDefinition<T> type, List<T> values) {
155        return new PropertyImpl<T>(type, values);
156    }
157
158    @Override
159    public ContentStream createContentStream(String filename, long length, String mimetype, InputStream stream) {
160        return new ContentStreamImpl(filename, BigInteger.valueOf(length), mimetype, stream);
161    }
162
163    @Override
164    public ContentStream createContentStream(String filename, long length, String mimetype, InputStream stream,
165            boolean partial) {
166        // TODO partial
167        return createContentStream(filename, length, mimetype, stream);
168    }
169
170    @Override
171    public Acl convertAces(List<Ace> aces) {
172        return aces == null ? null : new AccessControlListImpl(aces);
173    }
174
175    @Override
176    public ContentStream convertContentStream(ContentStream contentStream) {
177        if (contentStream == null) {
178            return null;
179        }
180        long len = contentStream.getLength();
181        BigInteger length = len < 0 ? null : BigInteger.valueOf(len);
182        return of.createContentStream(contentStream.getFileName(), length, contentStream.getMimeType(),
183                contentStream.getStream());
184    }
185
186    @Override
187    public List<String> convertPolicies(List<Policy> policies) {
188        if (policies == null) {
189            return null;
190        }
191        List<String> policyIds = new ArrayList<String>(policies.size());
192        for (Policy p : policies) {
193            policyIds.add(p.getId());
194        }
195        return policyIds;
196    }
197
198    @Override
199    public Map<String, Property<?>> convertProperties(ObjectType objectType, Collection<SecondaryType> secondaryTypes,
200            Properties properties) {
201        throw new UnsupportedOperationException();
202    }
203
204    @Override
205    public Properties convertProperties(Map<String, ?> properties, ObjectType type,
206            Collection<SecondaryType> secondaryTypes, Set<Updatability> updatabilityFilter) {
207        if (properties == null) {
208            return null;
209        }
210        // TODO secondaryTypes
211        // TODO updatabilityFilter
212        PropertiesImpl props = new PropertiesImpl();
213        for (Entry<String, ?> es : properties.entrySet()) {
214            PropertyData<?> prop = convertProperty(es.getKey(), es.getValue(), type);
215            props.addProperty(prop);
216        }
217        return props;
218    }
219
220    @SuppressWarnings("unchecked")
221    protected static PropertyData<?> convertProperty(String key, Object value, ObjectType type) {
222        PropertyDefinition<?> pd = type.getPropertyDefinitions().get(key);
223        if (pd == null) {
224            throw new IllegalArgumentException("Unknown property '" + key + "' for type: " + type.getId());
225        }
226        boolean single = pd.getCardinality() == Cardinality.SINGLE;
227
228        List<?> values;
229        if (value == null) {
230            values = null;
231        } else if (value instanceof List<?>) {
232            if (single) {
233                throw new IllegalArgumentException("Property '" + key + "' is not a multi value property!");
234            }
235            values = (List<?>) value;
236        } else {
237            if (!single) {
238                throw new IllegalArgumentException("Property '" + key + "' is not a single value property!");
239            }
240            values = Collections.singletonList(value);
241        }
242        Object firstValue = values == null ? null : values.get(0);
243
244        if (pd instanceof PropertyStringDefinition) {
245            return of.createPropertyStringData(key, (List<String>) values);
246        } else if (pd instanceof PropertyIdDefinition) {
247            return of.createPropertyIdData(key, (List<String>) values);
248        } else if (pd instanceof PropertyHtmlDefinition) {
249            return of.createPropertyHtmlData(key, (List<String>) values);
250        } else if (pd instanceof PropertyUriDefinition) {
251            return of.createPropertyUriData(key, (List<String>) values);
252        } else if (pd instanceof PropertyIntegerDefinition) {
253            if (firstValue == null) {
254                return of.createPropertyIntegerData(key, (List<BigInteger>) null);
255            } else if (firstValue instanceof BigInteger) {
256                return of.createPropertyIntegerData(key, (List<BigInteger>) values);
257            } else if ((firstValue instanceof Byte) || (firstValue instanceof Short) || (firstValue instanceof Integer)
258                    || (firstValue instanceof Long)) {
259                List<BigInteger> list = new ArrayList<BigInteger>(values.size());
260                for (Object v : values) {
261                    list.add(BigInteger.valueOf(((Number) v).longValue()));
262                }
263                return of.createPropertyIntegerData(key, list);
264            } else {
265                throw new IllegalArgumentException("Property '" + key + "' is an Integer property");
266            }
267        } else if (pd instanceof PropertyBooleanDefinition) {
268            return of.createPropertyBooleanData(key, (List<Boolean>) values);
269        } else if (pd instanceof PropertyDecimalDefinition) {
270            return of.createPropertyDecimalData(key, (List<BigDecimal>) values);
271        } else if (pd instanceof PropertyDateTimeDefinition) {
272            return of.createPropertyDateTimeData(key, (List<GregorianCalendar>) values);
273        }
274        throw new CmisRuntimeException("Unknown class: " + pd.getClass().getName());
275    }
276
277    @Override
278    public List<PropertyData<?>> convertQueryProperties(Properties properties) {
279        if (properties == null || properties.getProperties() == null) {
280            return null;
281        }
282        return new ArrayList<PropertyData<?>>(properties.getPropertyList());
283    }
284
285    @Override
286    public QueryResult convertQueryResult(ObjectData objectData) {
287        throw new UnsupportedOperationException();
288    }
289
290    @Override
291    public Rendition convertRendition(String objectId, RenditionData rendition) {
292        if (rendition == null) {
293            return null;
294        }
295        BigInteger rl = rendition.getBigLength();
296        BigInteger rh = rendition.getBigHeight();
297        BigInteger rw = rendition.getBigWidth();
298        long length = rl == null ? -1 : rl.longValue();
299        int height = rh == null ? -1 : rh.intValue();
300        int width = rw == null ? -1 : rw.intValue();
301        return new RenditionImpl(session, objectId, rendition.getStreamId(), rendition.getRenditionDocumentId(),
302                rendition.getKind(), length, rendition.getMimeType(), rendition.getTitle(), height, width);
303    }
304
305    @Override
306    public ObjectType convertTypeDefinition(TypeDefinition typeDefinition) {
307        if (typeDefinition instanceof DocumentTypeDefinition) {
308            return new DocumentTypeImpl(session, (DocumentTypeDefinition) typeDefinition);
309        } else if (typeDefinition instanceof FolderTypeDefinition) {
310            return new FolderTypeImpl(session, (FolderTypeDefinition) typeDefinition);
311        } else if (typeDefinition instanceof RelationshipTypeDefinition) {
312            return new RelationshipTypeImpl(session, (RelationshipTypeDefinition) typeDefinition);
313        } else if (typeDefinition instanceof PolicyTypeDefinition) {
314            return new PolicyTypeImpl(session, (PolicyTypeDefinition) typeDefinition);
315        } else if (typeDefinition instanceof SecondaryTypeDefinition) {
316            return new SecondaryTypeImpl(session, (SecondaryTypeDefinition) typeDefinition);
317        }
318        throw new CmisRuntimeException("Unknown base class: " + typeDefinition.getClass().getName());
319    }
320
321    @Override
322    public ChangeEvent convertChangeEvent(ObjectData objectData) {
323        // TODO Auto-generated method stub
324        throw new UnsupportedOperationException();
325    }
326
327    @Override
328    public ChangeEvents convertChangeEvents(String changeLogToken, ObjectList objectList) {
329        // TODO Auto-generated method stub
330        throw new UnsupportedOperationException();
331    }
332
333}