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