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.ObjectInFolderData;
040import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
041import org.apache.chemistry.opencmis.commons.data.ObjectParentData;
042import org.apache.chemistry.opencmis.commons.data.Properties;
043import org.apache.chemistry.opencmis.commons.data.PropertyData;
044import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
045import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
046import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
047import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
048import org.apache.chemistry.opencmis.commons.enums.Action;
049import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
050import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl;
051import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
052import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
053import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin;
054import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions;
055import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
056import org.apache.chemistry.opencmis.commons.enums.Updatability;
057import org.apache.chemistry.opencmis.commons.enums.VersioningState;
058import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
059import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
060import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
061import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
062import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
063import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
064import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
065import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
066import org.apache.chemistry.opencmis.commons.impl.dataobjects.AllowableActionsImpl;
067import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
068import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
069import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl;
070import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl;
071import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl;
072import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl;
073import org.apache.chemistry.opencmis.commons.impl.dataobjects.PartialContentStreamImpl;
074import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
075import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
076import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
077import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
078import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
079import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
080import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl;
081import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl;
082import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
083import org.apache.chemistry.opencmis.commons.server.CallContext;
084import org.apache.chemistry.opencmis.commons.server.ObjectInfoHandler;
085import org.apache.chemistry.opencmis.commons.spi.Holder;
086import org.apache.commons.lang.IllegalClassException;
087import org.apache.commons.lang3.StringUtils;
088
089import org.ametys.core.util.URIUtils;
090import org.ametys.plugins.explorer.resources.ModifiableResource;
091import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
092import org.ametys.plugins.explorer.resources.actions.AddOrUpdateResourceHelper.ResourceOperationMode;
093import org.ametys.plugins.explorer.resources.actions.AddOrUpdateResourceHelper.ResourceOperationResult;
094import org.ametys.plugins.repository.AmetysObject;
095import org.ametys.plugins.repository.AmetysObjectIterable;
096import org.ametys.plugins.workspaces.documents.DocumentWorkspaceModule;
097import org.ametys.plugins.workspaces.project.objects.Project;
098
099/**
100 * Helper class to retreive CMIS objects
101 *
102 */
103public class CmisRepository
104{
105    private static final String USER_UNKNOWN = "<unknown>";
106    
107    /**
108     * retreive informations for a repository
109     * @param context call context
110     * @param project project
111     * @param factory factory
112     * @return RepositoryInfo
113     */
114    public RepositoryInfo getRepositoryInfo(CallContext context, Project project, CmisServiceFactory factory)
115    {
116        String id = project.getId();
117        String name = project.getName();
118        if (id == null || id.trim().length() == 0 || name == null || name.trim().length() == 0)
119        {
120            throw new CmisInvalidArgumentException("Invalid repository!");
121        }
122
123        // set up repository infos
124        return createRepositoryInfo(project, factory, CmisVersion.CMIS_1_1);
125    }
126    
127    private RepositoryInfo createRepositoryInfo(Project project, CmisServiceFactory factory, CmisVersion cmisVersion)
128    {
129        assert cmisVersion != null;
130
131        RepositoryInfoImpl repositoryInfo = new RepositoryInfoImpl();
132
133        String id = project.getId();
134        ModifiableResourceCollection documentRoot = getRoot(project, factory);
135        
136        String root = "";
137        if (documentRoot != null)
138        {
139            root = documentRoot.getId();
140        }
141        
142        id = URIUtils.encodeParameter(URIUtils.encodeParameter(id));
143        root = URIUtils.encodeParameter(URIUtils.encodeParameter(root));
144        
145        repositoryInfo.setId(id);
146        repositoryInfo.setName(project.getName());
147        repositoryInfo.setDescription(project.getDescription());
148
149        repositoryInfo.setCmisVersionSupported(cmisVersion.value());
150
151        repositoryInfo.setProductName("Ametys CMIS Server");
152        repositoryInfo.setProductVersion("1.0");
153        repositoryInfo.setVendorName("Ametys");
154
155        repositoryInfo.setRootFolder(root);
156
157        repositoryInfo.setThinClientUri("");
158        repositoryInfo.setChangesIncomplete(false);
159        RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl();
160        capabilities.setCapabilityAcl(CapabilityAcl.NONE);
161        capabilities.setAllVersionsSearchable(false);
162        capabilities.setCapabilityJoin(CapabilityJoin.NONE);
163        capabilities.setSupportsMultifiling(false);
164        capabilities.setSupportsUnfiling(false);
165        capabilities.setSupportsVersionSpecificFiling(false);
166        capabilities.setIsPwcSearchable(false);
167        capabilities.setIsPwcUpdatable(false);
168        //capabilities.setCapabilityQuery(CapabilityQuery.METADATAONLY);
169        capabilities.setCapabilityChanges(CapabilityChanges.NONE);
170        capabilities.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.ANYTIME);
171        capabilities.setSupportsGetDescendants(false);
172        capabilities.setSupportsGetFolderTree(false);
173        capabilities.setCapabilityRendition(CapabilityRenditions.NONE);
174
175        repositoryInfo.setCapabilities(capabilities);
176        
177        return repositoryInfo;
178    }
179    
180    /**
181     * getTypeDefinition
182     * @param context call context
183     * @param typeId type ID
184     * @param factory factory
185     * @return the type definition
186     */
187    public TypeDefinition getTypeDefinition(CallContext context, String typeId, CmisServiceFactory factory) 
188    {
189        //checkUser(context, false);
190
191        return factory.getTypeManager().getTypeDefinition(context, typeId);
192    }
193    
194    /**
195     * getTypeChildren
196     * @param context context
197     * @param typeId typeId
198     * @param includePropertyDefinitions includePropertyDefinitions
199     * @param maxItems maxItems
200     * @param skipCount skipCount
201     * @param factory factory
202     * @return the type definition list
203     */
204    public TypeDefinitionList getTypeChildren(CallContext context, String typeId, Boolean includePropertyDefinitions,
205            BigInteger maxItems, BigInteger skipCount, CmisServiceFactory factory) 
206    {
207        //checkUser(context, false);
208
209        return factory.getTypeManager().getTypeChildren(context, typeId, includePropertyDefinitions, maxItems, skipCount);
210    }
211    
212    /**
213     * getObject
214     * @param context context
215     * @param project Project
216     * @param objectId objectId
217     * @param versionServicesId versionServicesId
218     * @param filter filter
219     * @param includeAllowableActions includeAllowableActions
220     * @param includeAcl includeAcl
221     * @param objectInfos objectInfos
222     * @param factory factory
223     * @return ObjectData ObjectData
224     */
225    public ObjectData getObject(CallContext context, Project project, String objectId, String versionServicesId, String filter,
226            Boolean includeAllowableActions, Boolean includeAcl, ObjectInfoHandler objectInfos, 
227            CmisServiceFactory factory)
228    {
229        // check id
230        if (objectId == null && versionServicesId == null)
231        {
232            throw new CmisInvalidArgumentException("Object Id must be set.");
233        }
234        
235        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
236        
237        boolean userReadOnly = false;
238
239        // set defaults if values not set
240        boolean iaa = CmisUtils.getBooleanParameter(includeAllowableActions, false);
241        boolean iacl = false; //CmisUtils.getBooleanParameter(includeAcl, false);
242
243        // split filter
244        Set<String> filterCollection = CmisUtils.splitFilter(filter);
245
246        // gather properties
247        
248        return compileObjectData(context, ametysObject, project, filterCollection, iaa, iacl, userReadOnly, objectInfos, factory);
249    }
250    
251    
252    private ObjectData compileObjectData(CallContext context, AmetysObject ametysObject, Project project, Set<String> filter,
253            boolean includeAllowableActions, boolean includeAcl, boolean userReadOnly, ObjectInfoHandler objectInfos,
254            CmisServiceFactory factory)
255    {
256        ObjectDataImpl result = new ObjectDataImpl();
257        ObjectInfoImpl objectInfo = new ObjectInfoImpl();
258
259        result.setProperties(compileProperties(context, ametysObject, project, filter, objectInfo, factory));
260
261        if (includeAllowableActions)
262        {
263            result.setAllowableActions(compileAllowableActions(ametysObject, project, factory, userReadOnly));
264        }
265
266        if (includeAcl)
267        {
268            result.setIsExactAcl(true);
269        }
270        result.setIsExactAcl(true);
271
272        if (context.isObjectInfoRequired())
273        {
274            objectInfo.setObject(result);
275            objectInfos.addObjectInfo(objectInfo);
276        }
277
278        return result;
279    }
280    
281    private Properties compileProperties(CallContext context, AmetysObject ametysObject, Project project, Set<String> orgfilter,
282            ObjectInfoImpl objectInfo, CmisServiceFactory factory)
283    {
284        // copy filter
285        Set<String> filter = orgfilter == null ? null : new HashSet<>(orgfilter);
286
287        // find base type
288        String typeId = null;
289        Boolean isFolder = true;
290        
291        if (ametysObject != null && ametysObject instanceof ModifiableResource)
292        {
293            isFolder = false;
294            typeId = BaseTypeId.CMIS_DOCUMENT.value();
295            objectInfo.setBaseType(BaseTypeId.CMIS_DOCUMENT);
296            objectInfo.setTypeId(typeId);
297            objectInfo.setHasAcl(false);
298            objectInfo.setHasContent(true);
299            objectInfo.setHasParent(true);
300            objectInfo.setVersionSeriesId(null);
301            objectInfo.setIsCurrentVersion(true);
302            objectInfo.setRelationshipSourceIds(null);
303            objectInfo.setRelationshipTargetIds(null);
304            objectInfo.setRenditionInfos(null);
305            objectInfo.setSupportsDescendants(false);
306            objectInfo.setSupportsFolderTree(false);
307            objectInfo.setSupportsPolicies(false);
308            objectInfo.setSupportsRelationships(false);
309            objectInfo.setWorkingCopyId(null);
310            objectInfo.setWorkingCopyOriginalId(null);
311        }
312        else if (ametysObject != null && ametysObject instanceof ModifiableResourceCollection)
313        {
314            isFolder = true;
315            typeId = BaseTypeId.CMIS_FOLDER.value();
316            objectInfo.setBaseType(BaseTypeId.CMIS_FOLDER);
317            objectInfo.setTypeId(typeId);
318            objectInfo.setContentType(null);
319            objectInfo.setFileName(null);
320            objectInfo.setHasAcl(false);
321            objectInfo.setHasContent(false);
322            objectInfo.setVersionSeriesId(null);
323            objectInfo.setIsCurrentVersion(true);
324            objectInfo.setRelationshipSourceIds(null);
325            objectInfo.setRelationshipTargetIds(null);
326            objectInfo.setRenditionInfos(null);
327            objectInfo.setSupportsDescendants(true);
328            objectInfo.setSupportsFolderTree(true);
329            objectInfo.setSupportsPolicies(false);
330            objectInfo.setSupportsRelationships(false);
331            objectInfo.setWorkingCopyId(null);
332            objectInfo.setWorkingCopyOriginalId(null);
333        }
334        else
335        {
336            throw new IllegalArgumentException("Resource not found");
337        }
338        
339        try 
340        {
341            PropertiesImpl result = new PropertiesImpl();
342
343            addPropertyId(result, typeId, filter, PropertyIds.OBJECT_ID, ametysObject.getId(), factory);
344            objectInfo.setId(ametysObject.getId());
345
346            // name
347            String name = ametysObject.getName();
348            addPropertyString(result, typeId, filter, PropertyIds.NAME, name, factory);
349            objectInfo.setName(name);
350
351            if (isFolder)
352            {
353                ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
354                addPropertyString(result, typeId, filter, PropertyIds.CREATED_BY, USER_UNKNOWN, factory);
355                addPropertyString(result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, USER_UNKNOWN, factory);
356                objectInfo.setCreatedBy(USER_UNKNOWN);
357                addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, folder.getDescription(), factory);
358                
359                // base type and type name
360                addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_FOLDER.value(), factory);
361                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_FOLDER.value(), factory);
362                
363                String path = ((ModifiableResourceCollection) ametysObject).getExplorerPath();
364                ModifiableResourceCollection root = getRoot(project, factory);
365                path = path.substring(root.getExplorerPath().length());
366                if (path.length() == 0)
367                {
368                    path = "/";
369                }
370                addPropertyString(result, typeId, filter, PropertyIds.PATH, path, factory);
371
372                // folder properties
373                
374                //On évite d'appeler isRoot qui ferait un appel JCR de plus
375                if (ametysObject.equals(root))
376                {
377                    addPropertyId(result, typeId, filter, PropertyIds.PARENT_ID, null, factory);
378                    objectInfo.setHasParent(false);
379                }
380                else
381                {
382                    String parentId = ametysObject.getParent().getId();
383                    addPropertyId(result, typeId, filter, PropertyIds.PARENT_ID, parentId, factory);
384                    objectInfo.setHasParent(true);
385                }
386
387                addPropertyIdList(result, typeId, filter, PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, null, factory);
388            }
389            else
390            {
391                ModifiableResource modifiableResource = (ModifiableResource) ametysObject;
392                String author = modifiableResource.getCreator().getLogin();
393                String contributor = modifiableResource.getLastContributor().getLogin();
394                addPropertyString(result, typeId, filter, PropertyIds.CREATED_BY, author, factory);
395                addPropertyString(result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, contributor, factory);
396                addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, modifiableResource.getDCDescription(), factory);
397                objectInfo.setCreatedBy(author);
398                
399                Date lastModifiedDate = modifiableResource.getLastModified();
400                GregorianCalendar lastModified = new GregorianCalendar();
401                lastModified.setTime(lastModifiedDate);
402                Date creationDate = modifiableResource.getCreationDate();
403                GregorianCalendar creation = new GregorianCalendar();
404                creation.setTime(creationDate);
405                addPropertyDateTime(result, typeId, filter, PropertyIds.CREATION_DATE, creation, factory);
406                addPropertyDateTime(result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified, factory);
407                objectInfo.setCreationDate(creation);
408                objectInfo.setLastModificationDate(lastModified);
409                
410             // base type and type name
411                addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value(), factory);
412                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value(), factory);
413
414                // file properties
415                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_IMMUTABLE, false, factory);
416                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_VERSION, true, factory);
417                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_MAJOR_VERSION, true, factory);
418                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, true, factory);
419                addPropertyString(result, typeId, filter, PropertyIds.VERSION_LABEL, modifiableResource.getName(), factory);
420                //addPropertyId(result, typeId, filter, PropertyIds.VERSION_SERIES_ID, fileToId(file), factory);
421                addPropertyBoolean(result, typeId, filter, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, false, factory);
422                addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null, factory);
423                addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null, factory);
424                addPropertyString(result, typeId, filter, PropertyIds.CHECKIN_COMMENT, "", factory);
425                if (context.getCmisVersion() != CmisVersion.CMIS_1_0) 
426                {
427                    addPropertyBoolean(result, typeId, filter, PropertyIds.IS_PRIVATE_WORKING_COPY, false, factory);
428                }
429
430                
431                if (modifiableResource.getLength() == 0) 
432                {
433                    addPropertyBigInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, null, factory);
434                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE, null, factory);
435                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, null, factory);
436
437                    objectInfo.setHasContent(false);
438                    objectInfo.setContentType(null);
439                    objectInfo.setFileName(null);
440                }
441                else 
442                {
443                    
444                    addPropertyInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, modifiableResource.getLength(), factory);
445                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE,
446                            modifiableResource.getMimeType(), factory);
447                    addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, modifiableResource.getName(), factory);
448
449                    objectInfo.setHasContent(true);
450                    objectInfo.setContentType(modifiableResource.getMimeType());
451                    objectInfo.setFileName(modifiableResource.getName());
452                }
453
454                addPropertyId(result, typeId, filter, PropertyIds.CONTENT_STREAM_ID, null, factory);
455            }
456
457            
458
459            // change token - always null
460            addPropertyString(result, typeId, filter, PropertyIds.CHANGE_TOKEN, null, factory);
461
462            // CMIS 1.1 properties
463            if (context.getCmisVersion() != CmisVersion.CMIS_1_0)
464            {
465                addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, null, factory);
466                addPropertyIdList(result, typeId, filter, PropertyIds.SECONDARY_OBJECT_TYPE_IDS, null, factory);
467            }
468
469            return result;
470        }
471        catch (CmisBaseException cbe)
472        {
473            throw cbe;
474        }
475        catch (Exception e)
476        {
477            throw new CmisRuntimeException(e.getMessage(), e);
478        }
479    }
480    
481    /**
482     * get children of a folder
483     * @param context call context
484     * @param folderId folder Id
485     * @param project Project
486     * @param filter filters
487     * @param includeAllowableActions allowable actions
488     * @param includePathSegment include path segment
489     * @param maxItems max items
490     * @param skipCount skip count
491     * @param objectInfos object infos
492     * @param factory factory
493     * @return ObjectInFolderList
494     */
495    public ObjectInFolderList getChildren(CallContext context, String folderId, Project project, String filter,
496            Boolean includeAllowableActions, Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount,
497            ObjectInfoHandler objectInfos, CmisServiceFactory factory)
498    {
499        boolean userReadOnly = false;
500        
501        // split filter
502        Set<String> filterCollection = CmisUtils.splitFilter(filter);
503
504        // set defaults if values not set
505        boolean iaa = CmisUtils.getBooleanParameter(includeAllowableActions, false);
506        boolean ips = CmisUtils.getBooleanParameter(includePathSegment, false);
507
508        // skip and max
509        int skip = skipCount == null ? 0 : skipCount.intValue();
510        if (skip < 0)
511        {
512            skip = 0;
513        }
514
515        int max = maxItems == null ? Integer.MAX_VALUE : maxItems.intValue();
516        if (max < 0)
517        {
518            max = Integer.MAX_VALUE;
519        }
520
521        // get the folder
522        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
523        if (!(ametysObject instanceof ModifiableResourceCollection)) 
524        {
525            throw new CmisObjectNotFoundException("Not a folder!");
526        }
527        ModifiableResourceCollection folder = (ModifiableResourceCollection) ametysObject;
528
529        // set object info of the the folder
530        if (context.isObjectInfoRequired())
531        {
532            compileObjectData(context, ametysObject, project, null, false, false, userReadOnly, objectInfos, factory);
533        }
534
535        // prepare result
536        ObjectInFolderListImpl result = new ObjectInFolderListImpl();
537        result.setObjects(new ArrayList<ObjectInFolderData>());
538        result.setHasMoreItems(false);
539        int count = 0;
540
541        // iterate through children
542        AmetysObjectIterable<AmetysObject> children = folder.getChildren();
543        for (AmetysObject child : children)
544        //for (File child : folder.listFiles())
545        {
546            // skip nodes other than ModifiableResource or ModifiableResourceCollection
547            if (!(child instanceof ModifiableResource || child instanceof ModifiableResourceCollection))
548            {
549                continue;
550            }
551
552            count++;
553
554            if (skip > 0)
555            {
556                skip--;
557                continue;
558            }
559
560            if (result.getObjects().size() >= max)
561            {
562                result.setHasMoreItems(true);
563                continue;
564            }
565
566            // build and add child object
567            ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
568            objectInFolder.setObject(compileObjectData(context, child, project, filterCollection, iaa, false, userReadOnly,
569                    objectInfos, factory));
570            if (ips)
571            {
572                objectInFolder.setPathSegment(child.getName());
573            }
574
575            result.getObjects().add(objectInFolder);
576        }
577
578        result.setNumItems(BigInteger.valueOf(count));
579
580        return result;
581    }
582    
583    /**
584     * get the parents of an object
585     * @param context call context
586     * @param objectId Object Id
587     * @param project Project
588     * @param filter filters
589     * @param includeAllowableActions allowable actions
590     * @param includeRelativePathSegment relative path segment
591     * @param objectInfos object infos
592     * @param factory factory 
593     * @return List of ObjectParentData
594     */
595    public List<ObjectParentData> getObjectParents(CallContext context, String objectId, Project project, String filter,
596            Boolean includeAllowableActions, Boolean includeRelativePathSegment, ObjectInfoHandler objectInfos, CmisServiceFactory factory)
597    {
598        boolean userReadOnly = false;
599
600        // split filter
601        Set<String> filterCollection = CmisUtils.splitFilter(filter);
602
603        // set defaults if values not set
604        boolean iaa = CmisUtils.getBooleanParameter(includeAllowableActions, false);
605        boolean irps = CmisUtils.getBooleanParameter(includeRelativePathSegment, false);
606
607        // get the file or folder
608        //File file = getFile(objectId);
609
610        // don't climb above the root folder
611        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
612        if (isRoot(ametysObject, project, factory))
613        {
614            return Collections.emptyList();
615        }
616
617        // set object info of the the object
618        if (context.isObjectInfoRequired())
619        {
620            compileObjectData(context, ametysObject, project, null, false, false, userReadOnly, objectInfos, factory);
621        }
622
623        // get parent folder
624        AmetysObject parent = ametysObject.getParent();
625        //File parent = file.getParentFile();
626        ObjectData object = compileObjectData(context, parent, project, filterCollection, iaa, false, userReadOnly, objectInfos, factory);
627
628        ObjectParentDataImpl result = new ObjectParentDataImpl();
629        result.setObject(object);
630        if (irps)
631        {
632            result.setRelativePathSegment(ametysObject.getName());
633        }
634
635        return Collections.<ObjectParentData> singletonList(result);
636    }
637    /**
638     * get the inputstream to read a file
639     * @param context call context
640     * @param objectId object Id
641     * @param offset offset
642     * @param length lenght
643     * @param factory factory
644     * @return ContentStream
645     */
646    public ContentStream getContentStream(CallContext context, String objectId, BigInteger offset, BigInteger length, CmisServiceFactory factory)
647    {
648        // get the file
649        AmetysObject ametysObject = factory.getResolver().resolveById(objectId);
650        if (!(ametysObject instanceof ModifiableResource))
651        {
652            throw new CmisStreamNotSupportedException("Not a file!");
653        }
654        ModifiableResource res = (ModifiableResource) ametysObject;
655        
656        if (res.getLength() == 0)
657        {
658            throw new CmisConstraintException("Document has no content!");
659        }
660
661        InputStream stream = null;
662        stream = res.getInputStream();
663        if (offset != null || length != null)
664        {
665            stream = new CmisContentRangeInputStream(stream, offset, length);
666        }
667
668        // compile data
669        ContentStreamImpl result;
670        if ((offset != null && offset.longValue() > 0) || 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<String>());
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}