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
671            || length != null)
672        {
673            result = new PartialContentStreamImpl();
674        }
675        else
676        {
677            result = new ContentStreamImpl();
678        }
679
680        result.setFileName(res.getName());
681        result.setLength(BigInteger.valueOf(res.getLength()));
682        result.setMimeType(res.getMimeType());
683        result.setStream(stream);
684
685        return result;
686    }
687
688    /**
689     * get an object by it's path
690     * @param context call context
691     * @param project Project
692     * @param folderPath path of the object/folder
693     * @param filter filters for metadata
694     * @param includeAllowableActions allowable actions
695     * @param includeACL ACL
696     * @param objectInfos infos
697     * @param factory factory
698     * @return datas of the object
699     */
700    public ObjectData getObjectByPath(CallContext context, Project project, String folderPath, String filter,
701            boolean includeAllowableActions, boolean includeACL, ObjectInfoHandler objectInfos, CmisServiceFactory factory)
702    {
703        boolean userReadOnly = false;
704
705        // split filter
706        Set<String> filterCollection = CmisUtils.splitFilter(filter);
707
708        // check path
709        if (folderPath == null || folderPath.length() == 0 || folderPath.charAt(0) != '/')
710        {
711            throw new CmisInvalidArgumentException("Invalid folder path!");
712        }
713
714        // get the file or folder
715        AmetysObject root = getRoot(project, factory);
716        AmetysObject file = null;
717        if (folderPath.length() == 1)
718        {
719            file = root;
720        }
721        else
722        {
723            file = factory.getResolver().resolveByPath(root.getPath() + folderPath);
724            //String path = folderPath.replace('/', File.separatorChar).substring(1);
725            //file = new File(root, path);
726        }
727
728        if (file == null)
729        {
730            throw new CmisObjectNotFoundException("Path doesn't exist.");
731        }
732        return compileObjectData(context, file, project, filterCollection, includeAllowableActions, includeACL, userReadOnly,
733                objectInfos, factory);
734    }
735    
736    /**
737     * Create a new folder in another one
738     * @param context call context
739     * @param properties properties
740     * @param project Project
741     * @param folderId folder Id
742     * @param factory factory
743     * @return id of the created folder
744     */
745    public String createFolder(CallContext context, Properties properties, Project project, String folderId, CmisServiceFactory factory)
746    {
747        // check properties
748        checkNewProperties(properties, BaseTypeId.CMIS_FOLDER, factory);
749
750        // get parent File
751        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
752        if (!(ametysObject instanceof ModifiableResourceCollection))
753        {
754            throw new CmisObjectNotFoundException("Parent is not a folder!");
755        }
756        
757        // create the folder
758        String name = CmisUtils.getStringProperty(properties, PropertyIds.NAME);
759        String description = CmisUtils.getStringProperty(properties, PropertyIds.DESCRIPTION, "");
760        
761        Map<String, Object> addFolder;
762        addFolder = factory.getWorkspaceExplorerResourceDAO().addFolder(folderId, name, description, true);
763        return (String) addFolder.get("id");
764    }
765    
766    /**
767     * Create a new document in a folder
768     * @param context call context
769     * @param properties properties
770     * @param project Project
771     * @param folderId folder Id
772     * @param contentStream content Stream
773     * @param versioningState versionning State
774     * @param factory factory
775     * @return id of the created document
776     */
777    public String createDocument(CallContext context, Properties properties, Project project, String folderId,
778            ContentStream contentStream, VersioningState versioningState, CmisServiceFactory factory)
779    {
780        // check versioning state
781        if (VersioningState.NONE != versioningState)
782        {
783            throw new CmisConstraintException("Versioning not supported!");
784        }
785
786        // check properties
787        checkNewProperties(properties, BaseTypeId.CMIS_DOCUMENT, factory);
788
789        String name = CmisUtils.getStringProperty(properties, PropertyIds.NAME);
790        AmetysObject ametysObject = factory.getResolver().resolveById(folderId);
791        if (!(ametysObject instanceof ModifiableResourceCollection))
792        {
793            throw new IllegalClassException(ModifiableResourceCollection.class, ametysObject.getClass());
794        }
795        
796        factory.getAddOrUpdateResourceHelper().checkAddResourceRight((ModifiableResourceCollection) ametysObject);
797        ResourceOperationResult resourceOperation = factory.getAddOrUpdateResourceHelper().performResourceOperation(contentStream.getStream(), name, (ModifiableResourceCollection) ametysObject, 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<String>());
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}