1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.plugins.document.modules.cmis.service;
35
36 import fr.paris.lutece.plugins.document.business.Document;
37 import fr.paris.lutece.plugins.document.business.spaces.DocumentSpace;
38 import fr.paris.lutece.portal.service.util.AppLogService;
39
40 import org.apache.chemistry.opencmis.commons.PropertyIds;
41 import org.apache.chemistry.opencmis.commons.data.*;
42 import org.apache.chemistry.opencmis.commons.definitions.PermissionDefinition;
43 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
44 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
45 import org.apache.chemistry.opencmis.commons.enums.*;
46 import org.apache.chemistry.opencmis.commons.exceptions.*;
47 import org.apache.chemistry.opencmis.commons.impl.dataobjects.*;
48 import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
49 import org.apache.chemistry.opencmis.commons.server.CallContext;
50 import org.apache.chemistry.opencmis.commons.server.ObjectInfoHandler;
51
52 import org.apache.tika.mime.MimeTypes;
53
54 import java.io.*;
55
56 import java.math.BigInteger;
57
58 import java.util.*;
59
60
61
62
63
64 public class DocumentRepository extends BaseRepository
65 {
66 private static final String REPOSITORY_ID = "document";
67 private static final String CMIS_VERSION = "1.0";
68 private static final String PRODUCT_NAME = "CMIS document module";
69 private static final String PRODUCT_VERSION = "1.0.0";
70 private static final String VENDOR_NAME = "Lutece";
71 private static final String ROOT_ID = "@root@";
72 private static final String CREATED_BY = "Lutece";
73 private static final String CMIS_READ = "cmis:read";
74 private static final String CMIS_WRITE = "cmis:write";
75 private static final String CMIS_ALL = "cmis:all";
76 private static final String ROOT_SPACE_ID = "S0";
77 private static final String PREFIX_DOC = "D";
78 private static final String PREFIX_SPACE = "S";
79 private static final String MIME_TYPE_XML = MimeTypes.PLAIN_TEXT;
80
81
82
83
84 private final TypeManager types = new TypeManager( );
85
86
87
88
89
90 public RepositoryInfo getInfos( )
91 {
92 RepositoryInfoImpl repositoryInfo = new RepositoryInfoImpl( );
93
94 repositoryInfo.setId( REPOSITORY_ID );
95 repositoryInfo.setName( REPOSITORY_ID );
96 repositoryInfo.setDescription( REPOSITORY_ID );
97
98 repositoryInfo.setCmisVersionSupported( CMIS_VERSION );
99
100 repositoryInfo.setProductName( PRODUCT_NAME );
101 repositoryInfo.setProductVersion( PRODUCT_VERSION );
102 repositoryInfo.setVendorName( VENDOR_NAME );
103
104 repositoryInfo.setRootFolder( ROOT_ID );
105
106 repositoryInfo.setThinClientUri( "" );
107
108 RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl( );
109 capabilities.setCapabilityAcl( CapabilityAcl.DISCOVER );
110 capabilities.setAllVersionsSearchable( false );
111 capabilities.setCapabilityJoin( CapabilityJoin.NONE );
112 capabilities.setSupportsMultifiling( false );
113 capabilities.setSupportsUnfiling( false );
114 capabilities.setSupportsVersionSpecificFiling( false );
115 capabilities.setIsPwcSearchable( false );
116 capabilities.setIsPwcUpdatable( false );
117 capabilities.setCapabilityQuery( CapabilityQuery.NONE );
118 capabilities.setCapabilityChanges( CapabilityChanges.NONE );
119 capabilities.setCapabilityContentStreamUpdates( CapabilityContentStreamUpdates.ANYTIME );
120 capabilities.setSupportsGetDescendants( true );
121 capabilities.setSupportsGetFolderTree( true );
122 capabilities.setCapabilityRendition( CapabilityRenditions.NONE );
123
124 repositoryInfo.setCapabilities( capabilities );
125
126 AclCapabilitiesDataImpl aclCapability = new AclCapabilitiesDataImpl( );
127 aclCapability.setSupportedPermissions( SupportedPermissions.BASIC );
128 aclCapability.setAclPropagation( AclPropagation.OBJECTONLY );
129
130
131 List<PermissionDefinition> permissions = new ArrayList<PermissionDefinition>( );
132 permissions.add( createPermission( CMIS_READ, "Read" ) );
133 permissions.add( createPermission( CMIS_WRITE, "Write" ) );
134 permissions.add( createPermission( CMIS_ALL, "All" ) );
135 aclCapability.setPermissionDefinitionData( permissions );
136
137
138 List<PermissionMapping> list = new ArrayList<PermissionMapping>( );
139 list.add( createMapping( PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER, CMIS_READ ) );
140 list.add( createMapping( PermissionMapping.CAN_CREATE_FOLDER_FOLDER, CMIS_READ ) );
141 list.add( createMapping( PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT, CMIS_WRITE ) );
142 list.add( createMapping( PermissionMapping.CAN_DELETE_OBJECT, CMIS_ALL ) );
143 list.add( createMapping( PermissionMapping.CAN_DELETE_TREE_FOLDER, CMIS_ALL ) );
144 list.add( createMapping( PermissionMapping.CAN_GET_ACL_OBJECT, CMIS_READ ) );
145 list.add( createMapping( PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES, CMIS_READ ) );
146 list.add( createMapping( PermissionMapping.CAN_GET_CHILDREN_FOLDER, CMIS_READ ) );
147 list.add( createMapping( PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, CMIS_READ ) );
148 list.add( createMapping( PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT, CMIS_READ ) );
149 list.add( createMapping( PermissionMapping.CAN_GET_PARENTS_FOLDER, CMIS_READ ) );
150 list.add( createMapping( PermissionMapping.CAN_GET_PROPERTIES_OBJECT, CMIS_READ ) );
151 list.add( createMapping( PermissionMapping.CAN_MOVE_OBJECT, CMIS_WRITE ) );
152 list.add( createMapping( PermissionMapping.CAN_MOVE_SOURCE, CMIS_READ ) );
153 list.add( createMapping( PermissionMapping.CAN_MOVE_TARGET, CMIS_WRITE ) );
154 list.add( createMapping( PermissionMapping.CAN_SET_CONTENT_DOCUMENT, CMIS_WRITE ) );
155 list.add( createMapping( PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, CMIS_WRITE ) );
156 list.add( createMapping( PermissionMapping.CAN_VIEW_CONTENT_OBJECT, CMIS_READ ) );
157
158 Map<String, PermissionMapping> map = new LinkedHashMap<String, PermissionMapping>( );
159
160 for ( PermissionMapping pm : list )
161 {
162 map.put( pm.getKey( ), pm );
163 }
164
165 aclCapability.setPermissionMappingData( map );
166
167 repositoryInfo.setAclCapabilities( aclCapability );
168
169 return repositoryInfo;
170 }
171
172
173
174
175
176
177
178
179
180
181
182 public TypeDefinitionList getTypesChildren( CallContext context, String typeId, boolean includePropertyDefinitions,
183 BigInteger maxItems, BigInteger skipCount )
184 {
185 return types.getTypesChildren( context, typeId, includePropertyDefinitions, maxItems, skipCount );
186 }
187
188
189
190
191
192
193
194
195 public TypeDefinition getTypeDefinition( CallContext context, String typeId )
196 {
197 return types.getTypeDefinition( context, typeId );
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 public ObjectInFolderList getChildren( CallContext context, String folderId, String filter, String orderBy,
217 Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
218 Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension,
219 ObjectInfoHandler objectInfos )
220 {
221 ObjectInFolderListImpl result = new ObjectInFolderListImpl( );
222 result.setObjects( new ArrayList<ObjectInFolderData>( ) );
223 result.setHasMoreItems( false );
224
225 int count = 0;
226
227
228 int skip = ( ( skipCount == null ) ? 0 : skipCount.intValue( ) );
229
230 if ( skip < 0 )
231 {
232 skip = 0;
233 }
234
235 int max = ( ( maxItems == null ) ? Integer.MAX_VALUE : maxItems.intValue( ) );
236
237 if ( max < 0 )
238 {
239 max = Integer.MAX_VALUE;
240 }
241
242 if ( folderId.equalsIgnoreCase( ROOT_ID ) )
243 {
244 folderId = ROOT_SPACE_ID;
245 }
246
247 RepositoryObject object = new RepositoryObject( folderId );
248
249 if ( object.isDocument( ) )
250 {
251 return result;
252 }
253
254 String folderPath = object.getName( );
255
256 List<Document> listDocuments = object.getDocumentChildren( );
257
258
259 for ( Document document : listDocuments )
260 {
261
262 if ( document.isOutOfDate( ) || !document.isValid( ) )
263 {
264 continue;
265 }
266
267 count++;
268
269 if ( skip > 0 )
270 {
271 skip--;
272
273 continue;
274 }
275
276 if ( result.getObjects( ).size( ) >= max )
277 {
278 result.setHasMoreItems( true );
279
280 continue;
281 }
282
283
284 ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl( );
285 objectInFolder.setObject( getObject( context, PREFIX_DOC + document.getId( ), filter,
286 includeAllowableActions, includeRelationships, renditionFilter, includePathSegment,
287 includePathSegment, extension, objectInfos, folderPath ) );
288
289 result.getObjects( ).add( objectInFolder );
290 }
291
292 List<DocumentSpace> listSpaces = object.getSpaceChildren( );
293
294
295 for ( DocumentSpace space : listSpaces )
296 {
297 count++;
298
299 if ( skip > 0 )
300 {
301 skip--;
302
303 continue;
304 }
305
306 if ( result.getObjects( ).size( ) >= max )
307 {
308 result.setHasMoreItems( true );
309
310 continue;
311 }
312
313
314 ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl( );
315 objectInFolder.setObject( getObject( context, PREFIX_SPACE + space.getId( ), filter,
316 includeAllowableActions, includeRelationships, renditionFilter, includePathSegment,
317 includePathSegment, extension, objectInfos, folderPath ) );
318
319 result.getObjects( ).add( objectInFolder );
320 }
321
322 result.setNumItems( BigInteger.valueOf( count ) );
323
324 return result;
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342 public ObjectData getObject( CallContext context, String objectId, String filter, Boolean includeAllowableActions,
343 IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds,
344 Boolean includeAcl, ExtensionsData extension, ObjectInfoHandler objectInfo )
345 {
346 return getObject( context, objectId, filter, includeAllowableActions, includeRelationships, renditionFilter,
347 includePolicyIds, includeAcl, extension, objectInfo, "/" );
348 }
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366 private ObjectData getObject( CallContext context, String objectId, String filter, Boolean includeAllowableActions,
367 IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds,
368 Boolean includeAcl, ExtensionsData extension, ObjectInfoHandler objectInfo, String folderPath )
369 {
370 if ( objectId.equalsIgnoreCase( ROOT_ID ) )
371 {
372 objectId = ROOT_SPACE_ID;
373 }
374
375 RepositoryObject object = new RepositoryObject( objectId );
376
377 return compileObjectType( context, object, null, true, true, true, objectInfo, folderPath );
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391 public ObjectData getObjectByPath( CallContext context, String folderPath, String filter,
392 boolean includeAllowableActions, boolean includeACL, ObjectInfoHandler objectInfos )
393 {
394 boolean userReadOnly = true;
395
396
397 Set<String> filterCollection = splitFilter( filter );
398
399
400 if ( ( folderPath == null ) || ( !folderPath.startsWith( "/" ) ) )
401 {
402 throw new CmisInvalidArgumentException( "Invalid folder path!" );
403 }
404
405 RepositoryObject object = new RepositoryObject( ROOT_SPACE_ID );
406
407 return compileObjectType( context, object, filterCollection, includeAllowableActions, includeACL, userReadOnly,
408 objectInfos, folderPath );
409 }
410
411
412
413
414
415
416
417
418
419
420 public ContentStream getContentStream( CallContext context, String objectId, BigInteger offset, BigInteger length )
421 {
422 if ( ( offset != null ) || ( length != null ) )
423 {
424 throw new CmisInvalidArgumentException( "Offset and Length are not supported!" );
425 }
426
427 RepositoryObject object = new RepositoryObject( objectId );
428
429 Document document = object.getDocument( );
430
431 if ( document == null )
432 {
433 throw new CmisStreamNotSupportedException( "Document not found" );
434 }
435
436 String xml = document.getXmlValidatedContent( );
437
438 if ( xml == null )
439 {
440 xml = document.getXmlWorkingContent( );
441 }
442
443 byte[] bytes = xml.getBytes( );
444 InputStream stream = new ByteArrayInputStream( bytes );
445 ContentStreamImpl result = new ContentStreamImpl( );
446 result.setFileName( document.getTitle( ) );
447 result.setLength( BigInteger.valueOf( bytes.length ) );
448 result.setMimeType( "application/xml" );
449 result.setStream( stream );
450
451 return result;
452 }
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468 public List<ObjectInFolderContainer> getDescendants( CallContext context, String folderId, BigInteger depth,
469 String filter, Boolean includeAllowableActions, Boolean includePathSegment, ObjectInfoHandler objectInfos,
470 boolean userReadOnly )
471 {
472 RepositoryObject object = new RepositoryObject( folderId );
473 String folderPath = "/" + object.getName( );
474
475
476 int d = ( ( depth == null ) ? 2 : depth.intValue( ) );
477
478 if ( d == 0 )
479 {
480 throw new CmisInvalidArgumentException( "Depth must not be 0!" );
481 }
482
483 if ( d < -1 )
484 {
485 d = -1;
486 }
487
488
489 Set<String> filterCollection = splitFilter( filter );
490
491 boolean foldersOnly = true;
492 List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>( );
493
494
495 boolean iaa = ( ( includeAllowableActions == null ) ? false : includeAllowableActions.booleanValue( ) );
496 boolean ips = ( ( includePathSegment == null ) ? false : includePathSegment.booleanValue( ) );
497
498 if ( context.isObjectInfoRequired( ) )
499 {
500 compileObjectType( context, object, null, false, false, userReadOnly, objectInfos, folderPath );
501 }
502
503 AppLogService.debug( "object=" + object );
504
505 gatherDescendants( context, object, result, foldersOnly, d, filterCollection, iaa, ips, userReadOnly,
506 objectInfos, folderPath );
507
508 return result;
509 }
510
511 private ObjectData compileObjectType( CallContext context, RepositoryObject object, Set<String> filter,
512 boolean includeAllowableActions, boolean includeAcl, boolean userReadOnly, ObjectInfoHandler objectInfos,
513 String folderPath )
514 {
515 ObjectDataImpl result = new ObjectDataImpl( );
516 ObjectInfoImpl objectInfo = new ObjectInfoImpl( );
517
518 result.setProperties( compileProperties( object, filter, objectInfo, folderPath ) );
519
520
521
522
523
524
525
526
527
528 if ( context.isObjectInfoRequired( ) )
529 {
530 objectInfo.setObject( result );
531 objectInfos.addObjectInfo( objectInfo );
532 }
533
534 return result;
535 }
536
537 private org.apache.chemistry.opencmis.commons.data.Properties compileProperties( RepositoryObject object,
538 Set<String> orgfilter, ObjectInfoImpl objectInfo, String folderPath )
539 {
540 if ( object == null )
541 {
542 throw new IllegalArgumentException( "Document must not be null!" );
543 }
544
545
546 Set<String> filter = ( ( orgfilter == null ) ? null : new HashSet<String>( orgfilter ) );
547
548
549 String typeId = null;
550
551 try
552 {
553 PropertiesImpl result = new PropertiesImpl( );
554
555 String id = object.getId( );
556 String name = object.getName( );
557
558 objectInfo.setId( id );
559 objectInfo.setName( name );
560 objectInfo.setCreatedBy( CREATED_BY );
561 objectInfo.setHasAcl( false );
562 objectInfo.setHasParent( true );
563 objectInfo.setVersionSeriesId( null );
564 objectInfo.setIsCurrentVersion( true );
565 objectInfo.setRelationshipSourceIds( null );
566 objectInfo.setRelationshipTargetIds( null );
567 objectInfo.setRenditionInfos( null );
568 objectInfo.setSupportsPolicies( false );
569 objectInfo.setSupportsRelationships( false );
570 objectInfo.setWorkingCopyId( null );
571 objectInfo.setWorkingCopyOriginalId( null );
572
573 addPropertyId( result, typeId, filter, PropertyIds.OBJECT_ID, id );
574 addPropertyString( result, typeId, filter, PropertyIds.NAME, name );
575 addPropertyString( result, typeId, filter, PropertyIds.CREATED_BY, CREATED_BY );
576 addPropertyString( result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, CREATED_BY );
577 addPropertyString( result, typeId, filter, PropertyIds.PATH, folderPath );
578 addPropertyString( result, typeId, filter, PropertyIds.CHANGE_TOKEN, null );
579
580 if ( object.isDocument( ) )
581 {
582 Document doc = object.getDocument( );
583 typeId = TypeManager.DOCUMENT_TYPE_ID;
584
585 GregorianCalendar created = millisToCalendar( object.getDocument( ).getDateCreation( ).getTime( ) );
586 GregorianCalendar lastModified = millisToCalendar( object.getDocument( ).getDateModification( )
587 .getTime( ) );
588
589 objectInfo.setBaseType( BaseTypeId.CMIS_DOCUMENT );
590 objectInfo.setTypeId( typeId );
591 objectInfo.setSupportsDescendants( false );
592 objectInfo.setSupportsFolderTree( false );
593 objectInfo.setHasContent( true );
594 objectInfo.setContentType( MIME_TYPE_XML );
595 objectInfo.setFileName( doc.getTitle( ) );
596 objectInfo.setCreationDate( created );
597 objectInfo.setLastModificationDate( lastModified );
598
599 addPropertyId( result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value( ) );
600 addPropertyId( result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value( ) );
601 addPropertyDateTime( result, typeId, filter, PropertyIds.CREATION_DATE, created );
602 addPropertyDateTime( result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified );
603 addPropertyId( result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE, MIME_TYPE_XML );
604 addPropertyId( result, typeId, filter, PropertyIds.CONTENT_STREAM_ID, object.getId( ) );
605 addPropertyId( result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH,
606 "" + doc.getXmlWorkingContent( ).length( ) );
607 }
608 else if ( object.isSpace( ) )
609 {
610 typeId = TypeManager.FOLDER_TYPE_ID;
611 objectInfo.setBaseType( BaseTypeId.CMIS_FOLDER );
612 objectInfo.setTypeId( typeId );
613 objectInfo.setSupportsDescendants( true );
614 objectInfo.setSupportsFolderTree( true );
615 objectInfo.setHasContent( false );
616 addPropertyId( result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_FOLDER.value( ) );
617 addPropertyId( result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_FOLDER.value( ) );
618 }
619
620
621
622 if ( filter != null )
623 {
624 if ( !filter.isEmpty( ) )
625 {
626
627 }
628 }
629
630 return result;
631 }
632 catch ( Exception e )
633 {
634 if ( e instanceof CmisBaseException )
635 {
636 throw (CmisBaseException) e;
637 }
638
639 throw new CmisRuntimeException( e.getMessage( ), e );
640 }
641 }
642
643
644
645
646
647
648
649 private static Set<String> splitFilter( String filter )
650 {
651 if ( filter == null )
652 {
653 return null;
654 }
655
656 if ( filter.trim( ).length( ) == 0 )
657 {
658 return null;
659 }
660
661 Set<String> result = new HashSet<String>( );
662
663 for ( String s : filter.split( "," ) )
664 {
665 s = s.trim( );
666
667 if ( s.equals( "*" ) )
668 {
669 return null;
670 }
671 else if ( s.length( ) > 0 )
672 {
673 result.add( s );
674 }
675 }
676
677
678
679 result.add( PropertyIds.OBJECT_ID );
680 result.add( PropertyIds.OBJECT_TYPE_ID );
681 result.add( PropertyIds.BASE_TYPE_ID );
682
683 return result;
684 }
685
686 private void gatherDescendants( CallContext context, RepositoryObject object, List<ObjectInFolderContainer> list,
687 boolean foldersOnly, int depth, Set<String> filter, boolean includeAllowableActions,
688 boolean includePathSegments, boolean userReadOnly, ObjectInfoHandler objectInfos, String folderPath )
689 {
690
691 if ( object.getSpaceChildren( ) == null )
692 {
693 AppLogService.debug( "No childs " );
694
695 return;
696 }
697
698 folderPath = folderPath + "/" + object.getName( );
699
700 for ( DocumentSpace space : object.getSpaceChildren( ) )
701 {
702 AppLogService.debug( "child space " + space.getName( ) );
703
704
705 ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl( );
706 RepositoryObject child = new RepositoryObject( PREFIX_SPACE + space.getId( ) );
707 objectInFolder.setObject( compileObjectType( context, child, filter, includeAllowableActions, false,
708 userReadOnly, objectInfos, folderPath ) );
709
710 if ( includePathSegments )
711 {
712 objectInFolder.setPathSegment( space.getName( ) );
713 }
714
715 ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl( );
716 container.setObject( objectInFolder );
717
718 list.add( container );
719
720
721 if ( ( depth != 1 ) )
722 {
723 container.setChildren( new ArrayList<ObjectInFolderContainer>( ) );
724 gatherDescendants( context, child, container.getChildren( ), foldersOnly, depth - 1, filter,
725 includeAllowableActions, includePathSegments, userReadOnly, objectInfos, folderPath );
726 }
727 }
728
729
730 if ( !foldersOnly )
731 {
732 for ( Document doc : object.getDocumentChildren( ) )
733 {
734 AppLogService.debug( "document " + doc.getTitle( ) );
735
736
737 ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl( );
738 RepositoryObject child = new RepositoryObject( PREFIX_DOC + doc.getId( ) );
739 objectInFolder.setObject( compileObjectType( context, child, filter, includeAllowableActions, false,
740 userReadOnly, objectInfos, folderPath ) );
741
742 if ( includePathSegments )
743 {
744 objectInFolder.setPathSegment( doc.getTitle( ) );
745 }
746
747 ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl( );
748 container.setObject( objectInFolder );
749
750 list.add( container );
751
752
753 if ( ( depth != 1 ) )
754 {
755 container.setChildren( new ArrayList<ObjectInFolderContainer>( ) );
756 gatherDescendants( context, child, container.getChildren( ), foldersOnly, depth - 1, filter,
757 includeAllowableActions, includePathSegments, userReadOnly, objectInfos, folderPath );
758 }
759 }
760 }
761 }
762 }