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.lang3.StringUtils;
086
087import org.ametys.core.util.URIUtils;
088import org.ametys.plugins.explorer.resources.ModifiableResource;
089import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
090import org.ametys.plugins.explorer.resources.ResourceCollection;
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        ResourceCollection collection = factory.getResolver().resolveById(folderId);
790        if (!(collection instanceof ModifiableResourceCollection))
791        {
792            throw new IllegalArgumentException("Cannot create file on a non modifiable folder '" + folderId + "'");
793        }
794        
795        ModifiableResourceCollection mCollection = (ModifiableResourceCollection) collection;
796        factory.getAddOrUpdateResourceHelper().checkAddResourceRight(mCollection);
797        ResourceOperationResult resourceOperation = factory.getAddOrUpdateResourceHelper().performResourceOperation(contentStream.getStream(), name, mCollection, ResourceOperationMode.ADD_RENAME);
798        if (!resourceOperation.isSuccess())
799        {
800            throw new CmisStorageException("Could not create file: " + resourceOperation.getErrorMessage());
801        }
802
803        String id = resourceOperation.getResource().getId();
804        return id;
805    }
806    
807    /**
808     * CMIS deleteObject.
809     * @param context call context
810     * @param objectId object ID
811     * @param factory factory
812     */
813    public void deleteObject(CallContext context, String objectId, CmisServiceFactory factory)
814    {
815        List<String> toDelete = new ArrayList<>(1);
816        toDelete.add(objectId);
817        Map deleted = factory.getWorkspaceExplorerResourceDAO().deleteObject(toDelete);
818        
819        if (deleted.containsKey("message"))
820        {
821            throw new CmisStorageException("Deletion failed : " + deleted.get("message"));
822        }
823    }
824    
825    /**
826     * CMIS getFolderParent.
827     * @param context call context
828     * @param project Project
829     * @param folderId folder Id
830     * @param filter filters
831     * @param objectInfos objectInfos
832     * @param factory factory
833     * @return ObjectData
834     */
835    public ObjectData getFolderParent(CallContext context, Project project, String folderId, String filter, ObjectInfoHandler objectInfos, CmisServiceFactory factory)
836    {
837        boolean userReadOnly = false;
838        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
839        if (isRoot(ametysObject, project, factory))
840        {
841            throw new CmisInvalidArgumentException("The root folder has no parent!");
842        }
843        AmetysObject parent = ametysObject.getParent();
844        
845        Set<String> filterCollection = CmisUtils.splitFilter(filter);
846        ObjectData object = compileObjectData(context, parent, project, filterCollection, false, false, userReadOnly, objectInfos, factory);
847        
848        return object;
849    }
850    
851    /**
852     * CMIS deleteTree.
853     * @param context call context
854     * @param project Project
855     * @param folderId folder Id
856     * @param factory factory
857     * @return FailedToDeleteData
858     */
859    public FailedToDeleteData deleteTree(CallContext context, Project project, String folderId, CmisServiceFactory factory)
860    {
861        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
862        if (ametysObject instanceof ModifiableResourceCollection)
863        {
864            ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
865            AmetysObjectIterable<AmetysObject> children = folder.getChildren();
866            List<String> toDelete = new ArrayList<>();
867            for (AmetysObject child : children)
868            {
869                toDelete.add(child.getId());
870                
871            }
872            Map deletedMsg = factory.getWorkspaceExplorerResourceDAO().deleteObject(toDelete);
873            
874            if (deletedMsg.containsKey("message"))
875            {
876                throw new CmisStorageException("Deletion failed : " + deletedMsg.get("message"));
877            }
878        }
879        else
880        {
881            throw new CmisConstraintException("Object is not a folder!");
882        }
883        
884        FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
885        result.setIds(new ArrayList<>());
886
887        return result;
888    }
889    
890    /**
891     * CMIS updateProperties.
892     * @param context call context
893     * @param project Project
894     * @param objectIdHolder objectId in a holder
895     * @param properties properties
896     * @param objectInfos object infos
897     * @param factory factory
898     * @return ObjectData
899     */
900    public ObjectData updateProperties(CallContext context, Project project, Holder<String> objectIdHolder, Properties properties,
901            ObjectInfoHandler objectInfos, CmisServiceFactory factory)
902    {
903        // check object id
904        if (objectIdHolder == null || objectIdHolder.getValue() == null)
905        {
906            throw new CmisInvalidArgumentException("Id is not valid!");
907        }
908        String objectId = objectIdHolder.getValue();
909        boolean userReadOnly = false;
910        
911        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
912
913        if (ametysObject != null && ametysObject instanceof ModifiableResource)
914        {
915            ModifiableResource resource = (ModifiableResource) ametysObject;
916            String inputName = CmisUtils.getStringProperty(properties, PropertyIds.NAME, resource.getName());
917            String description = CmisUtils.getStringProperty(properties, PropertyIds.DESCRIPTION, resource.getDCDescription());
918            Collection<String> tags =  new ArrayList<>(Arrays.asList(resource.getKeywords()));
919            
920            factory.getWorkspaceExplorerResourceDAO().editFile(objectId, inputName, description, tags);
921            
922        }
923        else if (ametysObject != null && ametysObject instanceof ModifiableResourceCollection)
924        {
925            ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
926            String inputName = CmisUtils.getStringProperty(properties, PropertyIds.NAME, folder.getName());
927            String description = CmisUtils.getStringProperty(properties, PropertyIds.DESCRIPTION, folder.getDescription());
928            if (description == null)
929            {
930                description = StringUtils.EMPTY;
931            }
932            factory.getWorkspaceExplorerResourceDAO().editFolder(objectId, inputName, description);
933        }
934        else
935        {
936            throw new CmisObjectNotFoundException("File not found!");
937        }
938        
939        AmetysObject modified = factory.getResolver().resolveById(objectId);
940        return compileObjectData(context, modified, project, null, false, false, userReadOnly, objectInfos, factory);
941    }
942    
943    /**
944     * CMIS setContentStream, deleteContentStream, and appendContentStream.
945     * @param context call context
946     * @param project Project
947     * @param objectIdHolder object Id in a holder
948     * @param overwriteFlag overwrite
949     * @param contentStream inputStream
950     * @param append append
951     * @param factory factory
952     */
953    public void changeContentStream(CallContext context, Project project, Holder<String> objectIdHolder, Boolean overwriteFlag,
954            ContentStream contentStream, boolean append, CmisServiceFactory factory)
955    {
956        if (objectIdHolder == null || objectIdHolder.getValue() == null)
957        {
958            throw new CmisInvalidArgumentException("Id is not valid!");
959        }
960        String objectId = objectIdHolder.getValue();
961        
962        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
963        
964     // check overwrite
965        boolean owf = CmisUtils.getBooleanParameter(overwriteFlag, true);
966        if (!owf && contentStream.getLength() > 0)
967        {
968            throw new CmisContentAlreadyExistsException("Content already exists!");
969        }
970        
971        if (ametysObject instanceof ModifiableResource)
972        {
973            factory.getAddOrUpdateResourceHelper().checkAddResourceRight((ModifiableResourceCollection) ametysObject);
974            
975            String name = ametysObject.getName();
976            ResourceOperationResult resourceOperation = factory.getAddOrUpdateResourceHelper().performResourceOperation(contentStream.getStream(), name, ametysObject.getParent(), ResourceOperationMode.UPDATE);
977                
978            if (!resourceOperation.isSuccess())
979            {
980                throw new CmisRuntimeException("Impossible to update file : " + resourceOperation.getErrorMessage());
981            }
982        }
983        else
984        {
985            throw new CmisObjectNotFoundException("File not found!");
986        }
987    }
988    /**
989     * CMIS moveObject.
990     * @param context call context
991     * @param project Project
992     * @param objectId objectId in a holder
993     * @param targetFolderId folderId
994     * @param objectInfos objectInfos
995     * @param factory factory
996     * @return ObjectData
997     */
998    public ObjectData moveObject(CallContext context, Project project, Holder<String> objectId, String targetFolderId,
999            ObjectInfoHandler objectInfos, CmisServiceFactory factory)
1000    {
1001        boolean userReadOnly = false;
1002
1003        if (objectId == null)
1004        {
1005            throw new CmisInvalidArgumentException("Id is not valid!");
1006        }
1007
1008        List<String> documentIds = new ArrayList<>(1);
1009        documentIds.add(objectId.getValue());
1010        
1011        Map<String, Object> result;
1012        try
1013        {
1014            result = factory.getWorkspaceExplorerResourceDAO().moveDocuments(documentIds, targetFolderId);
1015            if (result.containsKey("message"))
1016            {
1017                throw new CmisStorageException((String) result.get("message"));
1018            }
1019            else
1020            {
1021                AmetysObject ametysObject = factory.getResolver().resolveById(objectId.getValue());
1022                return compileObjectData(context, ametysObject, project, null, false, false, userReadOnly, objectInfos, factory);
1023            }
1024        }
1025        catch (RepositoryException e)
1026        {
1027            throw new CmisStorageException("Impossible to move file", e);
1028        }
1029        
1030        
1031    }
1032    
1033    /*
1034     * private helpers
1035     * 
1036     */
1037    
1038    /*
1039     * Checks a property set for a new object.
1040     */
1041    private void checkNewProperties(Properties properties, BaseTypeId baseTypeId, CmisServiceFactory factory)
1042    {
1043        // check properties
1044        if (properties == null || properties.getProperties() == null)
1045        {
1046            throw new CmisInvalidArgumentException("Properties must be set!");
1047        }
1048
1049        // check the name
1050        /*
1051        String name = CmisUtils.getStringProperty(properties, PropertyIds.NAME);
1052        if (!isValidName(name)) {
1053            throw new CmisNameConstraintViolationException("Name is not valid!");
1054        }*/
1055
1056        // check the type
1057        String typeId = CmisUtils.getObjectTypeId(properties);
1058        if (typeId == null)
1059        {
1060            throw new CmisInvalidArgumentException("Type Id is not set!");
1061        }
1062
1063        TypeDefinition type = factory.getTypeManager().getInternalTypeDefinition(typeId);
1064        if (type == null)
1065        {
1066            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
1067        }
1068
1069        if (type.getBaseTypeId() != baseTypeId)
1070        {
1071            if (baseTypeId == BaseTypeId.CMIS_DOCUMENT)
1072            {
1073                throw new CmisInvalidArgumentException("Type is not a document type!");
1074            }
1075            else if (baseTypeId == BaseTypeId.CMIS_DOCUMENT)
1076            {
1077                throw new CmisInvalidArgumentException("Type is not a folder type!");
1078            }
1079            else
1080            {
1081                throw new CmisRuntimeException("A file system does not support a " + baseTypeId.value() + " type!");
1082            }
1083        }
1084
1085        // check type properties
1086        checkTypeProperties(properties, typeId, factory, true);
1087
1088        // check if required properties are missing
1089        for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values())
1090        {
1091            if (propDef.isRequired() && !properties.getProperties().containsKey(propDef.getId())
1092                    && propDef.getUpdatability() != Updatability.READONLY)
1093            {
1094                throw new CmisConstraintException("Property '" + propDef.getId() + "' is required!");
1095            }
1096        }
1097    }
1098    
1099    /*
1100     * Checks if the property belong to the type and are settable.
1101     */
1102    private void checkTypeProperties(Properties properties, String typeId, CmisServiceFactory factory, boolean isCreate)
1103    {
1104        // check type
1105        TypeDefinition type = factory.getTypeManager().getInternalTypeDefinition(typeId);
1106        if (type == null)
1107        {
1108            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
1109        }
1110
1111        // check if all required properties are there
1112        for (PropertyData<?> prop : properties.getProperties().values())
1113        {
1114            PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
1115
1116            // do we know that property?
1117            if (propType == null)
1118            {
1119                throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
1120            }
1121
1122            // can it be set?
1123            if (propType.getUpdatability() == Updatability.READONLY)
1124            {
1125                throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
1126            }
1127
1128            if (!isCreate)
1129            {
1130                // can it be set?
1131                if (propType.getUpdatability() == Updatability.ONCREATE)
1132                {
1133                    throw new CmisConstraintException("Property '" + prop.getId() + "' cannot be updated!");
1134                }
1135            }
1136        }
1137    }
1138    
1139    private void addPropertyId(PropertiesImpl props, String typeId, Set<String> filter, String id, String value, CmisServiceFactory factory)
1140    {
1141        if (!checkAddProperty(props, typeId, filter, id, factory))
1142        {
1143            return;
1144        }
1145
1146        props.addProperty(new PropertyIdImpl(id, value));
1147    }
1148
1149    private void addPropertyIdList(PropertiesImpl props, String typeId, Set<String> filter, String id,
1150            List<String> value, CmisServiceFactory factory)
1151    {
1152        if (!checkAddProperty(props, typeId, filter, id, factory))
1153        {
1154            return;
1155        }
1156
1157        props.addProperty(new PropertyIdImpl(id, value));
1158    }
1159
1160    private void addPropertyString(PropertiesImpl props, String typeId, Set<String> filter, String id, String value, CmisServiceFactory factory)
1161    {
1162        if (!checkAddProperty(props, typeId, filter, id, factory))
1163        {
1164            return;
1165        }
1166
1167        props.addProperty(new PropertyStringImpl(id, value));
1168    }
1169
1170    private void addPropertyInteger(PropertiesImpl props, String typeId, Set<String> filter, String id, long value, CmisServiceFactory factory)
1171    {
1172        addPropertyBigInteger(props, typeId, filter, id, BigInteger.valueOf(value), factory);
1173    }
1174
1175    private void addPropertyBigInteger(PropertiesImpl props, String typeId, Set<String> filter, String id,
1176            BigInteger value, CmisServiceFactory factory)
1177    {
1178        if (!checkAddProperty(props, typeId, filter, id, factory))
1179        {
1180            return;
1181        }
1182
1183        props.addProperty(new PropertyIntegerImpl(id, value));
1184    }
1185
1186    private void addPropertyBoolean(PropertiesImpl props, String typeId, Set<String> filter, String id, boolean value, CmisServiceFactory factory)
1187    {
1188        if (!checkAddProperty(props, typeId, filter, id, factory))
1189        {
1190            return;
1191        }
1192
1193        props.addProperty(new PropertyBooleanImpl(id, value));
1194    }
1195
1196    private void addPropertyDateTime(PropertiesImpl props, String typeId, Set<String> filter, String id,
1197            GregorianCalendar value, CmisServiceFactory factory)
1198    {
1199        if (!checkAddProperty(props, typeId, filter, id, factory))
1200        {
1201            return;
1202        }
1203
1204        props.addProperty(new PropertyDateTimeImpl(id, value));
1205    }
1206
1207    private boolean checkAddProperty(Properties properties, String typeId, Set<String> filter, String id, CmisServiceFactory factory)
1208    {
1209        if (properties == null || properties.getProperties() == null)
1210        {
1211            throw new IllegalArgumentException("Properties must not be null!");
1212        }
1213
1214        if (id == null)
1215        {
1216            throw new IllegalArgumentException("Id must not be null!");
1217        }
1218
1219        TypeDefinition type = factory.getTypeManager().getInternalTypeDefinition(typeId);
1220        if (type == null)
1221        {
1222            throw new IllegalArgumentException("Unknown type: " + typeId);
1223        }
1224        if (!type.getPropertyDefinitions().containsKey(id))
1225        {
1226            throw new IllegalArgumentException("Unknown property: " + id);
1227        }
1228
1229        String queryName = type.getPropertyDefinitions().get(id).getQueryName();
1230
1231        if (queryName != null && filter != null)
1232        {
1233            if (!filter.contains(queryName))
1234            {
1235                return false;
1236            }
1237            else
1238            {
1239                filter.remove(queryName);
1240            }
1241        }
1242
1243        return true;
1244    }
1245    
1246    /*
1247     * Compiles the allowable actions for a file or folder.
1248     */
1249    private AllowableActions compileAllowableActions(AmetysObject file, Project project, CmisServiceFactory factory, boolean userReadOnly)
1250    {
1251        if (file == null)
1252        {
1253            throw new IllegalArgumentException("File must not be null!");
1254        }
1255
1256        boolean isReadOnly = false;
1257        boolean isRoot = isRoot(file, project, factory);
1258
1259        Set<Action> aas = EnumSet.noneOf(Action.class);
1260
1261        addAction(aas, Action.CAN_GET_OBJECT_PARENTS, !isRoot);
1262        addAction(aas, Action.CAN_GET_PROPERTIES, true);
1263        addAction(aas, Action.CAN_UPDATE_PROPERTIES, !userReadOnly && !isReadOnly);
1264        addAction(aas, Action.CAN_MOVE_OBJECT, !userReadOnly && !isRoot);
1265        addAction(aas, Action.CAN_DELETE_OBJECT, !userReadOnly && !isReadOnly && !isRoot);
1266        addAction(aas, Action.CAN_GET_ACL, true);
1267
1268        if (file instanceof ModifiableResourceCollection)
1269        {
1270            addAction(aas, Action.CAN_GET_DESCENDANTS, true);
1271            addAction(aas, Action.CAN_GET_CHILDREN, true);
1272            addAction(aas, Action.CAN_GET_FOLDER_PARENT, !isRoot);
1273            addAction(aas, Action.CAN_GET_FOLDER_TREE, true);
1274            addAction(aas, Action.CAN_CREATE_DOCUMENT, !userReadOnly);
1275            addAction(aas, Action.CAN_CREATE_FOLDER, !userReadOnly);
1276            addAction(aas, Action.CAN_DELETE_TREE, !userReadOnly && !isReadOnly);
1277        }
1278        else if (file instanceof ModifiableResource)
1279        {
1280            ModifiableResource res = (ModifiableResource) file;
1281            addAction(aas, Action.CAN_GET_CONTENT_STREAM, res.getLength() > 0);
1282            addAction(aas, Action.CAN_SET_CONTENT_STREAM, !userReadOnly && !isReadOnly);
1283            addAction(aas, Action.CAN_DELETE_CONTENT_STREAM, !userReadOnly && !isReadOnly);
1284            addAction(aas, Action.CAN_GET_ALL_VERSIONS, true);
1285        }
1286
1287        AllowableActionsImpl result = new AllowableActionsImpl();
1288        result.setAllowableActions(aas);
1289
1290        return result;
1291    }
1292    private void addAction(Set<Action> aas, Action action, boolean condition)
1293    {
1294        if (condition)
1295        {
1296            aas.add(action);
1297        }
1298    }
1299    
1300    private Boolean isRoot(ModifiableResourceCollection object, Project project, DocumentWorkspaceModule documentModule)
1301    {
1302        ModifiableResourceCollection documentRoot = documentModule.getModuleRoot(project, false);
1303        return documentRoot.getId().equals(object.getId());
1304    }
1305    private Boolean isRoot(AmetysObject ametysObject, Project project, CmisServiceFactory factory)
1306    {
1307        if (ametysObject instanceof ModifiableResourceCollection)
1308        {
1309            return isRoot((ModifiableResourceCollection) ametysObject, project, factory.getDocumentModule());
1310        }
1311        else
1312        {
1313            return false;
1314        }
1315    }
1316    
1317    private ModifiableResourceCollection getRoot(Project project, CmisServiceFactory factory)
1318    {
1319        return factory.getDocumentModule().getModuleRoot(project, false);
1320    }
1321    
1322    
1323}