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.RenditionData;
060import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
061import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
062import org.apache.chemistry.opencmis.commons.definitions.FolderTypeDefinition;
063import org.apache.chemistry.opencmis.commons.definitions.PolicyTypeDefinition;
064import org.apache.chemistry.opencmis.commons.definitions.PropertyBooleanDefinition;
065import org.apache.chemistry.opencmis.commons.definitions.PropertyDateTimeDefinition;
066import org.apache.chemistry.opencmis.commons.definitions.PropertyDecimalDefinition;
067import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
068import org.apache.chemistry.opencmis.commons.definitions.PropertyHtmlDefinition;
069import org.apache.chemistry.opencmis.commons.definitions.PropertyIdDefinition;
070import org.apache.chemistry.opencmis.commons.definitions.PropertyIntegerDefinition;
071import org.apache.chemistry.opencmis.commons.definitions.PropertyStringDefinition;
072import org.apache.chemistry.opencmis.commons.definitions.PropertyUriDefinition;
073import org.apache.chemistry.opencmis.commons.definitions.RelationshipTypeDefinition;
074import org.apache.chemistry.opencmis.commons.definitions.SecondaryTypeDefinition;
075import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
076import org.apache.chemistry.opencmis.commons.enums.Cardinality;
077import org.apache.chemistry.opencmis.commons.enums.Updatability;
078import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
079import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
080import org.apache.chemistry.opencmis.commons.impl.dataobjects.BindingsObjectFactoryImpl;
081import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
082import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
083import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
084import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoObjectData;
085
086/**
087 * Factory for {@link NuxeoObject} and its related classes.
088 */
089public class NuxeoObjectFactory implements ObjectFactory {
090
091    private final NuxeoSession session;
092
093    private static final BindingsObjectFactory of = new BindingsObjectFactoryImpl();
094
095    public NuxeoObjectFactory(NuxeoSession session) {
096        this.session = session;
097    }
098
099    @Override
100    public void initialize(Session session, Map<String, String> parameters) {
101        // TODO
102        throw new UnsupportedOperationException();
103    }
104
105    @Override
106    public RepositoryInfo convertRepositoryInfo(RepositoryInfo repositoryInfo) {
107        return repositoryInfo;
108    }
109
110    @Override
111    public NuxeoObject convertObject(ObjectData data, OperationContext context) {
112        if (data == null || data.getProperties() == null || data.getProperties().getProperties() == null) {
113            return null;
114        }
115        // object type
116        PropertyData<?> propData = data.getProperties().getProperties().get(PropertyIds.OBJECT_TYPE_ID);
117        ObjectType type = session.getTypeDefinition((String) propData.getFirstValue());
118        // secondary types
119        propData = data.getProperties().getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS);
120        List<SecondaryType> secondaryTypes;
121        if (propData == null) {
122            secondaryTypes = Collections.emptyList();
123        } else {
124            @SuppressWarnings("unchecked")
125            List<String> sts = (List<String>) propData.getValues();
126            secondaryTypes = new ArrayList<>(sts.size());
127            for (String st : sts) {
128                secondaryTypes.add((SecondaryType) session.getTypeDefinition(st));
129            }
130        }
131        return NuxeoObject.construct(session, (NuxeoObjectData) data, type, secondaryTypes);
132    }
133
134    @Override
135    public ObjectType getTypeFromObjectData(ObjectData objectData) {
136        // TODO Auto-generated method stub
137        throw new UnsupportedOperationException();
138    }
139
140    @Override
141    public Ace createAce(String principal, List<String> permissions) {
142        // TODO Auto-generated method stub
143        throw new UnsupportedOperationException();
144    }
145
146    @Override
147    public Acl createAcl(List<Ace> aces) {
148        // TODO Auto-generated method stub
149        throw new UnsupportedOperationException();
150    }
151
152    @Override
153    public <T> Property<T> createProperty(PropertyDefinition<T> type, List<T> values) {
154        return new PropertyImpl<>(type, values);
155    }
156
157    @Override
158    public ContentStream createContentStream(String filename, long length, String mimetype, InputStream stream) {
159        return new ContentStreamImpl(filename, BigInteger.valueOf(length), mimetype, stream);
160    }
161
162    @Override
163    public ContentStream createContentStream(String filename, long length, String mimetype, InputStream stream,
164            boolean partial) {
165        // TODO partial
166        return createContentStream(filename, length, mimetype, stream);
167    }
168
169    @Override
170    public Acl convertAces(List<Ace> aces) {
171        return aces == null ? null : new AccessControlListImpl(aces);
172    }
173
174    @Override
175    public ContentStream convertContentStream(ContentStream contentStream) {
176        if (contentStream == null) {
177            return null;
178        }
179        long len = contentStream.getLength();
180        BigInteger length = len < 0 ? null : BigInteger.valueOf(len);
181        return of.createContentStream(contentStream.getFileName(), length, contentStream.getMimeType(),
182                contentStream.getStream());
183    }
184
185    @Override
186    public List<String> convertPolicies(List<Policy> policies) {
187        if (policies == null) {
188            return null;
189        }
190        List<String> policyIds = new ArrayList<>(policies.size());
191        for (Policy p : policies) {
192            policyIds.add(p.getId());
193        }
194        return policyIds;
195    }
196
197    @Override
198    public Map<String, Property<?>> convertProperties(ObjectType objectType, Collection<SecondaryType> secondaryTypes,
199            Properties properties) {
200        throw new UnsupportedOperationException();
201    }
202
203    @Override
204    public Properties convertProperties(Map<String, ?> properties, ObjectType type,
205            Collection<SecondaryType> secondaryTypes, Set<Updatability> updatabilityFilter) {
206        if (properties == null) {
207            return null;
208        }
209        // TODO secondaryTypes
210        // TODO updatabilityFilter
211        PropertiesImpl props = new PropertiesImpl();
212        for (Entry<String, ?> es : properties.entrySet()) {
213            PropertyData<?> prop = convertProperty(es.getKey(), es.getValue(), type);
214            props.addProperty(prop);
215        }
216        return props;
217    }
218
219    @SuppressWarnings("unchecked")
220    protected static PropertyData<?> convertProperty(String key, Object value, ObjectType type) {
221        PropertyDefinition<?> pd = type.getPropertyDefinitions().get(key);
222        if (pd == null) {
223            throw new IllegalArgumentException("Unknown property '" + key + "' for type: " + type.getId());
224        }
225        boolean single = pd.getCardinality() == Cardinality.SINGLE;
226
227        List<?> values;
228        if (value == null) {
229            values = null;
230        } else if (value instanceof List<?>) {
231            if (single) {
232                throw new IllegalArgumentException("Property '" + key + "' is not a multi value property!");
233            }
234            values = (List<?>) value;
235        } else {
236            if (!single) {
237                throw new IllegalArgumentException("Property '" + key + "' is not a single value property!");
238            }
239            values = Collections.singletonList(value);
240        }
241        Object firstValue = values == null ? null : values.get(0);
242
243        if (pd instanceof PropertyStringDefinition) {
244            return of.createPropertyStringData(key, (List<String>) values);
245        } else if (pd instanceof PropertyIdDefinition) {
246            return of.createPropertyIdData(key, (List<String>) values);
247        } else if (pd instanceof PropertyHtmlDefinition) {
248            return of.createPropertyHtmlData(key, (List<String>) values);
249        } else if (pd instanceof PropertyUriDefinition) {
250            return of.createPropertyUriData(key, (List<String>) values);
251        } else if (pd instanceof PropertyIntegerDefinition) {
252            if (firstValue == null) {
253                return of.createPropertyIntegerData(key, (List<BigInteger>) null);
254            } else if (firstValue instanceof BigInteger) {
255                return of.createPropertyIntegerData(key, (List<BigInteger>) values);
256            } else if ((firstValue instanceof Byte) || (firstValue instanceof Short) || (firstValue instanceof Integer)
257                    || (firstValue instanceof Long)) {
258                List<BigInteger> list = new ArrayList<>(values.size());
259                for (Object v : values) {
260                    list.add(BigInteger.valueOf(((Number) v).longValue()));
261                }
262                return of.createPropertyIntegerData(key, list);
263            } else {
264                throw new IllegalArgumentException("Property '" + key + "' is an Integer property");
265            }
266        } else if (pd instanceof PropertyBooleanDefinition) {
267            return of.createPropertyBooleanData(key, (List<Boolean>) values);
268        } else if (pd instanceof PropertyDecimalDefinition) {
269            return of.createPropertyDecimalData(key, (List<BigDecimal>) values);
270        } else if (pd instanceof PropertyDateTimeDefinition) {
271            return of.createPropertyDateTimeData(key, (List<GregorianCalendar>) values);
272        }
273        throw new CmisRuntimeException("Unknown class: " + pd.getClass().getName());
274    }
275
276    @Override
277    public List<PropertyData<?>> convertQueryProperties(Properties properties) {
278        if (properties == null || properties.getProperties() == null) {
279            return null;
280        }
281        return new ArrayList<>(properties.getPropertyList());
282    }
283
284    @Override
285    public QueryResult convertQueryResult(ObjectData objectData) {
286        throw new UnsupportedOperationException();
287    }
288
289    @Override
290    public Rendition convertRendition(String objectId, RenditionData rendition) {
291        if (rendition == null) {
292            return null;
293        }
294        BigInteger rl = rendition.getBigLength();
295        BigInteger rh = rendition.getBigHeight();
296        BigInteger rw = rendition.getBigWidth();
297        long length = rl == null ? -1 : rl.longValue();
298        int height = rh == null ? -1 : rh.intValue();
299        int width = rw == null ? -1 : rw.intValue();
300        return new RenditionImpl(session, objectId, rendition.getStreamId(), rendition.getRenditionDocumentId(),
301                rendition.getKind(), length, rendition.getMimeType(), rendition.getTitle(), height, width);
302    }
303
304    @Override
305    public ObjectType convertTypeDefinition(TypeDefinition typeDefinition) {
306        if (typeDefinition instanceof DocumentTypeDefinition) {
307            return new DocumentTypeImpl(session, (DocumentTypeDefinition) typeDefinition);
308        } else if (typeDefinition instanceof FolderTypeDefinition) {
309            return new FolderTypeImpl(session, (FolderTypeDefinition) typeDefinition);
310        } else if (typeDefinition instanceof RelationshipTypeDefinition) {
311            return new RelationshipTypeImpl(session, (RelationshipTypeDefinition) typeDefinition);
312        } else if (typeDefinition instanceof PolicyTypeDefinition) {
313            return new PolicyTypeImpl(session, (PolicyTypeDefinition) typeDefinition);
314        } else if (typeDefinition instanceof SecondaryTypeDefinition) {
315            return new SecondaryTypeImpl(session, (SecondaryTypeDefinition) typeDefinition);
316        }
317        throw new CmisRuntimeException("Unknown base class: " + typeDefinition.getClass().getName());
318    }
319
320    @Override
321    public ChangeEvent convertChangeEvent(ObjectData objectData) {
322        // TODO Auto-generated method stub
323        throw new UnsupportedOperationException();
324    }
325
326    @Override
327    public ChangeEvents convertChangeEvents(String changeLogToken, ObjectList objectList) {
328        // TODO Auto-generated method stub
329        throw new UnsupportedOperationException();
330    }
331
332}