| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| #import "CMISBrowserUtil.h" |
| #import "CMISConstants.h" |
| #import "CMISBrowserConstants.h" |
| #import "CMISRepositoryInfo.h" |
| #import "CMISPropertyDefinition.h" |
| #import "CMISRenditionData.h" |
| #import "CMISDocumentTypeDefinition.h" |
| #import "CMISFolderTypeDefinition.h" |
| #import "CMISRelationshipTypeDefinition.h" |
| #import "CMISItemTypeDefinition.h" |
| #import "CMISSecondaryTypeDefinition.h" |
| #import "CMISErrors.h" |
| #import "CMISNSDictionary+CMISUtil.h" |
| #import "CMISRepositoryCapabilities.h" |
| #import "CMISObjectConverter.h" |
| #import "CMISAcl.h" |
| #import "CMISAce.h" |
| #import "CMISPrincipal.h" |
| #import "CMISAllowableActions.h" |
| |
| @interface NSObject (CMISUtil) |
| |
| + (void)performBlock:(void (^)(void))block; |
| |
| @end |
| |
| @implementation NSObject (CMISUtil) |
| |
| + (void)performBlock:(void (^)(void))block |
| { |
| [NSObject performSelector:@selector(executeBlock:) onThread:[NSThread currentThread] withObject:block waitUntilDone:NO]; |
| } |
| |
| + (void)executeBlock:(void (^)(void))block { |
| block(); |
| } |
| |
| @end |
| |
| @implementation CMISBrowserUtil |
| |
| + (NSDictionary *)repositoryInfoDictionaryFromJSONData:(NSData *)jsonData bindingSession:(CMISBindingSession *)bindingSession error:(NSError **)outError |
| { |
| // TODO: error handling i.e. if jsonData is nil, also handle outError being nil |
| |
| // parse the JSON response |
| NSError *serialisationError = nil; |
| id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError]; |
| |
| NSMutableDictionary *repositories = nil; |
| if (!serialisationError) { |
| repositories = [NSMutableDictionary dictionary]; |
| |
| // parse the json into CMISRepositoryInfo objects and store in self.repositories |
| NSArray *repos = [jsonDictionary allValues]; |
| for (NSDictionary *repo in repos) { |
| CMISRepositoryInfo *repoInfo = [CMISRepositoryInfo new]; |
| repoInfo.identifier = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONRepositoryId]; |
| repoInfo.name = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONRepositoryName]; |
| repoInfo.summary = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONRepositoryDescription]; |
| repoInfo.vendorName = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONVendorName]; |
| repoInfo.productName = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONProductName]; |
| repoInfo.productVersion = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONProductVersion]; |
| repoInfo.rootFolderId = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONRootFolderId]; |
| NSString *repositoryUrl = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONRepositoryUrl]; |
| NSString *rootFolderUrl = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONRootFolderUrl]; |
| |
| repoInfo.repositoryCapabilities = [CMISBrowserUtil convertRepositoryCapabilities:[repo cmis_objectForKeyNotNull:kCMISBrowserJSONCapabilities]]; |
| //TOOD aclCapabilities |
| repoInfo.latestChangeLogToken = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONLatestChangeLogToken]; |
| |
| repoInfo.cmisVersionSupported = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONCMISVersionSupported]; |
| repoInfo.thinClientUri = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONThinClientUri]; |
| |
| //TODO repoInfo.changesIncomplete = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONChangesIncomplete); |
| //TODO changesOnType |
| |
| repoInfo.principalIdAnonymous = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONPrincipalIdAnonymous]; |
| repoInfo.principalIdAnyone = [repo cmis_objectForKeyNotNull:kCMISBrowserJSONPrincipalIdAnyone]; |
| |
| //handle extensions |
| repoInfo.extensions = [CMISObjectConverter convertExtensions:repo cmisKeys:[CMISBrowserConstants repositoryInfoKeys]]; |
| |
| // store the repo and root folder URLs in the session (when the repoId matches) |
| if ([repoInfo.identifier isEqualToString:bindingSession.repositoryId]) { |
| [bindingSession setObject:rootFolderUrl forKey:kCMISBrowserBindingSessionKeyRootFolderUrl]; |
| [bindingSession setObject:repositoryUrl forKey:kCMISBrowserBindingSessionKeyRepositoryUrl]; |
| } |
| |
| [repositories setObject:repoInfo forKey:repoInfo.identifier]; |
| } |
| } else { |
| if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]; |
| return nil; |
| } |
| |
| return repositories; |
| } |
| |
| + (CMISTypeDefinition *)typeDefinitionFromJSONData:(NSData *)jsonData error:(NSError **)outError |
| { |
| // TODO: error handling i.e. if jsonData is nil, also handle outError being nil |
| |
| // parse the JSON response |
| NSError *serialisationError = nil; |
| id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError]; |
| |
| CMISTypeDefinition *typeDef = nil; |
| if (!serialisationError) { |
| //TODO check for valid baseTypeId (cmis:document, cmis:folder, cmis:relationship, cmis:policy, [cmis:item, cmis:secondary]) |
| CMISBaseType baseType = [CMISEnums enumForBaseId:[jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONBaseId]]; |
| switch (baseType) { |
| case CMISBaseTypeDocument: { |
| typeDef = [CMISDocumentTypeDefinition new]; |
| ((CMISDocumentTypeDefinition*)typeDef).contentStreamAllowed = [CMISEnums enumForContentStreamAllowed: |
| [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONContentStreamAllowed]]; |
| ((CMISDocumentTypeDefinition*)typeDef).versionable = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONVersionable]; |
| break; |
| } |
| case CMISBaseTypeFolder: |
| typeDef = [CMISFolderTypeDefinition new]; |
| break; |
| |
| case CMISBaseTypeRelationship: { |
| typeDef = [CMISRelationshipTypeDefinition new]; |
| |
| id allowedSourceTypes = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONAllowedSourceTypes]; |
| if ([allowedSourceTypes isKindOfClass:NSArray.class]){ |
| NSMutableArray *types = [[NSMutableArray alloc] init]; |
| for (id type in allowedSourceTypes) { |
| if (type){ |
| [types addObject:type]; |
| } |
| } |
| ((CMISRelationshipTypeDefinition*)typeDef).allowedSourceTypes = types; |
| } |
| |
| id allowedTargetTypes = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONAllowedTargetTypes]; |
| if ([allowedTargetTypes isKindOfClass:NSArray.class]){ |
| NSMutableArray *types = [[NSMutableArray alloc] init]; |
| for (id type in allowedTargetTypes) { |
| if (type){ |
| [types addObject:type]; |
| } |
| } |
| ((CMISRelationshipTypeDefinition*)typeDef).allowedTargetTypes = types; |
| } |
| break; |
| } |
| case CMISBaseTypeItem: |
| typeDef = [CMISItemTypeDefinition new]; |
| break; |
| case CMISBaseTypeSecondary: |
| typeDef = [CMISSecondaryTypeDefinition new]; |
| break; |
| default: |
| if (outError != NULL) *outError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:[NSString stringWithFormat:@"Type '%@' does not match a base type!", [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONBaseId]]]; |
| return nil; |
| } |
| |
| typeDef.baseTypeId = baseType; |
| typeDef.summary = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONDescription]; |
| typeDef.displayName = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONDisplayName]; |
| typeDef.identifier = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONId]; |
| typeDef.controllablePolicy = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONControllablePolicy]; |
| typeDef.controllableAcl = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONControllableAcl]; |
| typeDef.creatable = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONCreateable]; |
| typeDef.fileable = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONFileable]; |
| typeDef.fullTextIndexed = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONFullTextIndexed]; |
| typeDef.includedInSupertypeQuery = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONIncludedInSuperTypeQuery]; |
| typeDef.queryable = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONQueryable]; |
| typeDef.localName = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONLocalName]; |
| typeDef.localNameSpace = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONLocalNamespace]; |
| typeDef.parentTypeId = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONParentId]; |
| typeDef.queryName = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONQueryName]; |
| |
| //TODO type mutability |
| |
| NSDictionary *propertyDefinitions = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONPropertyDefinitions]; |
| for (NSDictionary *propertyDefDictionary in [propertyDefinitions allValues]) { |
| [typeDef addPropertyDefinition:[CMISBrowserUtil convertPropertyDefinition:propertyDefDictionary]]; |
| } |
| |
| // handle extensions |
| typeDef.extensions = [CMISObjectConverter convertExtensions:jsonDictionary cmisKeys:[CMISBrowserConstants typeKeys]]; |
| } else { |
| if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]; |
| return nil; |
| } |
| |
| return typeDef; |
| } |
| |
| + (void)objectDataFromJSONData:(NSData *)jsonData typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void(^)(CMISObjectData *objectData, NSError *error))completionBlock |
| { |
| // TODO: error handling i.e. if jsonData is nil, also handle outError being nil |
| |
| // parse the JSON response |
| NSError *serialisationError = nil; |
| id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError]; |
| |
| if (!serialisationError) { |
| // parse the json into a CMISObjectData object |
| [CMISBrowserUtil convertObject:jsonDictionary typeCache:typeCache completionBlock:^(CMISObjectData *objectData, NSError *error) { |
| completionBlock(objectData, error); |
| }]; |
| } else { |
| completionBlock(nil, [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]); |
| } |
| } |
| |
| + (void)objectListFromJSONData:(NSData *)jsonData typeCache:(CMISBrowserTypeCache *)typeCache isQueryResult:(BOOL)isQueryResult completionBlock:(void(^)(CMISObjectList *objectList, NSError *error))completionBlock |
| { |
| // TODO: error handling i.e. if jsonData is nil, also handle outError being nil |
| |
| // parse the JSON response |
| NSError *serialisationError = nil; |
| id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError]; |
| |
| CMISObjectList *objectList = nil; |
| if (!serialisationError) { |
| // parse the json into a CMISObjectList object |
| objectList = [CMISObjectList new]; |
| |
| // parse the objects |
| NSArray *objectsArray; |
| if ([jsonDictionary isKindOfClass:NSArray.class]){ |
| objectsArray = jsonDictionary; |
| |
| objectList.hasMoreItems = NO; |
| objectList.numItems = (int)objectsArray.count; |
| } else { // is NSDictionary |
| if (isQueryResult) { |
| objectsArray = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONResults]; |
| } else { |
| objectsArray = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONObjects]; |
| } |
| // retrieve the paging data |
| objectList.hasMoreItems = [jsonDictionary cmis_boolForKey:kCMISBrowserJSONHasMoreItems]; |
| objectList.numItems = [jsonDictionary cmis_intForKey:kCMISBrowserJSONNumberItems]; |
| } |
| |
| [CMISBrowserUtil convertObjects:objectsArray typeCache:typeCache completionBlock:^(NSArray *objects, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| // pass objects to list |
| objectList.objects = objects; |
| |
| // handle extension data |
| if([jsonDictionary isKindOfClass:NSDictionary.class]) { |
| if (isQueryResult) { |
| objectList.extensions = [CMISObjectConverter convertExtensions:jsonDictionary cmisKeys:[CMISBrowserConstants queryResultListKeys]]; |
| } else { |
| objectList.extensions = [CMISObjectConverter convertExtensions:jsonDictionary cmisKeys:[CMISBrowserConstants objectListKeys]]; |
| } |
| } |
| completionBlock(objectList, nil); |
| } |
| }]; |
| } else { |
| completionBlock(nil, [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]); |
| } |
| } |
| |
| + (NSArray *)renditionsFromJSONData:(NSData *)jsonData error:(NSError **)outError |
| { |
| // TODO: error handling i.e. if jsonData is nil, also handle outError being nil |
| |
| // parse the JSON response |
| NSError *serialisationError = nil; |
| id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError]; |
| |
| NSArray *renditions = nil; |
| if (!serialisationError) { |
| // parse the json into a CMISObjectData object |
| renditions = [CMISBrowserUtil renditionsFromArray:jsonDictionary]; |
| } else { |
| if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]; |
| return nil; |
| } |
| |
| return renditions; |
| } |
| |
| + (NSArray *)failedToDeleteObjectsFromJSONData:(NSData *)jsonData error:(NSError **)outError |
| { |
| // TODO: error handling i.e. if jsonData is nil, also handle outError being nil |
| |
| // parse the JSON response |
| NSError *serialisationError = nil; |
| id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError]; |
| |
| if (!serialisationError) { |
| NSMutableArray *ids = [[NSMutableArray alloc] init]; |
| NSArray *jsonIds = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONFailedToDeleteId]; |
| |
| if (jsonIds) { |
| for (NSObject *obj in jsonIds) { |
| [ids addObject:obj.description]; //obj can't be nil as it came out of an array |
| } |
| } |
| |
| return ids; |
| } else { |
| if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]; |
| return nil; |
| } |
| } |
| |
| + (void)objectParents:(NSData *)jsonData typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void(^)(NSArray *objectParents, NSError *error))completionBlock |
| { |
| // TODO: error handling i.e. if jsonData is nil, also handle outError being nil |
| |
| // parse the JSON response |
| NSError *serialisationError = nil; |
| id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError]; |
| |
| if (!serialisationError) { |
| [self convertObjects:jsonDictionary typeCache:typeCache completionBlock:completionBlock]; |
| } else { |
| completionBlock(nil, [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]); |
| return; |
| } |
| } |
| |
| #pragma mark - |
| #pragma mark Private helper methods |
| |
| + (void)convertObject:(NSDictionary *)dictionary typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void(^)(CMISObjectData *objectData, NSError *error))completionBlock |
| { |
| if (!dictionary) { |
| completionBlock(nil, nil); |
| } |
| |
| CMISObjectData *objectData = [CMISObjectData new]; |
| |
| BOOL hasSuccinctProperties = YES; |
| NSDictionary *propertiesJson = [dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONSuccinctProperties]; |
| if(!propertiesJson){ |
| hasSuccinctProperties = NO; |
| propertiesJson = [dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONProperties]; |
| } |
| |
| |
| id identifier = [propertiesJson cmis_objectForKeyNotNull:kCMISPropertyObjectId]; |
| if ([identifier isKindOfClass:NSDictionary.class]){ |
| objectData.identifier = [identifier cmis_objectForKeyNotNull:kCMISBrowserJSONValue]; |
| } else { |
| objectData.identifier = identifier; |
| } |
| |
| // determine the object type |
| id baseType = [propertiesJson cmis_objectForKeyNotNull:kCMISPropertyBaseTypeId]; |
| if([baseType isKindOfClass:NSDictionary.class]) { |
| baseType = [baseType cmis_objectForKeyNotNull:kCMISBrowserJSONValue]; |
| } |
| |
| // TODO other base types |
| if ([baseType isEqualToString:kCMISPropertyObjectTypeIdValueDocument]) { |
| objectData.baseType = CMISBaseTypeDocument; |
| } else if ([baseType isEqualToString:kCMISPropertyObjectTypeIdValueFolder]) { |
| objectData.baseType = CMISBaseTypeFolder; |
| } |
| |
| objectData.acl = [CMISBrowserUtil convertAcl:[dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONAcl]]; //TODO here we should pass isExactAcl:nil! |
| |
| objectData.allowableActions = [CMISBrowserUtil convertAllowableActions:[dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONAllowableActions]]; |
| |
| objectData.isExactAcl = [dictionary cmis_boolForKey:kCMISBrowserJSONIsExact]; |
| |
| // TODO set policyIds |
| |
| NSDictionary *propertiesExtension = [dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONPropertiesExtension]; |
| |
| void (^continueWithObjectConversion)(CMISProperties*, NSError*) = ^(CMISProperties *properties, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| objectData.properties = properties; |
| |
| // relationships |
| NSArray *relationshipsJson = [dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONRelationships]; |
| [CMISBrowserUtil convertObjects:relationshipsJson typeCache:typeCache completionBlock:^(NSArray *objects, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| objectData.relationships = objects; |
| |
| //renditions |
| NSArray *renditionsJson = [dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONRenditions]; |
| objectData.renditions = [self renditionsFromArray:renditionsJson]; |
| |
| // handle extensions |
| objectData.extensions = [CMISObjectConverter convertExtensions:dictionary cmisKeys:[CMISBrowserConstants objectKeys]]; |
| |
| completionBlock(objectData, nil); |
| } |
| }]; |
| } |
| }; |
| |
| if(hasSuccinctProperties) { |
| [CMISBrowserUtil convertSuccinctProperties:propertiesJson propertiesExtension:propertiesExtension typeCache:typeCache completionBlock:^(CMISProperties *properties, NSError *error) { |
| continueWithObjectConversion(properties, error); |
| }]; |
| } else { |
| NSError *error = nil; |
| CMISProperties *properties = [CMISBrowserUtil convertProperties:propertiesJson propertiesExtension:propertiesExtension error:&error]; |
| continueWithObjectConversion(properties, error); |
| } |
| |
| } |
| |
| + (void)convertObjects:(NSArray *)objectsArray position:(NSInteger)position convertedObjects:(NSMutableArray *)convertedObjects typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void(^)(NSArray* objects, NSError *error))completionBlock |
| { |
| NSDictionary *dictionary = [objectsArray objectAtIndex:position]; |
| NSDictionary *objectDictionary = [dictionary cmis_objectForKeyNotNull:kCMISBrowserJSONObject]; |
| if (!objectDictionary) { |
| objectDictionary = dictionary; |
| } |
| |
| if(![objectDictionary isKindOfClass:NSDictionary.class]){ |
| completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:[NSString stringWithFormat:@"expected a dictionary but was %@", objectDictionary.class]]); |
| } |
| [CMISBrowserUtil convertObject:objectDictionary typeCache:typeCache completionBlock:^(CMISObjectData *objectData, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| if (position == 0) { |
| [convertedObjects addObject:objectData]; |
| completionBlock(convertedObjects, nil); |
| } else { |
| // TODO check if there is a better way on how to avoid a large call stack |
| // We need to do this workaround or else we would end up with a very large call stack |
| [CMISBrowserUtil performBlock:^{ |
| [self convertObjects:objectsArray |
| position:(position -1) |
| convertedObjects:convertedObjects |
| typeCache:typeCache |
| completionBlock:^(NSArray *objects, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| [convertedObjects addObject:objectData]; |
| completionBlock(objects, nil); |
| } |
| }]; |
| }]; |
| } |
| } |
| }]; |
| |
| } |
| |
| + (void)convertObjects:(NSArray *)objectsArray typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void(^)(NSArray* objects, NSError *error))completionBlock |
| { |
| NSMutableArray *objects = [NSMutableArray arrayWithCapacity:objectsArray.count]; |
| if (objectsArray.count > 0) { |
| [CMISBrowserUtil convertObjects:objectsArray |
| position:(objectsArray.count - 1) // start recursion with last item |
| convertedObjects:objects |
| typeCache:typeCache |
| completionBlock:^(NSArray *objects, NSError *error) { |
| completionBlock(objects, error); |
| }]; |
| } else { |
| completionBlock([NSArray array], nil); |
| } |
| } |
| |
| + (CMISProperties *)convertProperties:(NSDictionary *)propertiesJson propertiesExtension:(NSDictionary *)extJson error:(NSError **)outError |
| { |
| if(!propertiesJson) { |
| return nil; |
| } |
| |
| CMISProperties *properties = [[CMISProperties alloc] init]; |
| |
| for (NSString *propName in propertiesJson) { |
| NSDictionary *propertyDictionary = [propertiesJson cmis_objectForKeyNotNull:propName]; |
| if (!propertyDictionary) { |
| continue; |
| } |
| |
| CMISPropertyType propertyType = [CMISEnums enumForPropertyType:[propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONDatatype]]; |
| |
| id propValue = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONValue]; |
| NSArray *values = nil; |
| if ([propValue isKindOfClass:NSArray.class]) { |
| values = propValue; |
| } else if (propValue) { |
| values = [NSArray arrayWithObject:propValue]; |
| } |
| |
| CMISPropertyData *propertyData; |
| switch (propertyType) { |
| case CMISPropertyTypeString: |
| case CMISPropertyTypeId: |
| case CMISPropertyTypeBoolean: |
| case CMISPropertyTypeInteger: |
| case CMISPropertyTypeDecimal: |
| case CMISPropertyTypeHtml: |
| case CMISPropertyTypeUri: |
| propertyData = [CMISPropertyData createPropertyForId:propName arrayValue:values type:propertyType]; |
| break; |
| case CMISPropertyTypeDateTime: { |
| NSArray *dateValues = [CMISBrowserUtil convertNumbersToDates:values]; |
| propertyData = [CMISPropertyData createPropertyForId:propName arrayValue:dateValues type:propertyType]; |
| break; |
| } |
| default: { |
| if (outError != NULL) *outError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument |
| detailedDescription:@"Unknown property type!"]; |
| return nil; |
| } |
| } |
| propertyData.identifier = propName; |
| propertyData.displayName = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONDisplayName]; |
| propertyData.queryName = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONQueryName]; |
| propertyData.localName = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONLocalName]; |
| |
| propertyData.extensions = [CMISObjectConverter convertExtensions:propertyDictionary cmisKeys:[CMISBrowserConstants propertyKeys]]; |
| |
| [properties addProperty:propertyData]; |
| } |
| |
| if (extJson){ |
| properties.extensions = [CMISObjectConverter convertExtensions:extJson cmisKeys:[NSSet set]]; |
| } |
| |
| return properties; |
| } |
| |
| + (void)convertSuccinctProperties:(NSDictionary *)propertiesJson propertiesExtension:(NSDictionary *)extJson typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void(^)(CMISProperties *properties, NSError *error))completionBlock |
| { |
| if (!propertiesJson) { |
| completionBlock(nil, nil); |
| } |
| |
| void (^continueConvertSuccinctPropertiesAndGetSecondaryObjectTypeDefinitions)(CMISTypeDefinition*) = ^(CMISTypeDefinition *typeDef) { |
| |
| void (^continueConvertSuccinctPropertiesSecondaryObjectTypeDefinitions)(NSArray*) = ^(NSArray *secTypeDefs) { |
| |
| [self convertProperties:propertiesJson typeCache:typeCache typeDefinition:typeDef secondaryTypeDefinitions:secTypeDefs completionBlock:^(CMISProperties *properties, NSError *error){ |
| if(error){ |
| completionBlock(nil, error); |
| } else { |
| if (extJson){ |
| properties.extensions = [CMISObjectConverter convertExtensions:extJson cmisKeys:[NSSet set]]; |
| } |
| |
| completionBlock(properties, nil); |
| } |
| }]; |
| }; |
| |
| // Get secondary object type definitions |
| NSArray *secTypeIds = [propertiesJson cmis_objectForKeyNotNull:kCMISPropertySecondaryObjectTypeIds]; |
| if (secTypeIds != nil && secTypeIds.count > 0) { |
| [CMISBrowserUtil retrieveTypeDefinitions:secTypeIds typeCache:typeCache completionBlock:^(NSArray *typeDefinitions, NSError *error) { |
| if(error){ |
| completionBlock(nil, error); |
| } else { |
| continueConvertSuccinctPropertiesSecondaryObjectTypeDefinitions(typeDefinitions); |
| } |
| }]; |
| } else { |
| continueConvertSuccinctPropertiesSecondaryObjectTypeDefinitions(nil); |
| } |
| }; |
| |
| // Get type definition for given object type id |
| CMISTypeDefinition *typeDef = nil; |
| if ([[propertiesJson cmis_objectForKeyNotNull:kCMISPropertyObjectTypeId] isKindOfClass:NSString.class]){ |
| [typeCache typeDefinition:[propertiesJson cmis_objectForKeyNotNull:kCMISPropertyObjectTypeId] completionBlock:^(CMISTypeDefinition *typeDef, NSError *error){ |
| if(error){ |
| completionBlock(nil, error); |
| } else { |
| continueConvertSuccinctPropertiesAndGetSecondaryObjectTypeDefinitions(typeDef); |
| } |
| }]; |
| } else { |
| continueConvertSuccinctPropertiesAndGetSecondaryObjectTypeDefinitions(typeDef); |
| } |
| } |
| |
| +(void)convertProperty:(NSString *)propName propertiesJson:(NSDictionary *)propertiesJson typeCache:(CMISBrowserTypeCache *)typeCache typeDefinition:(CMISTypeDefinition *)typeDef secondaryTypeDefinitions:(NSArray *)secTypeDefs completionBlock:(void(^)(CMISPropertyData *propertyData, NSError *error))completionBlock { |
| CMISPropertyDefinition *propDef = nil; |
| if (typeDef){ |
| propDef = typeDef.propertyDefinitions[propName]; |
| } |
| |
| if (propDef == nil && secTypeDefs != nil) { |
| for (CMISTypeDefinition *secTypeDef in secTypeDefs) { |
| propDef = secTypeDef.propertyDefinitions[propName]; |
| if (propDef){ |
| break; |
| } |
| } |
| } |
| |
| void (^continueConvertSuccinctPropertiesTypeDefinitionDocument)(CMISPropertyDefinition*) = ^(CMISPropertyDefinition *propDef) { |
| |
| void (^continueConvertSuccinctPropertiesTypeDefinitionFolder)(CMISPropertyDefinition*) = ^(CMISPropertyDefinition *propDef) { |
| |
| id propValue = [propertiesJson cmis_objectForKeyNotNull:propName]; |
| NSArray *values = nil; |
| if ([propValue isKindOfClass:NSArray.class]) { |
| values = propValue; |
| } else if (propValue) { |
| values = [NSArray arrayWithObject:propValue]; |
| } |
| |
| CMISPropertyData *propertyData; |
| |
| if (propDef){ |
| |
| switch (propDef.propertyType) { |
| case CMISPropertyTypeString: |
| case CMISPropertyTypeId: |
| case CMISPropertyTypeBoolean: |
| case CMISPropertyTypeInteger: |
| case CMISPropertyTypeDecimal: |
| case CMISPropertyTypeHtml: |
| case CMISPropertyTypeUri: |
| propertyData = [CMISPropertyData createPropertyForId:propName arrayValue:values type:propDef.propertyType]; |
| break; |
| case CMISPropertyTypeDateTime: { |
| NSArray *dateValues = [CMISBrowserUtil convertNumbersToDates:values]; |
| propertyData = [CMISPropertyData createPropertyForId:propName arrayValue:dateValues type:propDef.propertyType]; |
| break; |
| } |
| default: { |
| NSError *error = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument |
| detailedDescription:@"Unknown property type!"]; |
| completionBlock(nil, error); |
| return; |
| } |
| } |
| propertyData.identifier = propName; |
| propertyData.displayName = propDef.displayName; |
| propertyData.queryName = propDef.queryName; |
| propertyData.localName = propDef.localName; |
| } else { |
| // this else block should only be reached in rare circumstances |
| // it may return incorrect types |
| if (values == nil) { |
| propertyData = [CMISPropertyData createPropertyForId:propName arrayValue:nil type:CMISPropertyTypeString]; |
| } else { |
| id firstValue = values[0]; |
| if ([firstValue isKindOfClass:NSNumber.class]) { |
| propertyData = [CMISPropertyData createPropertyForId:propName arrayValue:values type:CMISPropertyTypeInteger]; |
| } else { |
| propertyData = [CMISPropertyData createPropertyForId:propName arrayValue:values type:CMISPropertyTypeString]; |
| } |
| } |
| |
| propertyData.identifier = propName; |
| propertyData.displayName = propName; |
| propertyData.queryName = nil; |
| propertyData.localName = nil; |
| } |
| |
| completionBlock(propertyData, nil); |
| }; |
| |
| if (!propDef) { //try to find property definition on folder |
| [typeCache typeDefinition:kCMISPropertyObjectTypeIdValueFolder completionBlock:^(CMISTypeDefinition *typeDefinition, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| CMISPropertyDefinition *propertyDefinition = typeDefinition.propertyDefinitions[propName]; |
| continueConvertSuccinctPropertiesTypeDefinitionFolder(propertyDefinition); |
| } |
| }]; |
| } else { |
| continueConvertSuccinctPropertiesTypeDefinitionFolder(propDef); |
| } |
| |
| }; |
| |
| if (!propDef) { //try to find property definition on document |
| [typeCache typeDefinition:kCMISPropertyObjectTypeIdValueDocument completionBlock:^(CMISTypeDefinition *typeDefinition, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| CMISPropertyDefinition *propertyDefinition = typeDefinition.propertyDefinitions[propName]; |
| continueConvertSuccinctPropertiesTypeDefinitionDocument(propertyDefinition); |
| } |
| }]; |
| } else { |
| continueConvertSuccinctPropertiesTypeDefinitionDocument(propDef); |
| } |
| } |
| |
| + (NSArray *)convertNumbersToDates:(NSArray *)numbers |
| { |
| if(!numbers) { |
| return nil; |
| } |
| |
| NSMutableArray *dates = [[NSMutableArray alloc] initWithCapacity:numbers.count]; |
| for (NSNumber *miliseconds in numbers) { |
| NSDate *date = [NSDate dateWithTimeIntervalSince1970:[miliseconds doubleValue] / 1000.0]; // miliseconds to seconds |
| [dates addObject:date]; |
| } |
| return dates; |
| } |
| |
| + (void)convertProperties:(NSArray*)propNames position:(NSInteger)position properties:(CMISProperties *)properties propertiesJson:(NSDictionary *)propertiesJson typeCache:(CMISBrowserTypeCache *)typeCache typeDefinition:(CMISTypeDefinition *)typeDef secondaryTypeDefinitions:(NSArray *)secTypeDefs completionBlock:(void (^)(CMISProperties *properties, NSError *error))completionBlock |
| { |
| NSString *propName = [propNames objectAtIndex:position]; |
| |
| [self convertProperty:propName |
| propertiesJson:propertiesJson |
| typeCache:typeCache |
| typeDefinition:typeDef |
| secondaryTypeDefinitions:secTypeDefs |
| completionBlock:^(CMISPropertyData *propertyData, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| if (position == 0) { |
| [properties addProperty:propertyData]; |
| completionBlock(properties, nil); |
| } else { |
| // TODO check if there is a better way on how to avoid a large call stack |
| // We need to do this workaround or else we would end up with a very large call stack |
| [CMISBrowserUtil performBlock:^{ |
| [self convertProperties:propNames |
| position:(position -1) |
| properties:properties |
| propertiesJson:propertiesJson |
| typeCache:typeCache |
| typeDefinition:typeDef |
| secondaryTypeDefinitions:secTypeDefs |
| completionBlock:^(CMISProperties *properties, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| [properties addProperty:propertyData]; |
| completionBlock(properties, nil); |
| } |
| }]; |
| }]; |
| } |
| } |
| }]; |
| } |
| |
| + (void)convertProperties:(NSDictionary *)propertiesJson typeCache:(CMISBrowserTypeCache *)typeCache typeDefinition:(CMISTypeDefinition *)typeDef secondaryTypeDefinitions:(NSArray *)secTypeDefs completionBlock:(void(^)(CMISProperties *properties, NSError *error))completionBlock |
| { |
| // create properties |
| CMISProperties *properties = [CMISProperties new]; |
| |
| NSArray *propNames = [propertiesJson allKeys]; |
| if (propNames.count > 0) { |
| [CMISBrowserUtil convertProperties:propNames |
| position:(propNames.count - 1) // start recursion with last item |
| properties:properties |
| propertiesJson:propertiesJson |
| typeCache:typeCache |
| typeDefinition:typeDef |
| secondaryTypeDefinitions:secTypeDefs |
| completionBlock:^(CMISProperties *properties, NSError *error) { |
| completionBlock(properties, error); |
| }]; |
| } else { |
| completionBlock(properties, nil); |
| } |
| } |
| |
| + (CMISRepositoryCapabilities *)convertRepositoryCapabilities:(NSDictionary *)jsonDictionary |
| { |
| if (!jsonDictionary){ |
| return nil; |
| } |
| |
| CMISRepositoryCapabilities *result = [[CMISRepositoryCapabilities alloc] init]; |
| for (NSString *capabilityKey in jsonDictionary) { |
| id value = [jsonDictionary cmis_objectForKeyNotNull:capabilityKey]; |
| |
| if([[CMISConstants repositoryCapabilityKeys] containsObject:capabilityKey]) { |
| [result setCapability:capabilityKey value:value]; |
| } |
| } |
| |
| result.extensions = [CMISObjectConverter convertExtensions:jsonDictionary cmisKeys:[CMISConstants repositoryCapabilityKeys]]; |
| |
| return result; |
| } |
| |
| + (CMISAcl *)convertAcl:(NSDictionary *)jsonDictionary |
| { |
| if (!jsonDictionary) { |
| return nil; |
| } |
| |
| CMISAcl *result = [[CMISAcl alloc] init]; |
| |
| NSMutableArray *aces = [[NSMutableArray alloc] init]; |
| |
| NSArray *jsonAces = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONAces]; |
| if (jsonAces) { |
| for (NSDictionary *entry in jsonAces) { |
| CMISAce *ace = [[CMISAce alloc] init]; |
| |
| id isDirect = [entry cmis_objectForKeyNotNull:kCMISBrowserJSONAceIsDirect]; |
| ace.isDirect = isDirect != nil ? [isDirect boolValue] : YES; |
| |
| NSArray *jsonPermissions = [entry cmis_objectForKeyNotNull:kCMISBrowserJSONAcePermissions]; |
| if (jsonPermissions) { |
| NSMutableSet *permissions = [[NSMutableSet alloc] init]; |
| for (NSObject *perm in jsonPermissions) { |
| [permissions addObject:[perm description]]; |
| } |
| ace.permissions = permissions; |
| } |
| |
| NSDictionary *jsonPrincipal = [entry cmis_objectForKeyNotNull:kCMISBrowserJSONAcePrincipal]; |
| if (jsonPrincipal) { |
| CMISPrincipal *principal = [[CMISPrincipal alloc] init]; |
| |
| principal.principalId = [jsonPrincipal cmis_objectForKeyNotNull:kCMISBrowserJSONAcePrincipalId]; |
| |
| principal.extensions = [CMISObjectConverter convertExtensions:jsonPrincipal cmisKeys:[CMISBrowserConstants principalKeys]]; |
| |
| ace.principal = principal; |
| } |
| |
| ace.extensions = [CMISObjectConverter convertExtensions:entry cmisKeys:[CMISBrowserConstants aceKeys]]; |
| |
| [aces addObject:ace]; |
| } |
| } |
| |
| result.aces = [aces copy]; |
| |
| //TODO result.isExact = isExact; // there should be a "isExcat" parameter of this method |
| |
| result.extensions = [CMISObjectConverter convertExtensions:jsonDictionary cmisKeys:[CMISBrowserConstants aclKeys]]; |
| |
| return result; |
| } |
| |
| + (CMISAllowableActions *)convertAllowableActions:(NSDictionary *)jsonDictionary |
| { |
| if (!jsonDictionary) { |
| return nil; |
| } |
| |
| NSArray *extensions = [CMISObjectConverter convertExtensions:jsonDictionary cmisKeys:[NSSet setWithObjects:CMISAllowableActionsArray]]; |
| CMISAllowableActions *result = [[CMISAllowableActions alloc] initWithAllowableActionsDictionary:jsonDictionary extensionElementArray:extensions]; |
| |
| return result; |
| } |
| |
| + (void)retrieveTypeDefinitions:(NSArray *)objectTypeIds position:(NSInteger)position typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void (^)(NSMutableArray *typeDefinitions, NSError *error))completionBlock |
| { |
| [typeCache typeDefinition:[objectTypeIds objectAtIndex:position] |
| completionBlock:^(CMISTypeDefinition *typeDefinition, NSError *error) { |
| if (error){ |
| completionBlock(nil, error); |
| } else { |
| if (position == 0) { |
| NSMutableArray *typeDefinitions = [[NSMutableArray alloc] initWithCapacity:objectTypeIds.count]; |
| [typeDefinitions addObject:typeDefinition]; |
| completionBlock(typeDefinitions, error); |
| } else { |
| [self retrieveTypeDefinitions:objectTypeIds position:(position - 1) typeCache:typeCache completionBlock:^(NSMutableArray *typeDefinitions, NSError *error) { |
| if(error) { |
| completionBlock(nil, error); |
| } else { |
| [typeDefinitions addObject:typeDefinition]; |
| completionBlock(typeDefinitions, nil); |
| } |
| }]; |
| } |
| } |
| }]; |
| } |
| |
| + (void)retrieveTypeDefinitions:(NSArray *)objectTypeIds typeCache:(CMISBrowserTypeCache *)typeCache completionBlock:(void (^)(NSArray *typeDefinitions, NSError *error))completionBlock |
| { |
| if (objectTypeIds.count > 0) { |
| [CMISBrowserUtil retrieveTypeDefinitions:objectTypeIds |
| position:(objectTypeIds.count - 1) // start recursion with last item |
| typeCache:typeCache |
| completionBlock:^(NSMutableArray *typeDefinitions, NSError *error) { |
| completionBlock(typeDefinitions, error); |
| }]; |
| } else { |
| completionBlock([[NSArray alloc] init], nil); |
| } |
| } |
| |
| + (NSArray *)renditionsFromArray:(NSArray *)array |
| { |
| if (!array) { |
| return nil; |
| } |
| NSMutableArray *renditions = [[NSMutableArray alloc] initWithCapacity:array.count]; |
| for(NSDictionary *renditionJson in array){ |
| CMISRenditionData *renditionData = [CMISRenditionData new]; |
| renditionData.height = [NSNumber numberWithLongLong:[[renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionHeight] longLongValue]]; |
| renditionData.kind = [renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionKind]; |
| renditionData.length = [NSNumber numberWithLongLong:[[renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionLength] longLongValue]]; |
| renditionData.mimeType = [renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionMimeType]; |
| renditionData.renditionDocumentId = [renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionDocumentId]; |
| renditionData.streamId = [renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionStreamId]; |
| renditionData.title = [renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionTitle]; |
| renditionData.width = [NSNumber numberWithLongLong:[[renditionJson cmis_objectForKeyNotNull:kCMISBrowserJSONRenditionWidth] longLongValue]]; |
| |
| // handle extensions |
| renditionData.extensions = [CMISObjectConverter convertExtensions:renditionJson cmisKeys:[CMISBrowserConstants renditionKeys]]; |
| |
| [renditions addObject:renditionData]; |
| } |
| |
| return renditions; |
| } |
| |
| + (CMISPropertyDefinition *)convertPropertyDefinition:(NSDictionary *)propertyDictionary |
| { |
| if (!propertyDictionary){ |
| return nil; |
| } |
| |
| // create property definition and add to type definition |
| CMISPropertyDefinition *propDef = [CMISPropertyDefinition new]; |
| propDef.identifier = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONId]; |
| propDef.localName = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONLocalName]; |
| propDef.localNamespace = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONLocalNamespace]; |
| propDef.queryName = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONQueryName]; |
| propDef.summary = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONDescription]; |
| propDef.displayName = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONDisplayName]; |
| propDef.inherited = [propertyDictionary cmis_boolForKey:kCMISBrowserJSONInherited]; |
| propDef.openChoice = [propertyDictionary cmis_boolForKey:kCMISBrowserJSONOpenChoice]; |
| propDef.orderable = [propertyDictionary cmis_boolForKey:kCMISBrowserJSONOrderable]; |
| propDef.queryable = [propertyDictionary cmis_boolForKey:kCMISBrowserJSONQueryable]; |
| propDef.required = [propertyDictionary cmis_boolForKey:kCMISBrowserJSONRequired]; |
| |
| // determine property type |
| propDef.propertyType = [CMISEnums enumForPropertyType:[propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONPropertyType]]; |
| |
| // determine cardinality |
| NSString *cardinalityString = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONCardinality]; |
| if ([cardinalityString isEqualToString:kCMISBrowserJSONCardinalityValueSingle]) { |
| propDef.cardinality = CMISCardinalitySingle; |
| } else if ([cardinalityString isEqualToString:kCMISBrowserJSONCardinalityValueMultiple]) { |
| propDef.cardinality = CMISCardinalityMulti; |
| } |
| |
| // determine updatability |
| NSString *updatabilityString = [propertyDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONUpdateability]; |
| if ([updatabilityString isEqualToString:kCMISBrowserJSONUpdateabilityValueReadOnly]) { |
| propDef.updatability = CMISUpdatabilityReadOnly; |
| } else if ([updatabilityString isEqualToString:kCMISBrowserJSONUpdateabilityValueReadWrite]) { |
| propDef.updatability = CMISUpdatabilityReadWrite; |
| } else if ([updatabilityString isEqualToString:kCMISBrowserJSONUpdateabilityValueOnCreate]) { |
| propDef.updatability = CMISUpdatabilityOnCreate; |
| } else if ([updatabilityString isEqualToString:kCMISBrowserJSONUpdateabilityValueWhenCheckedOut]) { |
| propDef.updatability = CMISUpdatabilityWhenCheckedOut; |
| } |
| |
| // TODO default value |
| |
| // handle extensions |
| propDef.extensions = [CMISObjectConverter convertExtensions:propertyDictionary cmisKeys:[CMISBrowserConstants propertyTypeKeys]]; |
| |
| return propDef; |
| } |
| |
| |
| @end |