This project has retired. For details please refer to its Attic page.
ObjectFactoryImpl xref
View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.client.runtime.repository;
20  
21  import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNotEmpty;
22  import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNullOrEmpty;
23  
24  import java.io.InputStream;
25  import java.io.Serializable;
26  import java.math.BigDecimal;
27  import java.math.BigInteger;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Date;
32  import java.util.GregorianCalendar;
33  import java.util.HashMap;
34  import java.util.LinkedHashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  
39  import org.apache.chemistry.opencmis.client.api.ChangeEvent;
40  import org.apache.chemistry.opencmis.client.api.ChangeEvents;
41  import org.apache.chemistry.opencmis.client.api.CmisObject;
42  import org.apache.chemistry.opencmis.client.api.ObjectFactory;
43  import org.apache.chemistry.opencmis.client.api.ObjectType;
44  import org.apache.chemistry.opencmis.client.api.OperationContext;
45  import org.apache.chemistry.opencmis.client.api.Policy;
46  import org.apache.chemistry.opencmis.client.api.Property;
47  import org.apache.chemistry.opencmis.client.api.QueryResult;
48  import org.apache.chemistry.opencmis.client.api.Rendition;
49  import org.apache.chemistry.opencmis.client.api.SecondaryType;
50  import org.apache.chemistry.opencmis.client.api.Session;
51  import org.apache.chemistry.opencmis.client.runtime.ChangeEventImpl;
52  import org.apache.chemistry.opencmis.client.runtime.ChangeEventsImpl;
53  import org.apache.chemistry.opencmis.client.runtime.DocumentImpl;
54  import org.apache.chemistry.opencmis.client.runtime.FolderImpl;
55  import org.apache.chemistry.opencmis.client.runtime.ItemImpl;
56  import org.apache.chemistry.opencmis.client.runtime.PolicyImpl;
57  import org.apache.chemistry.opencmis.client.runtime.PropertyImpl;
58  import org.apache.chemistry.opencmis.client.runtime.QueryResultImpl;
59  import org.apache.chemistry.opencmis.client.runtime.RelationshipImpl;
60  import org.apache.chemistry.opencmis.client.runtime.RenditionImpl;
61  import org.apache.chemistry.opencmis.client.runtime.SessionImpl;
62  import org.apache.chemistry.opencmis.client.runtime.objecttype.DocumentTypeImpl;
63  import org.apache.chemistry.opencmis.client.runtime.objecttype.FolderTypeImpl;
64  import org.apache.chemistry.opencmis.client.runtime.objecttype.ItemTypeImpl;
65  import org.apache.chemistry.opencmis.client.runtime.objecttype.PolicyTypeImpl;
66  import org.apache.chemistry.opencmis.client.runtime.objecttype.RelationshipTypeImpl;
67  import org.apache.chemistry.opencmis.client.runtime.objecttype.SecondaryTypeImpl;
68  import org.apache.chemistry.opencmis.commons.PropertyIds;
69  import org.apache.chemistry.opencmis.commons.data.Ace;
70  import org.apache.chemistry.opencmis.commons.data.Acl;
71  import org.apache.chemistry.opencmis.commons.data.ContentStream;
72  import org.apache.chemistry.opencmis.commons.data.ObjectData;
73  import org.apache.chemistry.opencmis.commons.data.ObjectList;
74  import org.apache.chemistry.opencmis.commons.data.Properties;
75  import org.apache.chemistry.opencmis.commons.data.PropertyData;
76  import org.apache.chemistry.opencmis.commons.data.PropertyId;
77  import org.apache.chemistry.opencmis.commons.data.RenditionData;
78  import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
79  import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
80  import org.apache.chemistry.opencmis.commons.definitions.FolderTypeDefinition;
81  import org.apache.chemistry.opencmis.commons.definitions.ItemTypeDefinition;
82  import org.apache.chemistry.opencmis.commons.definitions.PolicyTypeDefinition;
83  import org.apache.chemistry.opencmis.commons.definitions.PropertyBooleanDefinition;
84  import org.apache.chemistry.opencmis.commons.definitions.PropertyDateTimeDefinition;
85  import org.apache.chemistry.opencmis.commons.definitions.PropertyDecimalDefinition;
86  import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
87  import org.apache.chemistry.opencmis.commons.definitions.PropertyHtmlDefinition;
88  import org.apache.chemistry.opencmis.commons.definitions.PropertyIdDefinition;
89  import org.apache.chemistry.opencmis.commons.definitions.PropertyIntegerDefinition;
90  import org.apache.chemistry.opencmis.commons.definitions.PropertyStringDefinition;
91  import org.apache.chemistry.opencmis.commons.definitions.PropertyUriDefinition;
92  import org.apache.chemistry.opencmis.commons.definitions.RelationshipTypeDefinition;
93  import org.apache.chemistry.opencmis.commons.definitions.SecondaryTypeDefinition;
94  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
95  import org.apache.chemistry.opencmis.commons.enums.Cardinality;
96  import org.apache.chemistry.opencmis.commons.enums.ChangeType;
97  import org.apache.chemistry.opencmis.commons.enums.Updatability;
98  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
99  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
100 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PartialContentStreamImpl;
101 import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
102 
103 /**
104  * Persistent model object factory.
105  */
106 public class ObjectFactoryImpl implements ObjectFactory, Serializable {
107 
108     private static final long serialVersionUID = 1L;
109 
110     private Session session;
111 
112     /**
113      * Default constructor.
114      */
115     public ObjectFactoryImpl() {
116     }
117 
118     @Override
119     public void initialize(Session session, Map<String, String> parameters) {
120         assert session != null;
121 
122         this.session = session;
123     }
124 
125     /**
126      * Returns the bindings object factory.
127      */
128     protected BindingsObjectFactory getBindingsObjectFactory() {
129         return session.getBinding().getObjectFactory();
130     }
131 
132     // repository info
133 
134     @Override
135     public RepositoryInfo convertRepositoryInfo(RepositoryInfo repositoryInfo) {
136         return repositoryInfo;
137     }
138 
139     // ACL and ACE
140 
141     @Override
142     public Acl convertAces(List<Ace> aces) {
143         if (aces == null) {
144             return null;
145         }
146 
147         BindingsObjectFactory bof = getBindingsObjectFactory();
148 
149         List<Ace> bindingAces = new ArrayList<Ace>();
150         for (Ace ace : aces) {
151             bindingAces.add(bof.createAccessControlEntry(ace.getPrincipalId(), ace.getPermissions()));
152         }
153 
154         return bof.createAccessControlList(bindingAces);
155     }
156 
157     @Override
158     public Ace createAce(String principal, List<String> permissions) {
159         BindingsObjectFactory bof = getBindingsObjectFactory();
160 
161         Ace ace = bof.createAccessControlEntry(principal, permissions);
162 
163         return ace;
164     }
165 
166     @Override
167     public Acl createAcl(List<Ace> aces) {
168         BindingsObjectFactory bof = getBindingsObjectFactory();
169 
170         Acl acl = bof.createAccessControlList(aces);
171 
172         return acl;
173     }
174 
175     // policies
176 
177     @Override
178     public List<String> convertPolicies(List<Policy> policies) {
179         if (policies == null) {
180             return null;
181         }
182 
183         List<String> result = new ArrayList<String>();
184 
185         for (Policy policy : policies) {
186             if ((policy != null) && (policy.getId() != null)) {
187                 result.add(policy.getId());
188             }
189         }
190 
191         return result;
192     }
193 
194     // renditions
195 
196     @Override
197     public Rendition convertRendition(String objectId, RenditionData rendition) {
198         if (rendition == null) {
199             throw new IllegalArgumentException("Rendition must be set!");
200         }
201 
202         long length = (rendition.getBigLength() == null ? -1 : rendition.getBigLength().longValue());
203         int height = (rendition.getBigHeight() == null ? -1 : rendition.getBigHeight().intValue());
204         int width = (rendition.getBigWidth() == null ? -1 : rendition.getBigWidth().intValue());
205 
206         return new RenditionImpl(this.session, objectId, rendition.getStreamId(), rendition.getRenditionDocumentId(),
207                 rendition.getKind(), length, rendition.getMimeType(), rendition.getTitle(), height, width);
208     }
209 
210     // content stream
211 
212     @Override
213     public ContentStream createContentStream(String filename, long length, String mimetype, InputStream stream) {
214         return createContentStream(filename, length, mimetype, stream, false);
215     }
216 
217     @Override
218     public ContentStream createContentStream(String filename, long length, String mimetype, InputStream stream,
219             boolean partial) {
220         if (partial) {
221             return new PartialContentStreamImpl(filename, (length < 0 ? null : BigInteger.valueOf(length)), mimetype,
222                     stream);
223         } else {
224             return new ContentStreamImpl(filename, (length < 0 ? null : BigInteger.valueOf(length)), mimetype, stream);
225         }
226     }
227 
228     @Override
229     public ContentStream convertContentStream(ContentStream contentStream) {
230         if (contentStream == null) {
231             return null;
232         }
233 
234         BigInteger length = (contentStream.getLength() < 0 ? null : BigInteger.valueOf(contentStream.getLength()));
235 
236         return getBindingsObjectFactory().createContentStream(contentStream.getFileName(), length,
237                 contentStream.getMimeType(), contentStream.getStream());
238     }
239 
240     // types
241 
242     @Override
243     public ObjectType convertTypeDefinition(TypeDefinition typeDefinition) {
244         if (typeDefinition instanceof DocumentTypeDefinition) {
245             return new DocumentTypeImpl(this.session, (DocumentTypeDefinition) typeDefinition);
246         } else if (typeDefinition instanceof FolderTypeDefinition) {
247             return new FolderTypeImpl(this.session, (FolderTypeDefinition) typeDefinition);
248         } else if (typeDefinition instanceof RelationshipTypeDefinition) {
249             return new RelationshipTypeImpl(this.session, (RelationshipTypeDefinition) typeDefinition);
250         } else if (typeDefinition instanceof PolicyTypeDefinition) {
251             return new PolicyTypeImpl(this.session, (PolicyTypeDefinition) typeDefinition);
252         } else if (typeDefinition instanceof ItemTypeDefinition) {
253             return new ItemTypeImpl(this.session, (ItemTypeDefinition) typeDefinition);
254         } else if (typeDefinition instanceof SecondaryTypeDefinition) {
255             return new SecondaryTypeImpl(this.session, (SecondaryTypeDefinition) typeDefinition);
256         } else if (typeDefinition == null) {
257             throw new CmisRuntimeException("No base type supplied!");
258         } else {
259             throw new CmisRuntimeException("Unknown base type! Received " + typeDefinition.getClass().getName());
260         }
261     }
262 
263     @Override
264     public ObjectType getTypeFromObjectData(ObjectData objectData) {
265         if (objectData == null || objectData.getProperties() == null
266                 || objectData.getProperties().getProperties() == null) {
267             return null;
268         }
269 
270         PropertyData<?> typeProperty = objectData.getProperties().getProperties().get(PropertyIds.OBJECT_TYPE_ID);
271         if (!(typeProperty instanceof PropertyId)) {
272             return null;
273         }
274 
275         return this.session.getTypeDefinition((String) typeProperty.getFirstValue());
276     }
277 
278     // properties
279 
280     @Override
281     public <T> Property<T> createProperty(PropertyDefinition<T> type, List<T> values) {
282         return new PropertyImpl<T>(type, values);
283     }
284 
285     @SuppressWarnings("unchecked")
286     protected <T> Property<T> convertProperty(ObjectType objectType, Collection<SecondaryType> secondaryTypes,
287             PropertyData<T> pd) {
288 
289         // handle invalid property IDs
290         if (pd.getId() == null || pd.getId().length() == 0) {
291             StringBuilder sb = null;
292             if (isNotEmpty(secondaryTypes)) {
293                 sb = new StringBuilder(128);
294                 sb.append(" or a secondary type of the object (");
295                 addSecondaryTypeIds(secondaryTypes, sb);
296                 sb.append(')');
297             }
298 
299             throw new CmisRuntimeException(
300                     "Cannot convert a property because it has no ID! The property is supposed to be part of the type '"
301                             + objectType.getId() + "'" + (sb == null ? "" : sb.toString())
302                             + ". The value of this property is: " + pd.getValues());
303         }
304 
305         PropertyDefinition<T> definition = (PropertyDefinition<T>) objectType.getPropertyDefinitions().get(pd.getId());
306 
307         // search secondary types
308         if (definition == null && secondaryTypes != null) {
309             for (SecondaryType secondaryType : secondaryTypes) {
310                 if (secondaryType != null && secondaryType.getPropertyDefinitions() != null) {
311                     definition = (PropertyDefinition<T>) secondaryType.getPropertyDefinitions().get(pd.getId());
312                     if (definition != null) {
313                         break;
314                     }
315                 }
316             }
317         }
318 
319         // the type might have changed -> reload type definitions
320         if (definition == null) {
321             TypeDefinition reloadedObjectType = session.getTypeDefinition(objectType.getId(), false);
322             definition = (PropertyDefinition<T>) reloadedObjectType.getPropertyDefinitions().get(pd.getId());
323 
324             if (definition == null && secondaryTypes != null) {
325                 for (SecondaryType secondaryType : secondaryTypes) {
326                     if (secondaryType != null) {
327                         TypeDefinition reloadedSecondaryType = session.getTypeDefinition(secondaryType.getId(), false);
328                         if (reloadedSecondaryType.getPropertyDefinitions() != null) {
329                             definition = (PropertyDefinition<T>) reloadedSecondaryType.getPropertyDefinitions().get(
330                                     pd.getId());
331                             if (definition != null) {
332                                 break;
333                             }
334                         }
335                     }
336                 }
337             }
338         }
339 
340         if (definition == null) {
341             // property without definition
342 
343             StringBuilder sb = null;
344             if (isNotEmpty(secondaryTypes)) {
345                 sb = new StringBuilder(128);
346                 sb.append(" or any secondary type of the object (");
347                 addSecondaryTypeIds(secondaryTypes, sb);
348                 sb.append(')');
349             }
350 
351             throw new CmisRuntimeException(
352                     "Cannot convert property '"
353                             + pd.getId()
354                             + "' because it does not exist in the object type. The property is supposed to be part of the type '"
355                             + objectType.getId() + "'" + (sb == null ? "" : sb.toString())
356                             + ". The value of this property is: " + pd.getValues());
357         }
358 
359         return createProperty(definition, pd.getValues());
360     }
361 
362     private void addSecondaryTypeIds(Collection<SecondaryType> secondaryTypes, StringBuilder sb) {
363         boolean first = true;
364         for (SecondaryType secondaryType : secondaryTypes) {
365             if (first) {
366                 first = false;
367             } else {
368                 sb.append(", ");
369             }
370 
371             sb.append('\'');
372             sb.append(secondaryType.getId());
373             sb.append('\'');
374         }
375     }
376 
377     @Override
378     public Map<String, Property<?>> convertProperties(ObjectType objectType, Collection<SecondaryType> secondaryTypes,
379             Properties properties) {
380         // check input
381         if (objectType == null) {
382             throw new IllegalArgumentException("Object type must set!");
383         }
384 
385         if (objectType.getPropertyDefinitions() == null) {
386             throw new IllegalArgumentException("Object type has no property defintions!");
387         }
388 
389         if (properties == null || properties.getProperties() == null) {
390             throw new IllegalArgumentException("Properties must be set!");
391         }
392 
393         // iterate through properties and convert them
394         Map<String, Property<?>> result = new LinkedHashMap<String, Property<?>>();
395         for (Map.Entry<String, PropertyData<?>> entry : properties.getProperties().entrySet()) {
396             // find property definition
397             Property<?> apiProperty = convertProperty(objectType, secondaryTypes, entry.getValue());
398             result.put(entry.getKey(), apiProperty);
399         }
400 
401         return result;
402     }
403 
404     @Override
405     @SuppressWarnings({ "unchecked", "rawtypes" })
406     public Properties convertProperties(Map<String, ?> properties, ObjectType type,
407             Collection<SecondaryType> secondaryTypes, Set<Updatability> updatabilityFilter) {
408         // check input
409         if (properties == null) {
410             return null;
411         }
412 
413         // get the type
414         if (type == null) {
415             Object typeId = properties.get(PropertyIds.OBJECT_TYPE_ID);
416 
417             if (typeId instanceof String) {
418                 type = session.getTypeDefinition(typeId.toString());
419             } else if (typeId instanceof List && !((List) typeId).isEmpty() && ((List) typeId).get(0) instanceof String) {
420                 type = session.getTypeDefinition(((List) typeId).get(0).toString());
421             } else {
422                 throw new IllegalArgumentException("Type or type property must be set!");
423             }
424         }
425 
426         // get secondary types
427         Collection<SecondaryType> allSecondaryTypes = null;
428         Object secondaryTypeIds = properties.get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS);
429         if (secondaryTypeIds instanceof List) {
430             allSecondaryTypes = new ArrayList<SecondaryType>();
431 
432             for (Object secondaryTypeId : (List<?>) secondaryTypeIds) {
433                 if (!(secondaryTypeId instanceof String)) {
434                     throw new IllegalArgumentException("Secondary types property contains an invalid entry: "
435                             + secondaryTypeId);
436                 }
437 
438                 ObjectType secondaryType = session.getTypeDefinition(secondaryTypeId.toString());
439                 if (!(secondaryType instanceof SecondaryType)) {
440                     throw new IllegalArgumentException(
441                             "Secondary types property contains a type that is not a secondary type: " + secondaryTypeId);
442                 }
443 
444                 allSecondaryTypes.add((SecondaryType) secondaryType);
445             }
446         }
447 
448         if (secondaryTypes != null && allSecondaryTypes == null) {
449             allSecondaryTypes = secondaryTypes;
450         }
451 
452         // some preparation
453         BindingsObjectFactory bof = getBindingsObjectFactory();
454         List<PropertyData<?>> propertyList = new ArrayList<PropertyData<?>>();
455 
456         // the big loop
457         for (Map.Entry<String, ?> property : properties.entrySet()) {
458             if ((property == null) || (property.getKey() == null)) {
459                 continue;
460             }
461 
462             String id = property.getKey();
463             Object value = property.getValue();
464 
465             if (value instanceof Property<?>) {
466                 Property<?> p = (Property<?>) value;
467                 if (!id.equals(p.getId())) {
468                     throw new IllegalArgumentException("Property id mismatch: '" + id + "' != '" + p.getId() + "'!");
469                 }
470                 value = (p.getDefinition().getCardinality() == Cardinality.SINGLE ? p.getFirstValue() : p.getValues());
471             }
472 
473             // get the property definition
474             PropertyDefinition<?> definition = type.getPropertyDefinitions().get(id);
475 
476             if (definition == null && allSecondaryTypes != null) {
477                 for (SecondaryType secondaryType : allSecondaryTypes) {
478                     if (secondaryType != null && secondaryType.getPropertyDefinitions() != null) {
479                         definition = secondaryType.getPropertyDefinitions().get(id);
480                         if (definition != null) {
481                             break;
482                         }
483                     }
484                 }
485             }
486 
487             if (definition == null) {
488                 throw new IllegalArgumentException("Property '" + id
489                         + "' is not valid for this type or one of the secondary types!");
490             }
491 
492             // check updatability
493             if (updatabilityFilter != null) {
494                 if (!updatabilityFilter.contains(definition.getUpdatability())) {
495                     continue;
496                 }
497             }
498 
499             // single and multi value check
500             List<?> values;
501             if (value == null) {
502                 values = null;
503             } else if (value instanceof List<?>) {
504                 if (definition.getCardinality() != Cardinality.MULTI) {
505                     throw new IllegalArgumentException("Property '" + id + "' is not a multi value property!");
506                 }
507                 values = (List<?>) value;
508 
509                 // check if the list is homogeneous and does not contain null
510                 // values
511                 Class<?> valueClazz = null;
512                 for (Object o : values) {
513                     if (o == null) {
514                         throw new IllegalArgumentException("Property '" + id + "' contains null values!");
515                     }
516                     if (valueClazz == null) {
517                         valueClazz = o.getClass();
518                     } else {
519                         if (!valueClazz.isInstance(o)) {
520                             throw new IllegalArgumentException("Property '" + id + "' is inhomogeneous!");
521                         }
522                     }
523                 }
524             } else {
525                 if (definition.getCardinality() != Cardinality.SINGLE) {
526                     throw new IllegalArgumentException("Property '" + id + "' is not a single value property!");
527                 }
528                 values = Collections.singletonList(value);
529             }
530 
531             // assemble property
532             PropertyData<?> propertyData = null;
533             Object firstValue = (isNullOrEmpty(values) ? null : values.get(0));
534 
535             if (definition instanceof PropertyStringDefinition) {
536                 if (firstValue == null) {
537                     propertyData = bof.createPropertyStringData(id, (List<String>) null);
538                 } else if (firstValue instanceof String) {
539                     propertyData = bof.createPropertyStringData(id, (List<String>) values);
540                 } else {
541                     throwWrongTypeError(firstValue, "string", String.class, id);
542                 }
543             } else if (definition instanceof PropertyIdDefinition) {
544                 if (firstValue == null) {
545                     propertyData = bof.createPropertyIdData(id, (List<String>) null);
546                 } else if (firstValue instanceof String) {
547                     propertyData = bof.createPropertyIdData(id, (List<String>) values);
548                 } else {
549                     throwWrongTypeError(firstValue, "string", String.class, id);
550                 }
551             } else if (definition instanceof PropertyHtmlDefinition) {
552                 if (firstValue == null) {
553                     propertyData = bof.createPropertyHtmlData(id, (List<String>) values);
554                 } else if (firstValue instanceof String) {
555                     propertyData = bof.createPropertyHtmlData(id, (List<String>) values);
556                 } else {
557                     throwWrongTypeError(firstValue, "html", String.class, id);
558                 }
559             } else if (definition instanceof PropertyUriDefinition) {
560                 if (firstValue == null) {
561                     propertyData = bof.createPropertyUriData(id, (List<String>) null);
562                 } else if (firstValue instanceof String) {
563                     propertyData = bof.createPropertyUriData(id, (List<String>) values);
564                 } else {
565                     throwWrongTypeError(firstValue, "uri", String.class, id);
566                 }
567             } else if (definition instanceof PropertyIntegerDefinition) {
568                 if (firstValue == null) {
569                     propertyData = bof.createPropertyIntegerData(id, (List<BigInteger>) null);
570                 } else if (firstValue instanceof BigInteger) {
571                     propertyData = bof.createPropertyIntegerData(id, (List<BigInteger>) values);
572                 } else if ((firstValue instanceof Byte) || (firstValue instanceof Short)
573                         || (firstValue instanceof Integer) || (firstValue instanceof Long)) {
574                     // we accept all kinds of integers
575                     List<BigInteger> list = new ArrayList<BigInteger>(values.size());
576                     for (Object v : values) {
577                         list.add(BigInteger.valueOf(((Number) v).longValue()));
578                     }
579 
580                     propertyData = bof.createPropertyIntegerData(id, list);
581                 } else {
582                     throwWrongTypeError(firstValue, "integer", BigInteger.class, id);
583                 }
584             } else if (definition instanceof PropertyBooleanDefinition) {
585                 if (firstValue == null) {
586                     propertyData = bof.createPropertyBooleanData(id, (List<Boolean>) null);
587                 } else if (firstValue instanceof Boolean) {
588                     propertyData = bof.createPropertyBooleanData(id, (List<Boolean>) values);
589                 } else {
590                     throwWrongTypeError(firstValue, "boolean", Boolean.class, id);
591                 }
592             } else if (definition instanceof PropertyDecimalDefinition) {
593                 if (firstValue == null) {
594                     propertyData = bof.createPropertyDecimalData(id, (List<BigDecimal>) null);
595                 } else if (firstValue instanceof BigDecimal) {
596                     propertyData = bof.createPropertyDecimalData(id, (List<BigDecimal>) values);
597                 } else if ((firstValue instanceof Float) || (firstValue instanceof Double)
598                         || (firstValue instanceof Byte) || (firstValue instanceof Short)
599                         || (firstValue instanceof Integer) || (firstValue instanceof Long)) {
600                     // we accept all kinds of integers
601                     // as well as floats and doubles
602                     List<BigDecimal> list = new ArrayList<BigDecimal>(values.size());
603                     for (Object v : values) {
604                         list.add(new BigDecimal(v.toString()));
605                     }
606 
607                     propertyData = bof.createPropertyDecimalData(id, list);
608                 } else {
609                     throwWrongTypeError(firstValue, "decimal", BigDecimal.class, id);
610                 }
611             } else if (definition instanceof PropertyDateTimeDefinition) {
612                 if (firstValue == null) {
613                     propertyData = bof.createPropertyDateTimeData(id, (List<GregorianCalendar>) null);
614                 } else if (firstValue instanceof GregorianCalendar) {
615                     propertyData = bof.createPropertyDateTimeData(id, (List<GregorianCalendar>) values);
616                 } else if (firstValue instanceof Date) {
617                     List<GregorianCalendar> list = new ArrayList<GregorianCalendar>(values.size());
618                     for (Object d : values) {
619                         GregorianCalendar cal = new GregorianCalendar();
620                         cal.setTime((Date) d);
621                         list.add(cal);
622                     }
623                     propertyData = bof.createPropertyDateTimeData(id, list);
624                 } else {
625                     throwWrongTypeError(firstValue, "datetime", GregorianCalendar.class, id);
626                 }
627             }
628 
629             // do we have something?
630             if (propertyData == null) {
631                 throw new IllegalArgumentException("Property '" + id + "' doesn't match the property defintion!");
632             }
633 
634             propertyList.add(propertyData);
635         }
636 
637         return bof.createPropertiesData(propertyList);
638     }
639 
640     @Override
641     public List<PropertyData<?>> convertQueryProperties(Properties properties) {
642         // check input
643         if ((properties == null) || (properties.getProperties() == null)) {
644             throw new IllegalArgumentException("Properties must be set!");
645         }
646         return new ArrayList<PropertyData<?>>(properties.getPropertyList());
647     }
648 
649     // objects
650 
651     @Override
652     public CmisObject convertObject(ObjectData objectData, OperationContext context) {
653         if (objectData == null) {
654             throw new IllegalArgumentException("Object data is null!");
655         }
656 
657         if (objectData.getId() == null) {
658             throw new IllegalArgumentException("Object ID property not set!");
659         }
660 
661         if (objectData.getBaseTypeId() == null) {
662             throw new IllegalArgumentException("Base type ID property not set!");
663         }
664 
665         ObjectType type = getTypeFromObjectData(objectData);
666 
667         /* determine type */
668         switch (objectData.getBaseTypeId()) {
669         case CMIS_DOCUMENT:
670             return new DocumentImpl((SessionImpl) session, type, objectData, context);
671         case CMIS_FOLDER:
672             return new FolderImpl((SessionImpl) session, type, objectData, context);
673         case CMIS_POLICY:
674             return new PolicyImpl((SessionImpl) session, type, objectData, context);
675         case CMIS_RELATIONSHIP:
676             return new RelationshipImpl((SessionImpl) session, type, objectData, context);
677         case CMIS_ITEM:
678             return new ItemImpl((SessionImpl) session, type, objectData, context);
679         case CMIS_SECONDARY:
680             throw new CmisRuntimeException("Secondary type is used as object type: " + objectData.getBaseTypeId());
681         default:
682             throw new CmisRuntimeException("Unsupported base type: " + objectData.getBaseTypeId());
683         }
684     }
685 
686     @Override
687     public QueryResult convertQueryResult(ObjectData objectData) {
688         if (objectData == null) {
689             throw new IllegalArgumentException("Object data is null!");
690         }
691 
692         return new QueryResultImpl(session, objectData);
693     }
694 
695     @Override
696     public ChangeEvent convertChangeEvent(ObjectData objectData) {
697         ChangeType changeType = null;
698         GregorianCalendar changeTime = null;
699         String objectId = null;
700         Map<String, List<?>> properties = null;
701         List<String> policyIds = null;
702         Acl acl = null;
703 
704         if (objectData.getChangeEventInfo() != null) {
705             changeType = objectData.getChangeEventInfo().getChangeType();
706             changeTime = objectData.getChangeEventInfo().getChangeTime();
707         }
708 
709         if ((objectData.getProperties() != null) && (objectData.getProperties().getPropertyList() != null)) {
710             properties = new HashMap<String, List<?>>();
711 
712             for (PropertyData<?> property : objectData.getProperties().getPropertyList()) {
713                 properties.put(property.getId(), property.getValues());
714             }
715 
716             if (properties.containsKey(PropertyIds.OBJECT_ID)) {
717                 List<?> objectIdList = properties.get(PropertyIds.OBJECT_ID);
718                 if (isNotEmpty(objectIdList)) {
719                     objectId = objectIdList.get(0).toString();
720                 }
721             }
722 
723             if ((objectData.getPolicyIds() != null) && (objectData.getPolicyIds().getPolicyIds() != null)) {
724                 policyIds = objectData.getPolicyIds().getPolicyIds();
725             }
726 
727             if (objectData.getAcl() != null) {
728                 acl = objectData.getAcl();
729             }
730         }
731 
732         return new ChangeEventImpl(changeType, changeTime, objectId, properties, policyIds, acl);
733     }
734 
735     @Override
736     public ChangeEvents convertChangeEvents(String changeLogToken, ObjectList objectList) {
737         if (objectList == null) {
738             return null;
739         }
740 
741         List<ChangeEvent> events = new ArrayList<ChangeEvent>();
742         if (objectList.getObjects() != null) {
743             for (ObjectData objectData : objectList.getObjects()) {
744                 if (objectData == null) {
745                     continue;
746                 }
747 
748                 events.add(convertChangeEvent(objectData));
749             }
750         }
751 
752         boolean hasMoreItems = objectList.hasMoreItems() == null ? false : objectList.hasMoreItems().booleanValue();
753         long totalNumItems = objectList.getNumItems() == null ? -1 : objectList.getNumItems().longValue();
754 
755         return new ChangeEventsImpl(changeLogToken, events, hasMoreItems, totalNumItems);
756     }
757 
758     private void throwWrongTypeError(Object obj, String type, Class<?> clazz, String id) {
759         String expectedTypes;
760         if (BigInteger.class.isAssignableFrom(clazz)) {
761             expectedTypes = "<BigInteger, Byte, Short, Integer, Long>";
762         } else if (BigDecimal.class.isAssignableFrom(clazz)) {
763             expectedTypes = "<BigDecimal, Double, Float, Byte, Short, Integer, Long>";
764         } else if (GregorianCalendar.class.isAssignableFrom(clazz)) {
765             expectedTypes = "<java.util.GregorianCalendar, java.util.Date>";
766         } else {
767             expectedTypes = clazz.getName();
768         }
769 
770         String message = "Property '" + id + "' is a " + type + " property. Expected type '" + expectedTypes
771                 + "' but received a '" + obj.getClass().getName() + "' property.";
772 
773         throw new IllegalArgumentException(message);
774     }
775 }