001/*
002 *  Copyright 2017 Anyware Services
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 */
016package org.ametys.plugins.workspaces.cmis;
017
018import java.io.InputStream;
019import java.math.BigInteger;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Date;
025import java.util.EnumSet;
026import java.util.GregorianCalendar;
027import java.util.HashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import javax.jcr.RepositoryException;
033
034import org.apache.chemistry.opencmis.commons.PropertyIds;
035import org.apache.chemistry.opencmis.commons.data.AllowableActions;
036import org.apache.chemistry.opencmis.commons.data.ContentStream;
037import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
038import org.apache.chemistry.opencmis.commons.data.ObjectData;
039import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
040import org.apache.chemistry.opencmis.commons.data.ObjectParentData;
041import org.apache.chemistry.opencmis.commons.data.Properties;
042import org.apache.chemistry.opencmis.commons.data.PropertyData;
043import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
044import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
045import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
046import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
047import org.apache.chemistry.opencmis.commons.enums.Action;
048import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
049import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl;
050import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
051import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
052import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin;
053import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions;
054import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
055import org.apache.chemistry.opencmis.commons.enums.Updatability;
056import org.apache.chemistry.opencmis.commons.enums.VersioningState;
057import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
058import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
059import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
060import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
061import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
062import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
063import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
064import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
065import org.apache.chemistry.opencmis.commons.impl.dataobjects.AllowableActionsImpl;
066import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
067import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
068import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl;
069import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl;
070import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl;
071import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl;
072import org.apache.chemistry.opencmis.commons.impl.dataobjects.PartialContentStreamImpl;
073import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
074import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
075import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
076import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
077import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
078import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
079import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl;
080import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl;
081import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
082import org.apache.chemistry.opencmis.commons.server.CallContext;
083import org.apache.chemistry.opencmis.commons.server.ObjectInfoHandler;
084import org.apache.chemistry.opencmis.commons.spi.Holder;
085import org.apache.commons.lang.IllegalClassException;
086import org.apache.commons.lang3.StringUtils;
087
088import org.ametys.core.util.URIUtils;
089import org.ametys.plugins.explorer.resources.ModifiableResource;
090import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
091import org.ametys.plugins.explorer.resources.actions.AddOrUpdateResourceHelper.ResourceOperationMode;
092import org.ametys.plugins.explorer.resources.actions.AddOrUpdateResourceHelper.ResourceOperationResult;
093import org.ametys.plugins.repository.AmetysObject;
094import org.ametys.plugins.repository.AmetysObjectIterable;
095import org.ametys.plugins.workspaces.documents.DocumentWorkspaceModule;
096import org.ametys.plugins.workspaces.project.objects.Project;
097
098/**
099 * Helper class to retreive CMIS objects
100 *
101 */
102public class CmisRepository
103{
104    private static final String USER_UNKNOWN = "<unknown>";
105    
106    /**
107     * retreive informations for a repository
108     * @param context call context
109     * @param project project
110     * @param factory factory
111     * @return RepositoryInfo
112     */
113    public RepositoryInfo getRepositoryInfo(CallContext context, Project project, CmisServiceFactory factory)
114    {
115        String id = project.getId();
116        String name = project.getName();
117        if (id == null || id.trim().length() == 0 || name == null || name.trim().length() == 0)
118        {
119            throw new CmisInvalidArgumentException("Invalid repository!");
120        }
121
122        // set up repository infos
123        return createRepositoryInfo(project, factory, CmisVersion.CMIS_1_1);
124    }
125    
126    private RepositoryInfo createRepositoryInfo(Project project, CmisServiceFactory factory, CmisVersion cmisVersion)
127    {
128        assert cmisVersion != null;
129
130        RepositoryInfoImpl repositoryInfo = new RepositoryInfoImpl();
131
132        String id = project.getId();
133        ModifiableResourceCollection documentRoot = getRoot(project, factory);
134        
135        String root = "";
136        if (documentRoot != null)
137        {
138            root = documentRoot.getId();
139        }
140        
141        id = URIUtils.encodeParameter(URIUtils.encodeParameter(id));
142        root = URIUtils.encodeParameter(URIUtils.encodeParameter(root));
143        
144        repositoryInfo.setId(id);
145        repositoryInfo.setName(project.getName());
146        repositoryInfo.setDescription(project.getDescription());
147
148        repositoryInfo.setCmisVersionSupported(cmisVersion.value());
149
150        repositoryInfo.setProductName("Ametys CMIS Server");
151        repositoryInfo.setProductVersion("1.0");
152        repositoryInfo.setVendorName("Ametys");
153
154        repositoryInfo.setRootFolder(root);
155
156        repositoryInfo.setThinClientUri("");
157        repositoryInfo.setChangesIncomplete(false);
158        RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl();
159        capabilities.setCapabilityAcl(CapabilityAcl.NONE);
160        capabilities.setAllVersionsSearchable(false);
161        capabilities.setCapabilityJoin(CapabilityJoin.NONE);
162        capabilities.setSupportsMultifiling(false);
163        capabilities.setSupportsUnfiling(false);
164        capabilities.setSupportsVersionSpecificFiling(false);
165        capabilities.setIsPwcSearchable(false);
166        capabilities.setIsPwcUpdatable(false);
167        //capabilities.setCapabilityQuery(CapabilityQuery.METADATAONLY);
168        capabilities.setCapabilityChanges(CapabilityChanges.NONE);
169        capabilities.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.ANYTIME);
170        capabilities.setSupportsGetDescendants(false);
171        capabilities.setSupportsGetFolderTree(false);
172        capabilities.setCapabilityRendition(CapabilityRenditions.NONE);
173
174        repositoryInfo.setCapabilities(capabilities);
175        
176        return repositoryInfo;
177    }
178    
179    /**
180     * getTypeDefinition
181     * @param context call context
182     * @param typeId type ID
183     * @param factory factory
184     * @return the type definition
185     */
186    public TypeDefinition getTypeDefinition(CallContext context, String typeId, CmisServiceFactory factory) 
187    {
188        //checkUser(context, false);
189
190        return factory.getTypeManager().getTypeDefinition(context, typeId);
191    }
192    
193    /**
194     * getTypeChildren
195     * @param context context
196     * @param typeId typeId
197     * @param includePropertyDefinitions includePropertyDefinitions
198     * @param maxItems maxItems
199     * @param skipCount skipCount
200     * @param factory factory
201     * @return the type definition list
202     */
203    public TypeDefinitionList getTypeChildren(CallContext context, String typeId, Boolean includePropertyDefinitions,
204            BigInteger maxItems, BigInteger skipCount, CmisServiceFactory factory) 
205    {
206        //checkUser(context, false);
207
208        return factory.getTypeManager().getTypeChildren(context, typeId, includePropertyDefinitions, maxItems, skipCount);
209    }
210    
211    /**
212     * getObject
213     * @param context context
214     * @param project Project
215     * @param objectId objectId
216     * @param versionServicesId versionServicesId
217     * @param filter filter
218     * @param includeAllowableActions includeAllowableActions
219     * @param includeAcl includeAcl
220     * @param objectInfos objectInfos
221     * @param factory factory
222     * @return ObjectData ObjectData
223     */
224    public ObjectData getObject(CallContext context, Project project, String objectId, String versionServicesId, String filter,
225            Boolean includeAllowableActions, Boolean includeAcl, ObjectInfoHandler objectInfos, 
226            CmisServiceFactory factory)
227    {
228        // check id
229        if (objectId == null && versionServicesId == null)
230        {
231            throw new CmisInvalidArgumentException("Object Id must be set.");
232        }
233        
234        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
235        
236        boolean userReadOnly = false;
237
238        // set defaults if values not set
239        boolean iaa = CmisUtils.getBooleanParameter(includeAllowableActions, false);
240        boolean iacl = false; //CmisUtils.getBooleanParameter(includeAcl, false);
241
242        // split filter
243        Set<String> filterCollection = CmisUtils.splitFilter(filter);
244
245        // gather properties
246        
247        return compileObjectData(context, ametysObject, project, filterCollection, iaa, iacl, userReadOnly, objectInfos, factory);
248    }
249    
250    
251    private ObjectData compileObjectData(CallContext context, AmetysObject ametysObject, Project project, Set<String> filter,
252            boolean includeAllowableActions, boolean includeAcl, boolean userReadOnly, ObjectInfoHandler objectInfos,
253            CmisServiceFactory factory)
254    {
255        ObjectDataImpl result = new ObjectDataImpl();
256        ObjectInfoImpl objectInfo = new ObjectInfoImpl();
257
258        result.setProperties(compileProperties(context, ametysObject, project, filter, objectInfo, factory));
259
260        if (includeAllowableActions)
261        {
262            result.setAllowableActions(compileAllowableActions(ametysObject, project, factory, userReadOnly));
263        }
264
265        if (includeAcl)
266        {
267            result.setIsExactAcl(true);
268        }
269        result.setIsExactAcl(true);
270
271        if (context.isObjectInfoRequired())
272        {
273            objectInfo.setObject(result);
274            objectInfos.addObjectInfo(objectInfo);
275        }
276
277        return result;
278    }
279    
280    private Properties compileProperties(CallContext context, AmetysObject ametysObject, Project project, Set<String> orgfilter,
281            ObjectInfoImpl objectInfo, CmisServiceFactory factory)
282    {
283        // copy filter
284        Set<String> filter = orgfilter == null ? null : new HashSet<>(orgfilter);
285
286        // find base type
287        String typeId = null;
288        Boolean isFolder = true;
289        
290        if (ametysObject != null && ametysObject instanceof ModifiableResource)
291        {
292            isFolder = false;
293            typeId = BaseTypeId.CMIS_DOCUMENT.value();
294            objectInfo.setBaseType(BaseTypeId.CMIS_DOCUMENT);
295            objectInfo.setTypeId(typeId);
296            objectInfo.setHasAcl(false);
297            objectInfo.setHasContent(true);
298            objectInfo.setHasParent(true);
299            objectInfo.setVersionSeriesId(null);
300            objectInfo.setIsCurrentVersion(true);
301            objectInfo.setRelationshipSourceIds(null);
302            objectInfo.setRelationshipTargetIds(null);
303            objectInfo.setRenditionInfos(null);
304            objectInfo.setSupportsDescendants(false);
305            objectInfo.setSupportsFolderTree(false);
306            objectInfo.setSupportsPolicies(false);
307            objectInfo.setSupportsRelationships(false);
308            objectInfo.setWorkingCopyId(null);
309            objectInfo.setWorkingCopyOriginalId(null);
310        }
311        else if (ametysObject != null && ametysObject instanceof ModifiableResourceCollection)
312        {
313            isFolder = true;
314            typeId = BaseTypeId.CMIS_FOLDER.value();
315            objectInfo.setBaseType(BaseTypeId.CMIS_FOLDER);
316            objectInfo.setTypeId(typeId);
317            objectInfo.setContentType(null);
318            objectInfo.setFileName(null);
319            objectInfo.setHasAcl(false);
320            objectInfo.setHasContent(false);
321            objectInfo.setVersionSeriesId(null);
322            objectInfo.setIsCurrentVersion(true);
323            objectInfo.setRelationshipSourceIds(null);
324            objectInfo.setRelationshipTargetIds(null);
325            objectInfo.setRenditionInfos(null);
326            objectInfo.setSupportsDescendants(true);
327            objectInfo.setSupportsFolderTree(true);
328            objectInfo.setSupportsPolicies(false);
329            objectInfo.setSupportsRelationships(false);
330            objectInfo.setWorkingCopyId(null);
331            objectInfo.setWorkingCopyOriginalId(null);
332        }
333        else
334        {
335            throw new IllegalArgumentException("Resource not found");
336        }
337        
338        try 
339        {
340            PropertiesImpl result = new PropertiesImpl();
341
342            addPropertyId(result, typeId, filter, PropertyIds.OBJECT_ID, ametysObject.getId(), factory);
343            objectInfo.setId(ametysObject.getId());
344
345            // name
346            String name = ametysObject.getName();
347            addPropertyString(result, typeId, filter, PropertyIds.NAME, name, factory);
348            objectInfo.setName(name);
349
350            if (isFolder)
351            {
352                ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
353                addPropertyString(result, typeId, filter, PropertyIds.CREATED_BY, USER_UNKNOWN, factory);
354                addPropertyString(result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, USER_UNKNOWN, factory);
355                objectInfo.setCreatedBy(USER_UNKNOWN);
356                addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, folder.getDescription(), factory);
357                
358                // base type and type name
359                addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_FOLDER.value(), factory);
360                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_FOLDER.value(), factory);
361                
362                String path = ((ModifiableResourceCollection) ametysObject).getExplorerPath();
363                ModifiableResourceCollection root = getRoot(project, factory);
364                path = path.substring(root.getExplorerPath().length());
365                if (path.length() == 0)
366                {
367                    path = "/";
368                }
369                addPropertyString(result, typeId, filter, PropertyIds.PATH, path, factory);
370
371                // folder properties
372                
373                //On évite d'appeler isRoot qui ferait un appel JCR de plus
374                if (ametysObject.equals(root))
375                {
376                    addPropertyId(result, typeId, filter, PropertyIds.PARENT_ID, null, factory);
377                    objectInfo.setHasParent(false);
378                }
379                else
380                {
381                    String parentId = ametysObject.getParent().getId();
382                    addPropertyId(result, typeId, filter, PropertyIds.PARENT_ID, parentId, factory);
383                    objectInfo.setHasParent(true);
384                }
385
386                addPropertyIdList(result, typeId, filter, PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, null, factory);
387            }
388            else
389            {
390                ModifiableResource modifiableResource = (ModifiableResource) ametysObject;
391                String author = modifiableResource.getCreator().getLogin();
392                String contributor = modifiableResource.getLastContributor().getLogin();
393                addPropertyString(result, typeId, filter, PropertyIds.CREATED_BY, author, factory);
394                addPropertyString(result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, contributor, factory);
395                addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, modifiableResource.getDCDescription(), factory);
396                objectInfo.setCreatedBy(author);
397                
398                Date lastModifiedDate = modifiableResource.getLastModified();
399                GregorianCalendar lastModified = new GregorianCalendar();
400                lastModified.setTime(lastModifiedDate);
401                Date creationDate = modifiableResource.getCreationDate();
402                GregorianCalendar creation = new GregorianCalendar();
403                creation.setTime(creationDate);
404                addPropertyDateTime(result, typeId, filter, PropertyIds.CREATION_DATE, creation, factory);
405                addPropertyDateTime(result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified, factory);
406                objectInfo.setCreationDate(creation);
407                objectInfo.setLastModificationDate(lastModified);
408                
409             // base type and type name
410                addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value(), factory);
411                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value(), factory);
412
413                // file properties
414                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_IMMUTABLE, false, factory);
415                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_VERSION, true, factory);
416                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_MAJOR_VERSION, true, factory);
417                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, true, factory);
418                addPropertyString(result, typeId, filter, PropertyIds.VERSION_LABEL, modifiableResource.getName(), factory);
419                //addPropertyId(result, typeId, filter, PropertyIds.VERSION_SERIES_ID, fileToId(file), factory);
420                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, false, factory);
421                addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null, factory);
422                addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null, factory);
423                addPropertyString(result, typeId, filter, PropertyIds.CHECKIN_COMMENT, "", factory);
424                if (context.getCmisVersion() != CmisVersion.CMIS_1_0) 
425                {
426                    addPropertyBoolean(result, typeId, filter, PropertyIds.IS_PRIVATE_WORKING_COPY, false, factory);
427                }
428
429                
430                if (modifiableResource.getLength() == 0) 
431                {
432                    addPropertyBigInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, null, factory);
433                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE, null, factory);
434                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, null, factory);
435
436                    objectInfo.setHasContent(false);
437                    objectInfo.setContentType(null);
438                    objectInfo.setFileName(null);
439                }
440                else 
441                {
442                    
443                    addPropertyInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, modifiableResource.getLength(), factory);
444                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE,
445                            modifiableResource.getMimeType(), factory);
446                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, modifiableResource.getName(), factory);
447
448                    objectInfo.setHasContent(true);
449                    objectInfo.setContentType(modifiableResource.getMimeType());
450                    objectInfo.setFileName(modifiableResource.getName());
451                }
452
453                addPropertyId(result, typeId, filter, PropertyIds.CONTENT_STREAM_ID, null, factory);
454            }
455
456            
457
458            // change token - always null
459            addPropertyString(result, typeId, filter, PropertyIds.CHANGE_TOKEN, null, factory);
460
461            // CMIS 1.1 properties
462            if (context.getCmisVersion() != CmisVersion.CMIS_1_0)
463            {
464                addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, null, factory);
465                addPropertyIdList(result, typeId, filter, PropertyIds.SECONDARY_OBJECT_TYPE_IDS, null, factory);
466            }
467
468            return result;
469        }
470        catch (CmisBaseException cbe)
471        {
472            throw cbe;
473        }
474        catch (Exception e)
475        {
476            throw new CmisRuntimeException(e.getMessage(), e);
477        }
478    }
479    
480    /**
481     * get children of a folder
482     * @param context call context
483     * @param folderId folder Id
484     * @param project Project
485     * @param filter filters
486     * @param includeAllowableActions allowable actions
487     * @param includePathSegment include path segment
488     * @param maxItems max items
489     * @param skipCount skip count
490     * @param objectInfos object infos
491     * @param factory factory
492     * @return ObjectInFolderList
493     */
494    public ObjectInFolderList getChildren(CallContext context, String folderId, Project project, String filter,
495            Boolean includeAllowableActions, Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount,
496            ObjectInfoHandler objectInfos, CmisServiceFactory factory)
497    {
498        boolean userReadOnly = false;
499        
500        // split filter
501        Set<String> filterCollection = CmisUtils.splitFilter(filter);
502
503        // set defaults if values not set
504        boolean iaa = CmisUtils.getBooleanParameter(includeAllowableActions, false);
505        boolean ips = CmisUtils.getBooleanParameter(includePathSegment, false);
506
507        // skip and max
508        int skip = skipCount == null ? 0 : skipCount.intValue();
509        if (skip < 0)
510        {
511            skip = 0;
512        }
513
514        int max = maxItems == null ? Integer.MAX_VALUE : maxItems.intValue();
515        if (max < 0)
516        {
517            max = Integer.MAX_VALUE;
518        }
519
520        // get the folder
521        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
522        if (!(ametysObject instanceof ModifiableResourceCollection)) 
523        {
524            throw new CmisObjectNotFoundException("Not a folder!");
525        }
526        ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
527
528        // set object info of the the folder
529        if (context.isObjectInfoRequired())
530        {
531            compileObjectData(context, ametysObject, project, null, false, false, userReadOnly, objectInfos, factory);
532        }
533
534        // prepare result
535        ObjectInFolderListImpl result = new ObjectInFolderListImpl();
536        result.setObjects(new ArrayList<>());
537        result.setHasMoreItems(false);
538        int count = 0;
539
540        // iterate through children
541        AmetysObjectIterable<AmetysObject> children = folder.getChildren();
542        for (AmetysObject child : children)
543        //for (File child : folder.listFiles())
544        {
545            // skip nodes other than ModifiableResource or ModifiableResourceCollection
546            if (!(child instanceof ModifiableResource || child instanceof ModifiableResourceCollection))
547            {
548                continue;
549            }
550
551            count++;
552
553            if (skip > 0)
554            {
555                skip--;
556                continue;
557            }
558
559            if (result.getObjects().size() >= max)
560            {
561                result.setHasMoreItems(true);
562                continue;
563            }
564
565            // build and add child object
566            ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
567            objectInFolder.setObject(compileObjectData(context, child, project, filterCollection, iaa, false, userReadOnly,
568                    objectInfos, factory));
569            if (ips)
570            {
571                objectInFolder.setPathSegment(child.getName());
572            }
573
574            result.getObjects().add(objectInFolder);
575        }
576
577        result.setNumItems(BigInteger.valueOf(count));
578
579        return result;
580    }
581    
582    /**
583     * get the parents of an object
584     * @param context call context
585     * @param objectId Object Id
586     * @param project Project
587     * @param filter filters
588     * @param includeAllowableActions allowable actions
589     * @param includeRelativePathSegment relative path segment
590     * @param objectInfos object infos
591     * @param factory factory 
592     * @return List of ObjectParentData
593     */
594    public List<ObjectParentData> getObjectParents(CallContext context, String objectId, Project project, String filter,
595            Boolean includeAllowableActions, Boolean includeRelativePathSegment, ObjectInfoHandler objectInfos, CmisServiceFactory factory)
596    {
597        boolean userReadOnly = false;
598
599        // split filter
600        Set<String> filterCollection = CmisUtils.splitFilter(filter);
601
602        // set defaults if values not set
603        boolean iaa = CmisUtils.getBooleanParameter(includeAllowableActions, false);
604        boolean irps = CmisUtils.getBooleanParameter(includeRelativePathSegment, false);
605
606        // get the file or folder
607        //File file = getFile(objectId);
608
609        // don't climb above the root folder
610        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
611        if (isRoot(ametysObject, project, factory))
612        {
613            return Collections.emptyList();
614        }
615
616        // set object info of the the object
617        if (context.isObjectInfoRequired())
618        {
619            compileObjectData(context, ametysObject, project, null, false, false, userReadOnly, objectInfos, factory);
620        }
621
622        // get parent folder
623        AmetysObject parent = ametysObject.getParent();
624        //File parent = file.getParentFile();
625        ObjectData object = compileObjectData(context, parent, project, filterCollection, iaa, false, userReadOnly, objectInfos, factory);
626
627        ObjectParentDataImpl result = new ObjectParentDataImpl();
628        result.setObject(object);
629        if (irps)
630        {
631            result.setRelativePathSegment(ametysObject.getName());
632        }
633
634        return Collections.<ObjectParentData> singletonList(result);
635    }
636    /**
637     * get the inputstream to read a file
638     * @param context call context
639     * @param objectId object Id
640     * @param offset offset
641     * @param length lenght
642     * @param factory factory
643     * @return ContentStream
644     */
645    public ContentStream getContentStream(CallContext context, String objectId, BigInteger offset, BigInteger length, CmisServiceFactory factory)
646    {
647        // get the file
648        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
649        if (!(ametysObject instanceof ModifiableResource))
650        {
651            throw new CmisStreamNotSupportedException("Not a file!");
652        }
653        ModifiableResource res = (ModifiableResource) ametysObject;
654        
655        if (res.getLength() == 0)
656        {
657            throw new CmisConstraintException("Document has no content!");
658        }
659
660        InputStream stream = null;
661        stream = res.getInputStream();
662        if (offset != null || length != null)
663        {
664            stream = new CmisContentRangeInputStream(stream, offset, length);
665        }
666
667        // compile data
668        ContentStreamImpl result;
669        if (offset != null && offset.longValue() > 0
670            || length != null)
671        {
672            result = new PartialContentStreamImpl();
673        }
674        else
675        {
676            result = new ContentStreamImpl();
677        }
678
679        result.setFileName(res.getName());
680        result.setLength(BigInteger.valueOf(res.getLength()));
681        result.setMimeType(res.getMimeType());
682        result.setStream(stream);
683
684        return result;
685    }
686
687    /**
688     * get an object by it's path
689     * @param context call context
690     * @param project Project
691     * @param folderPath path of the object/folder
692     * @param filter filters for metadata
693     * @param includeAllowableActions allowable actions
694     * @param includeACL ACL
695     * @param objectInfos infos
696     * @param factory factory
697     * @return datas of the object
698     */
699    public ObjectData getObjectByPath(CallContext context, Project project, String folderPath, String filter,
700            boolean includeAllowableActions, boolean includeACL, ObjectInfoHandler objectInfos, CmisServiceFactory factory)
701    {
702        boolean userReadOnly = false;
703
704        // split filter
705        Set<String> filterCollection = CmisUtils.splitFilter(filter);
706
707        // check path
708        if (folderPath == null || folderPath.length() == 0 || folderPath.charAt(0) != '/')
709        {
710            throw new CmisInvalidArgumentException("Invalid folder path!");
711        }
712
713        // get the file or folder
714        AmetysObject root = getRoot(project, factory);
715        AmetysObject file = null;
716        if (folderPath.length() == 1)
717        {
718            file = root;
719        }
720        else
721        {
722            file = factory.getResolver().resolveByPath(root.getPath() + folderPath);
723            //String path = folderPath.replace('/', File.separatorChar).substring(1);
724            //file = new File(root, path);
725        }
726
727        if (file == null)
728        {
729            throw new CmisObjectNotFoundException("Path doesn't exist.");
730        }
731        return compileObjectData(context, file, project, filterCollection, includeAllowableActions, includeACL, userReadOnly,
732                objectInfos, factory);
733    }
734    
735    /**
736     * Create a new folder in another one
737     * @param context call context
738     * @param properties properties
739     * @param project Project
740     * @param folderId folder Id
741     * @param factory factory
742     * @return id of the created folder
743     */
744    public String createFolder(CallContext context, Properties properties, Project project, String folderId, CmisServiceFactory factory)
745    {
746        // check properties
747        checkNewProperties(properties, BaseTypeId.CMIS_FOLDER, factory);
748
749        // get parent File
750        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
751        if (!(ametysObject instanceof ModifiableResourceCollection))
752        {
753            throw new CmisObjectNotFoundException("Parent is not a folder!");
754        }
755        
756        // create the folder
757        String name = CmisUtils.getStringProperty(properties, PropertyIds.NAME);
758        String description = CmisUtils.getStringProperty(properties, PropertyIds.DESCRIPTION, "");
759        
760        Map<String, Object> addFolder;
761        addFolder = factory.getWorkspaceExplorerResourceDAO().addFolder(folderId, name, description, true);
762        return (String) addFolder.get("id");
763    }
764    
765    /**
766     * Create a new document in a folder
767     * @param context call context
768     * @param properties properties
769     * @param project Project
770     * @param folderId folder Id
771     * @param contentStream content Stream
772     * @param versioningState versionning State
773     * @param factory factory
774     * @return id of the created document
775     */
776    public String createDocument(CallContext context, Properties properties, Project project, String folderId,
777            ContentStream contentStream, VersioningState versioningState, CmisServiceFactory factory)
778    {
779        // check versioning state
780        if (VersioningState.NONE != versioningState)
781        {
782            throw new CmisConstraintException("Versioning not supported!");
783        }
784
785        // check properties
786        checkNewProperties(properties, BaseTypeId.CMIS_DOCUMENT, factory);
787
788        String name = CmisUtils.getStringProperty(properties, PropertyIds.NAME);
789        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
790        if (!(ametysObject instanceof ModifiableResourceCollection))
791        {
792            throw new IllegalClassException(ModifiableResourceCollection.class, ametysObject.getClass());
793        }
794        
795        factory.getAddOrUpdateResourceHelper().checkAddResourceRight((ModifiableResourceCollection) ametysObject);
796        ResourceOperationResult resourceOperation = factory.getAddOrUpdateResourceHelper().performResourceOperation(contentStream.getStream(), name, (ModifiableResourceCollection) ametysObject, ResourceOperationMode.ADD_RENAME);
797        if (!resourceOperation.isSuccess())
798        {
799            throw new CmisStorageException("Could not create file: " + resourceOperation.getErrorMessage());
800        }
801
802        String id = resourceOperation.getResource().getId();
803        return id;
804    }
805    
806    /**
807     * CMIS deleteObject.
808     * @param context call context
809     * @param objectId object ID
810     * @param factory factory
811     */
812    public void deleteObject(CallContext context, String objectId, CmisServiceFactory factory)
813    {
814        List<String> toDelete = new ArrayList<>(1);
815        toDelete.add(objectId);
816        Map deleted = factory.getWorkspaceExplorerResourceDAO().deleteObject(toDelete);
817        
818        if (deleted.containsKey("message"))
819        {
820            throw new CmisStorageException("Deletion failed : " + deleted.get("message"));
821        }
822    }
823    
824    /**
825     * CMIS getFolderParent.
826     * @param context call context
827     * @param project Project
828     * @param folderId folder Id
829     * @param filter filters
830     * @param objectInfos objectInfos
831     * @param factory factory
832     * @return ObjectData
833     */
834    public ObjectData getFolderParent(CallContext context, Project project, String folderId, String filter, ObjectInfoHandler objectInfos, CmisServiceFactory factory)
835    {
836        boolean userReadOnly = false;
837        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
838        if (isRoot(ametysObject, project, factory))
839        {
840            throw new CmisInvalidArgumentException("The root folder has no parent!");
841        }
842        AmetysObject parent = ametysObject.getParent();
843        
844        Set<String> filterCollection = CmisUtils.splitFilter(filter);
845        ObjectData object = compileObjectData(context, parent, project, filterCollection, false, false, userReadOnly, objectInfos, factory);
846        
847        return object;
848    }
849    
850    /**
851     * CMIS deleteTree.
852     * @param context call context
853     * @param project Project
854     * @param folderId folder Id
855     * @param factory factory
856     * @return FailedToDeleteData
857     */
858    public FailedToDeleteData deleteTree(CallContext context, Project project, String folderId, CmisServiceFactory factory)
859    {
860        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
861        if (ametysObject instanceof ModifiableResourceCollection)
862        {
863            ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
864            AmetysObjectIterable<AmetysObject> children = folder.getChildren();
865            List<String> toDelete = new ArrayList<>();
866            for (AmetysObject child : children)
867            {
868                toDelete.add(child.getId());
869                
870            }
871            Map deletedMsg = factory.getWorkspaceExplorerResourceDAO().deleteObject(toDelete);
872            
873            if (deletedMsg.containsKey("message"))
874            {
875                throw new CmisStorageException("Deletion failed : " + deletedMsg.get("message"));
876            }
877        }
878        else
879        {
880            throw new CmisConstraintException("Object is not a folder!");
881        }
882        
883        FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
884        result.setIds(new ArrayList<>());
885
886        return result;
887    }
888    
889    /**
890     * CMIS updateProperties.
891     * @param context call context
892     * @param project Project
893     * @param objectIdHolder objectId in a holder
894     * @param properties properties
895     * @param objectInfos object infos
896     * @param factory factory
897     * @return ObjectData
898     */
899    public ObjectData updateProperties(CallContext context, Project project, Holder<String> objectIdHolder, Properties properties,
900            ObjectInfoHandler objectInfos, CmisServiceFactory factory)
901    {
902        // check object id
903        if (objectIdHolder == null || objectIdHolder.getValue() == null)
904        {
905            throw new CmisInvalidArgumentException("Id is not valid!");
906        }
907        String objectId = objectIdHolder.getValue();
908        boolean userReadOnly = false;
909        
910        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
911
912        if (ametysObject != null && ametysObject instanceof ModifiableResource)
913        {
914            ModifiableResource resource = (ModifiableResource) ametysObject;
915            String inputName = CmisUtils.getStringProperty(properties, PropertyIds.NAME, resource.getName());
916            String description = CmisUtils.getStringProperty(properties, PropertyIds.DESCRIPTION, resource.getDCDescription());
917            Collection<String> tags =  new ArrayList<>(Arrays.asList(resource.getKeywords()));
918            
919            factory.getWorkspaceExplorerResourceDAO().editFile(objectId, inputName, description, tags);
920            
921        }
922        else if (ametysObject != null && ametysObject instanceof ModifiableResourceCollection)
923        {
924            ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
925            String inputName = CmisUtils.getStringProperty(properties, PropertyIds.NAME, folder.getName());
926            String description = CmisUtils.getStringProperty(properties, PropertyIds.DESCRIPTION, folder.getDescription());
927            if (description == null)
928            {
929                description = StringUtils.EMPTY;
930            }
931            factory.getWorkspaceExplorerResourceDAO().editFolder(objectId, inputName, description);
932        }
933        else
934        {
935            throw new CmisObjectNotFoundException("File not found!");
936        }
937        
938        AmetysObject modified = factory.getResolver().resolveById(objectId);
939        return compileObjectData(context, modified, project, null, false, false, userReadOnly, objectInfos, factory);
940    }
941    
942    /**
943     * CMIS setContentStream, deleteContentStream, and appendContentStream.
944     * @param context call context
945     * @param project Project
946     * @param objectIdHolder object Id in a holder
947     * @param overwriteFlag overwrite
948     * @param contentStream inputStream
949     * @param append append
950     * @param factory factory
951     */
952    public void changeContentStream(CallContext context, Project project, Holder<String> objectIdHolder, Boolean overwriteFlag,
953            ContentStream contentStream, boolean append, CmisServiceFactory factory)
954    {
955        if (objectIdHolder == null || objectIdHolder.getValue() == null)
956        {
957            throw new CmisInvalidArgumentException("Id is not valid!");
958        }
959        String objectId = objectIdHolder.getValue();
960        
961        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
962        
963     // check overwrite
964        boolean owf = CmisUtils.getBooleanParameter(overwriteFlag, true);
965        if (!owf && contentStream.getLength() > 0)
966        {
967            throw new CmisContentAlreadyExistsException("Content already exists!");
968        }
969        
970        if (ametysObject instanceof ModifiableResource)
971        {
972            factory.getAddOrUpdateResourceHelper().checkAddResourceRight((ModifiableResourceCollection) ametysObject);
973            
974            String name = ametysObject.getName();
975            ResourceOperationResult resourceOperation = factory.getAddOrUpdateResourceHelper().performResourceOperation(contentStream.getStream(), name, ametysObject.getParent(), ResourceOperationMode.UPDATE);
976                
977            if (!resourceOperation.isSuccess())
978            {
979                throw new CmisRuntimeException("Impossible to update file : " + resourceOperation.getErrorMessage());
980            }
981        }
982        else
983        {
984            throw new CmisObjectNotFoundException("File not found!");
985        }
986    }
987    /**
988     * CMIS moveObject.
989     * @param context call context
990     * @param project Project
991     * @param objectId objectId in a holder
992     * @param targetFolderId folderId
993     * @param objectInfos objectInfos
994     * @param factory factory
995     * @return ObjectData
996     */
997    public ObjectData moveObject(CallContext context, Project project, Holder<String> objectId, String targetFolderId,
998            ObjectInfoHandler objectInfos, CmisServiceFactory factory)
999    {
1000        boolean userReadOnly = false;
1001
1002        if (objectId == null)
1003        {
1004            throw new CmisInvalidArgumentException("Id is not valid!");
1005        }
1006
1007        List<String> documentIds = new ArrayList<>(1);
1008        documentIds.add(objectId.getValue());
1009        
1010        Map<String, Object> result;
1011        try
1012        {
1013            result = factory.getWorkspaceExplorerResourceDAO().moveDocuments(documentIds, targetFolderId);
1014            if (result.containsKey("message"))
1015            {
1016                throw new CmisStorageException((String) result.get("message"));
1017            }
1018            else
1019            {
1020                AmetysObject ametysObject = factory.getResolver().resolveById(objectId.getValue());
1021                return compileObjectData(context, ametysObject, project, null, false, false, userReadOnly, objectInfos, factory);
1022            }
1023        }
1024        catch (RepositoryException e)
1025        {
1026            throw new CmisStorageException("Impossible to move file", e);
1027        }
1028        
1029        
1030    }
1031    
1032    /*
1033     * private helpers
1034     * 
1035     */
1036    
1037    /*
1038     * Checks a property set for a new object.
1039     */
1040    private void checkNewProperties(Properties properties, BaseTypeId baseTypeId, CmisServiceFactory factory)
1041    {
1042        // check properties
1043        if (properties == null || properties.getProperties() == null)
1044        {
1045            throw new CmisInvalidArgumentException("Properties must be set!");
1046        }
1047
1048        // check the name
1049        /*
1050        String name = CmisUtils.getStringProperty(properties, PropertyIds.NAME);
1051        if (!isValidName(name)) {
1052            throw new CmisNameConstraintViolationException("Name is not valid!");
1053        }*/
1054
1055        // check the type
1056        String typeId = CmisUtils.getObjectTypeId(properties);
1057        if (typeId == null)
1058        {
1059            throw new CmisInvalidArgumentException("Type Id is not set!");
1060        }
1061
1062        TypeDefinition type = factory.getTypeManager().getInternalTypeDefinition(typeId);
1063        if (type == null)
1064        {
1065            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
1066        }
1067
1068        if (type.getBaseTypeId() != baseTypeId)
1069        {
1070            if (baseTypeId == BaseTypeId.CMIS_DOCUMENT)
1071            {
1072                throw new CmisInvalidArgumentException("Type is not a document type!");
1073            }
1074            else if (baseTypeId == BaseTypeId.CMIS_DOCUMENT)
1075            {
1076                throw new CmisInvalidArgumentException("Type is not a folder type!");
1077            }
1078            else
1079            {
1080                throw new CmisRuntimeException("A file system does not support a " + baseTypeId.value() + " type!");
1081            }
1082        }
1083
1084        // check type properties
1085        checkTypeProperties(properties, typeId, factory, true);
1086
1087        // check if required properties are missing
1088        for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values())
1089        {
1090            if (propDef.isRequired() && !properties.getProperties().containsKey(propDef.getId())
1091                    && propDef.getUpdatability() != Updatability.READONLY)
1092            {
1093                throw new CmisConstraintException("Property '" + propDef.getId() + "' is required!");
1094            }
1095        }
1096    }
1097    
1098    /*
1099     * Checks if the property belong to the type and are settable.
1100     */
1101    private void checkTypeProperties(Properties properties, String typeId, CmisServiceFactory factory, boolean isCreate)
1102    {
1103        // check type
1104        TypeDefinition type = factory.getTypeManager().getInternalTypeDefinition(typeId);
1105        if (type == null)
1106        {
1107            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
1108        }
1109
1110        // check if all required properties are there
1111        for (PropertyData<?> prop : properties.getProperties().values())
1112        {
1113            PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
1114
1115            // do we know that property?
1116            if (propType == null)
1117            {
1118                throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
1119            }
1120
1121            // can it be set?
1122            if (propType.getUpdatability() == Updatability.READONLY)
1123            {
1124                throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
1125            }
1126
1127            if (!isCreate)
1128            {
1129                // can it be set?
1130                if (propType.getUpdatability() == Updatability.ONCREATE)
1131                {
1132                    throw new CmisConstraintException("Property '" + prop.getId() + "' cannot be updated!");
1133                }
1134            }
1135        }
1136    }
1137    
1138    private void addPropertyId(PropertiesImpl props, String typeId, Set<String> filter, String id, String value, CmisServiceFactory factory)
1139    {
1140        if (!checkAddProperty(props, typeId, filter, id, factory))
1141        {
1142            return;
1143        }
1144
1145        props.addProperty(new PropertyIdImpl(id, value));
1146    }
1147
1148    private void addPropertyIdList(PropertiesImpl props, String typeId, Set<String> filter, String id,
1149            List<String> value, CmisServiceFactory factory)
1150    {
1151        if (!checkAddProperty(props, typeId, filter, id, factory))
1152        {
1153            return;
1154        }
1155
1156        props.addProperty(new PropertyIdImpl(id, value));
1157    }
1158
1159    private void addPropertyString(PropertiesImpl props, String typeId, Set<String> filter, String id, String value, CmisServiceFactory factory)
1160    {
1161        if (!checkAddProperty(props, typeId, filter, id, factory))
1162        {
1163            return;
1164        }
1165
1166        props.addProperty(new PropertyStringImpl(id, value));
1167    }
1168
1169    private void addPropertyInteger(PropertiesImpl props, String typeId, Set<String> filter, String id, long value, CmisServiceFactory factory)
1170    {
1171        addPropertyBigInteger(props, typeId, filter, id, BigInteger.valueOf(value), factory);
1172    }
1173
1174    private void addPropertyBigInteger(PropertiesImpl props, String typeId, Set<String> filter, String id,
1175            BigInteger value, CmisServiceFactory factory)
1176    {
1177        if (!checkAddProperty(props, typeId, filter, id, factory))
1178        {
1179            return;
1180        }
1181
1182        props.addProperty(new PropertyIntegerImpl(id, value));
1183    }
1184
1185    private void addPropertyBoolean(PropertiesImpl props, String typeId, Set<String> filter, String id, boolean value, CmisServiceFactory factory)
1186    {
1187        if (!checkAddProperty(props, typeId, filter, id, factory))
1188        {
1189            return;
1190        }
1191
1192        props.addProperty(new PropertyBooleanImpl(id, value));
1193    }
1194
1195    private void addPropertyDateTime(PropertiesImpl props, String typeId, Set<String> filter, String id,
1196            GregorianCalendar value, CmisServiceFactory factory)
1197    {
1198        if (!checkAddProperty(props, typeId, filter, id, factory))
1199        {
1200            return;
1201        }
1202
1203        props.addProperty(new PropertyDateTimeImpl(id, value));
1204    }
1205
1206    private boolean checkAddProperty(Properties properties, String typeId, Set<String> filter, String id, CmisServiceFactory factory)
1207    {
1208        if (properties == null || properties.getProperties() == null)
1209        {
1210            throw new IllegalArgumentException("Properties must not be null!");
1211        }
1212
1213        if (id == null)
1214        {
1215            throw new IllegalArgumentException("Id must not be null!");
1216        }
1217
1218        TypeDefinition type = factory.getTypeManager().getInternalTypeDefinition(typeId);
1219        if (type == null)
1220        {
1221            throw new IllegalArgumentException("Unknown type: " + typeId);
1222        }
1223        if (!type.getPropertyDefinitions().containsKey(id))
1224        {
1225            throw new IllegalArgumentException("Unknown property: " + id);
1226        }
1227
1228        String queryName = type.getPropertyDefinitions().get(id).getQueryName();
1229
1230        if (queryName != null && filter != null)
1231        {
1232            if (!filter.contains(queryName))
1233            {
1234                return false;
1235            }
1236            else
1237            {
1238                filter.remove(queryName);
1239            }
1240        }
1241
1242        return true;
1243    }
1244    
1245    /*
1246     * Compiles the allowable actions for a file or folder.
1247     */
1248    private AllowableActions compileAllowableActions(AmetysObject file, Project project, CmisServiceFactory factory, boolean userReadOnly)
1249    {
1250        if (file == null)
1251        {
1252            throw new IllegalArgumentException("File must not be null!");
1253        }
1254
1255        boolean isReadOnly = false;
1256        boolean isRoot = isRoot(file, project, factory);
1257
1258        Set<Action> aas = EnumSet.noneOf(Action.class);
1259
1260        addAction(aas, Action.CAN_GET_OBJECT_PARENTS, !isRoot);
1261        addAction(aas, Action.CAN_GET_PROPERTIES, true);
1262        addAction(aas, Action.CAN_UPDATE_PROPERTIES, !userReadOnly && !isReadOnly);
1263        addAction(aas, Action.CAN_MOVE_OBJECT, !userReadOnly && !isRoot);
1264        addAction(aas, Action.CAN_DELETE_OBJECT, !userReadOnly && !isReadOnly && !isRoot);
1265        addAction(aas, Action.CAN_GET_ACL, true);
1266
1267        if (file instanceof ModifiableResourceCollection)
1268        {
1269            addAction(aas, Action.CAN_GET_DESCENDANTS, true);
1270            addAction(aas, Action.CAN_GET_CHILDREN, true);
1271            addAction(aas, Action.CAN_GET_FOLDER_PARENT, !isRoot);
1272            addAction(aas, Action.CAN_GET_FOLDER_TREE, true);
1273            addAction(aas, Action.CAN_CREATE_DOCUMENT, !userReadOnly);
1274            addAction(aas, Action.CAN_CREATE_FOLDER, !userReadOnly);
1275            addAction(aas, Action.CAN_DELETE_TREE, !userReadOnly && !isReadOnly);
1276        }
1277        else if (file instanceof ModifiableResource)
1278        {
1279            ModifiableResource res = (ModifiableResource) file;
1280            addAction(aas, Action.CAN_GET_CONTENT_STREAM, res.getLength() > 0);
1281            addAction(aas, Action.CAN_SET_CONTENT_STREAM, !userReadOnly && !isReadOnly);
1282            addAction(aas, Action.CAN_DELETE_CONTENT_STREAM, !userReadOnly && !isReadOnly);
1283            addAction(aas, Action.CAN_GET_ALL_VERSIONS, true);
1284        }
1285
1286        AllowableActionsImpl result = new AllowableActionsImpl();
1287        result.setAllowableActions(aas);
1288
1289        return result;
1290    }
1291    private void addAction(Set<Action> aas, Action action, boolean condition)
1292    {
1293        if (condition)
1294        {
1295            aas.add(action);
1296        }
1297    }
1298    
1299    private Boolean isRoot(ModifiableResourceCollection object, Project project, DocumentWorkspaceModule documentModule)
1300    {
1301        ModifiableResourceCollection documentRoot = documentModule.getModuleRoot(project, false);
1302        return documentRoot.getId().equals(object.getId());
1303    }
1304    private Boolean isRoot(AmetysObject ametysObject, Project project, CmisServiceFactory factory)
1305    {
1306        if (ametysObject instanceof ModifiableResourceCollection)
1307        {
1308            return isRoot((ModifiableResourceCollection) ametysObject, project, factory.getDocumentModule());
1309        }
1310        else
1311        {
1312            return false;
1313        }
1314    }
1315    
1316    private ModifiableResourceCollection getRoot(Project project, CmisServiceFactory factory)
1317    {
1318        return factory.getDocumentModule().getModuleRoot(project, false);
1319    }
1320    
1321    
1322}