diff --git a/ChangeLog b/ChangeLog index bdd0e8c1c..a1c80add9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2011-11-13 Wolfgang Sourdeau + + * Tests/Unit/TestVersit.m (-test_rendering, -test_parsing): + adapted to new NGCards data structure and improved to test further + important use cases. + +2011-11-10 Wolfgang Sourdeau + + * Tests/Unit/TestVersit.m: new test module for versit parsing and + outputting. + 2011-11-09 Francis Lachapelle * UI/WebServerResources/SOGoRootPage.js (onLoginClick): don't diff --git a/OGoContentStore/OCSContactFieldExtractor.m b/OGoContentStore/OCSContactFieldExtractor.m index 60e204182..5382ef634 100644 --- a/OGoContentStore/OCSContactFieldExtractor.m +++ b/OGoContentStore/OCSContactFieldExtractor.m @@ -39,27 +39,19 @@ { NSMutableDictionary *fields; NSArray *values; - CardElement *adr; + CardElement *element; NSString *value; - unsigned int max; fields = [NSMutableDictionary dictionaryWithCapacity: 16]; value = [vCard fn]; if (value) [fields setObject: value forKey: @"c_cn"]; - values = [vCard n]; - if (values) - { - max = [values count]; - if (max > 0) - { - [fields setObject: [values objectAtIndex: 0] forKey: @"c_sn"]; - if (max > 1) - [fields setObject: [values objectAtIndex: 1] - forKey: @"c_givenName"]; - } - } + element = [vCard n]; + [fields setObject: [element flattenedValueAtIndex: 0 forKey: @""] + forKey: @"c_sn"]; + [fields setObject: [element flattenedValueAtIndex: 1 forKey: @""] + forKey: @"c_givenName"]; value = [vCard preferredTel]; if (value) [fields setObject: value forKey: @"c_telephoneNumber"]; @@ -67,18 +59,17 @@ if (![value isNotNull]) value = @""; [fields setObject: value forKey: @"c_mail"]; - values = [vCard org]; - max = [values count]; - if (max > 0) - { - [fields setObject: [values objectAtIndex: 0] forKey: @"c_o"]; - if (max > 1) - [fields setObject: [values objectAtIndex: 1] forKey: @"c_ou"]; - } - adr = [vCard preferredAdr]; - if (adr) - [fields setObject: [adr value: 3] forKey: @"c_l"]; - value = [[vCard uniqueChildWithTag: @"X-AIM"] value: 0]; + element = [vCard org]; + [fields setObject: [element flattenedValueAtIndex: 0 forKey: @""] + forKey: @"c_o"]; + [fields setObject: [element flattenedValueAtIndex: 1 forKey: @""] + forKey: @"c_ou"]; + element = [vCard preferredAdr]; + if (element && ![element isVoid]) + [fields setObject: [element flattenedValueAtIndex: 3 + forKey: @""] + forKey: @"c_l"]; + value = [[vCard uniqueChildWithTag: @"X-AIM"] flattenedValuesForKey: @""]; [fields setObject: value forKey: @"c_screenname"]; values = [[vCard categories] trimmedComponents]; if ([values count] > 0) diff --git a/SOPE/GDLContentStore/ChangeLog b/SOPE/GDLContentStore/ChangeLog index 155d4e62a..5be5f1ad4 100644 --- a/SOPE/GDLContentStore/ChangeLog +++ b/SOPE/GDLContentStore/ChangeLog @@ -1,3 +1,9 @@ +2011-11-14 Wolfgang Sourdeau + + * GCSFolder.m + (_generateUpdateStatementForRow:adaptor:fields:tableName:whereColumn:isEqualTo:andColumn:isEqualTo:): + same as below. + 2011-10-21 Francis Lachapelle * GCSFolder.m diff --git a/SOPE/GDLContentStore/GCSFolder.m b/SOPE/GDLContentStore/GCSFolder.m index c9cf89c61..24e6af857 100644 --- a/SOPE/GDLContentStore/GCSFolder.m +++ b/SOPE/GDLContentStore/GCSFolder.m @@ -602,40 +602,48 @@ static GCSStringFormatter *stringFormatter = nil; - (NSString *)_formatRowValue:(id)_value { - if (![_value isNotNull]) - return @"NULL"; - - if ([_value isKindOfClass:NSStringClass]) - return [stringFormatter stringByFormattingString:_value]; - - if ([_value isKindOfClass:NSNumberClass]) { -#if GNUSTEP_BASE_LIBRARY - _value = [_value stringValue]; - return ([(NSString *)_value hasPrefix:@"Y"] || - [(NSString *)_value hasPrefix:@"N"]) - ? (id)([_value boolValue] ? @"1" : @"0") - : _value; -#endif - return [_value stringValue]; - } - if ([_value isKindOfClass:NSCalendarDateClass]) { /* be smart ... convert to timestamp. Note: we loose precision. */ char buf[256]; snprintf(buf, sizeof(buf), "%i", (int)[_value timeIntervalSince1970]); return [NSString stringWithCString:buf]; } - - [self errorWithFormat:@"cannot handle value class: %@", [_value class]]; - return nil; + else { + return _value; + } } -- (NSString *)_generateInsertStatementForRow:(NSDictionary *)_row - tableName:(NSString *)_table +- (NSString *) _sqlTypeForColumn: (NSString *) _field withFieldInfos: (NSArray *) _fields +{ + NSString *sqlType; + NSEnumerator *fields; + GCSFieldInfo *fieldInfo; + + sqlType = nil; + fields = [_fields objectEnumerator]; + while ((fieldInfo = [fields nextObject])) + { + if ([[fieldInfo columnName] caseInsensitiveCompare: _field] == NSOrderedSame) + { + sqlType = [fieldInfo sqlType]; + break; + } + } + + return sqlType; +} + +- (NSString *) _generateInsertStatementForRow:(NSDictionary *)_row + adaptor:(EOAdaptor *)_adaptor + fields:(NSArray *)_fields + tableName:(NSString *)_table { // TODO: move to NSDictionary category? NSMutableString *sql; + NSString *fieldName, *sqlType; NSArray *keys; + EOAttribute *attribute; + id value; unsigned i, count; if (_row == nil || _table == nil) @@ -656,12 +664,25 @@ static GCSStringFormatter *stringFormatter = nil; [sql appendString:@") VALUES ("]; for (i = 0, count = [keys count]; i < count; i++) { - id value; + fieldName = [keys objectAtIndex:i]; + sqlType = [self _sqlTypeForColumn: fieldName withFieldInfos: _fields]; - if (i != 0) [sql appendString:@", "]; - value = [_row objectForKey:[keys objectAtIndex:i]]; - value = [self _formatRowValue:value]; - [sql appendString:value]; + if (sqlType) + { + value = [self _formatRowValue: [_row objectForKey: fieldName]]; + attribute = AUTORELEASE([[EOAttribute alloc] init]); + [attribute setName: fieldName]; + [attribute setColumnName: fieldName]; + [attribute setExternalType: sqlType]; + + if (i != 0) [sql appendString:@", "]; + [sql appendString:[_adaptor formatValue: value forAttribute: attribute]]; + } + else + { + [self errorWithFormat:@"%s: no type found for column name %@", + __PRETTY_FUNCTION__, fieldName]; + } } [sql appendString:@")"]; @@ -699,7 +720,7 @@ static GCSStringFormatter *stringFormatter = nil; if (sqlType) { - value = [_row objectForKey: fieldName]; + value = [self _formatRowValue: [_row objectForKey: fieldName]]; attribute = AUTORELEASE([[EOAttribute alloc] init]); [attribute setName: fieldName]; [attribute setColumnName: fieldName]; @@ -717,41 +738,32 @@ static GCSStringFormatter *stringFormatter = nil; } } + sqlType = [self _sqlTypeForColumn: _colname withFieldInfos: _fields]; + attribute = AUTORELEASE([[EOAttribute alloc] init]); + [attribute setName: _colname]; + [attribute setColumnName: _colname]; + [attribute setExternalType: sqlType]; [sql appendString:@" WHERE "]; [sql appendString:_colname]; [sql appendString:@" = "]; - [sql appendString:[self _formatRowValue:_value]]; + [sql appendString:[_adaptor formatValue: [self _formatRowValue:_value] forAttribute: attribute]]; if (_colname2 != nil) { [sql appendString:@" AND "]; + + sqlType = [self _sqlTypeForColumn: _colname2 withFieldInfos: _fields]; + attribute = AUTORELEASE([[EOAttribute alloc] init]); + [attribute setName: _colname2]; + [attribute setColumnName: _colname2]; + [attribute setExternalType: sqlType]; [sql appendString:_colname2]; [sql appendString:@" = "]; - [sql appendString:[self _formatRowValue:_value2]]; + [sql appendString:[_adaptor formatValue: [self _formatRowValue:_value2] forAttribute: attribute]]; } return sql; } -- (NSString *) _sqlTypeForColumn: (NSString *) _field withFieldInfos: (NSArray *) _fields -{ - NSString *sqlType; - NSEnumerator *fields; - GCSFieldInfo *fieldInfo; - - sqlType = nil; - fields = [_fields objectEnumerator]; - while ((fieldInfo = [fields nextObject])) - { - if ([[fieldInfo columnName] caseInsensitiveCompare: _field] == NSOrderedSame) - { - sqlType = [fieldInfo sqlType]; - break; - } - } - - return sqlType; -} - - (EOEntity *) _entityWithName: (NSString *) _name { EOAttribute *attribute; @@ -976,6 +988,8 @@ static GCSStringFormatter *stringFormatter = nil; ? [quickChannel insertRowX: quickRow forEntity: quickTableEntity] : [quickChannel evaluateExpressionX: [self _generateInsertStatementForRow: quickRow + adaptor: [[quickChannel adaptorContext] adaptor] + fields: [folderInfo quickFields] tableName: [self quickTableName]]]); if (!error) @@ -983,6 +997,8 @@ static GCSStringFormatter *stringFormatter = nil; ? [storeChannel insertRowX: contentRow forEntity: storeTableEntity] : [storeChannel evaluateExpressionX: [self _generateInsertStatementForRow: contentRow + adaptor: [[storeChannel adaptorContext] adaptor] + fields: [folderInfo fields] tableName: [self storeTableName]]]); } else diff --git a/SOPE/NGCards/CardElement.h b/SOPE/NGCards/CardElement.h index 6f59d0ad1..af94a5c49 100644 --- a/SOPE/NGCards/CardElement.h +++ b/SOPE/NGCards/CardElement.h @@ -36,7 +36,7 @@ @interface CardElement : NSObject { NSString *tag; - NSMutableArray *values; + NSMutableDictionary *values; NSMutableDictionary *attributes; NSString *group; CardGroup *parent; @@ -51,31 +51,44 @@ singleType: (NSString *) aType value: (NSString *) aValue; -+ (id) elementWithTag: (NSString *) aTag - attributes: (NSDictionary *) someAttributes - values: (NSArray *) someValues; - - (void) setParent: (CardGroup *) aParent; - (id) parent; - (void) setTag: (NSString *) aTag; +- (NSString *) tag; - (void) setGroup: (NSString *) aGroup; - (NSString *) group; -- (void) addValue: (NSString *) aValue; -- (void) addValues: (NSArray *) someValues; +- (BOOL) isVoid; -- (void) setValue: (unsigned int) anInt - to: (NSString *) aValue; -- (NSString *) value: (unsigned int) anInt; +- (void) setValues: (NSMutableDictionary *) newValues; +- (NSMutableDictionary *) values; -- (void) setNamedValue: (NSString *) aValueName - to: (NSString *) aValue; -- (NSString *) namedValue: (NSString *) aValueName; +/* ELEM:...;value1,value2,...;... */ +- (void) setValues: (NSMutableArray *) newValues + atIndex: (NSUInteger) idx + forKey: (NSString *) key; -- (void) setCommaSeparatedValues: (NSArray *) values; +/* ELEM:...;value;... */ +- (void) setSingleValue: (NSString *) newValue + atIndex: (NSUInteger) idx + forKey: (NSString *) key; +/* ELEM:value */ +- (void) setSingleValue: (NSString *) newValue + forKey: (NSString *) key; +- (NSMutableArray *) valuesForKey: (NSString *) key; +- (NSMutableArray *) valuesAtIndex: (NSUInteger) idx + forKey: (NSString *) key; + +/* This joins all subvalues with "," and ordered values with ";". Handy for + retrieving data from clients which don't escape their data properly. */ +- (NSString *) flattenedValuesForKey: (NSString *) key; +- (NSString *) flattenedValueAtIndex: (NSUInteger) idx + forKey: (NSString *) key; + +/* attribute values */ - (void) setValue: (unsigned int) anInt ofAttribute: (NSString *) anAttribute to: (NSString *) aValue; @@ -87,17 +100,11 @@ - (void) addAttributes: (NSDictionary *) someAttributes; - (void) removeValue: (NSString *) aValue fromAttribute: (NSString *) anAttribute; - -- (void) addType: (NSString *) aType; - -- (NSString *) tag; -- (void) setValues: (NSArray *) newValues; -- (NSArray *) values; -- (NSDictionary *) attributes; +- (NSMutableDictionary *) attributes; - (BOOL) hasAttribute: (NSString *) aType havingValue: (NSString *) aValue; -- (BOOL) isVoid; +- (void) addType: (NSString *) aType; - (NSString *) versitString; diff --git a/SOPE/NGCards/CardElement.m b/SOPE/NGCards/CardElement.m index 49c3913b4..ec4d7404f 100644 --- a/SOPE/NGCards/CardElement.m +++ b/SOPE/NGCards/CardElement.m @@ -56,7 +56,7 @@ id newElement; newElement = [self elementWithTag: aTag]; - [newElement addValue: aValue]; + [newElement setSingleValue: aValue forKey: @""]; return newElement; } @@ -74,21 +74,6 @@ return newElement; } -+ (id) elementWithTag: (NSString *) aTag - attributes: (NSDictionary *) someAttributes - values: (NSArray *) someValues -{ - id newElement; - - newElement = [self new]; - [newElement autorelease]; - [newElement setTag: aTag]; - [newElement addAttributes: someAttributes]; - [newElement addValues: someValues]; - - return newElement; -} - - (id) init { if ((self = [super init])) @@ -96,7 +81,7 @@ parent = nil; tag = nil; group = nil; - values = [NSMutableArray new]; + values = [NSMutableDictionary new]; attributes = [NSMutableDictionary new]; } @@ -142,16 +127,172 @@ return group; } -- (void) addValue: (NSString *) aValue +/* values */ +- (void) setValues: (NSMutableDictionary *) newValues { - if (!aValue) - aValue = @""; - [values addObject: aValue]; + ASSIGN (values, newValues); } -- (void) addValues: (NSArray *) someValues +- (NSMutableDictionary *) values { - [values addObjectsFromArray: someValues]; + return values; +} + +- (void) setValues: (NSMutableArray *) newValues + atIndex: (NSUInteger) idx + forKey: (NSString *) key +{ + NSMutableArray *oldValues, *subValues; + + oldValues = [self valuesForKey: key]; + if (!oldValues) + { + oldValues = [NSMutableArray new]; + [values setObject: oldValues forKey: key]; + [oldValues release]; + } + + while ([oldValues count] < (idx + 1)) + { + subValues = [NSMutableArray new]; + [oldValues addObject: subValues]; + [subValues release]; + } + + if (!newValues) + newValues = [NSMutableArray array]; + [oldValues replaceObjectAtIndex: idx withObject: newValues]; +} + +- (void) setSingleValue: (NSString *) newValue + atIndex: (NSUInteger) idx + forKey: (NSString *) key +{ + NSMutableArray *subValues; + + if (newValue) + { + subValues = [NSMutableArray new]; + [subValues addObject: newValue]; + } + else + subValues = nil; + [self setValues: subValues atIndex: idx forKey: key]; + [subValues release]; +} + +- (void) setSingleValue: (NSString *) newValue + forKey: (NSString *) key +{ + [self setSingleValue: newValue + atIndex: 0 forKey: key]; +} + +- (NSMutableArray *) valuesForKey: (NSString *) key +{ + return [values objectForKey: [key lowercaseString]]; +} + +- (NSMutableArray *) valuesAtIndex: (NSUInteger) idx + forKey: (NSString *) key +{ + return [[self valuesForKey: key] objectAtIndex: idx]; +} + +- (NSString *) flattenedValueAtIndex: (NSUInteger) idx + forKey: (NSString *) key +{ + NSMutableArray *orderedValues, *sValues; + NSUInteger count, max; + NSMutableString *flattenedValues; + NSString *encoding, *realValue, *value; + + flattenedValues = [NSMutableString string]; + + orderedValues = [self valuesForKey: key]; + max = [orderedValues count]; + if (max > idx) + { + encoding = [[self value: 0 ofAttribute: @"encoding"] + lowercaseString]; + sValues = [orderedValues objectAtIndex: idx]; + max = [sValues count]; + for (count = 0; count < max; count++) + { + if (count > 0) + [flattenedValues appendString: @","]; + realValue = [sValues objectAtIndex: count]; + if ([encoding isEqualToString: @"quoted-printable"]) + value = [realValue stringByDecodingQuotedPrintable]; + else if ([encoding isEqualToString: @"base64"]) + value = [realValue stringByDecodingBase64]; + else + { + value = realValue; + if ([encoding length] && ![encoding isEqualToString: @"8bit"]) + [self logWithFormat: @"unknown encoding '%@'", encoding]; + } + + [flattenedValues appendString: value]; + } + } + + return flattenedValues; +} + +- (NSString *) flattenedValuesForKey: (NSString *) key +{ + NSMutableArray *orderedValues, *sValues; + NSUInteger count, max, sCount, sMax; + NSMutableString *flattenedValues; + NSString *encoding, *realValue, *value; + + encoding = [[self value: 0 ofAttribute: @"encoding"] + lowercaseString]; + + flattenedValues = [NSMutableString string]; + + orderedValues = [self valuesForKey: key]; + max = [orderedValues count]; + for (count = 0; count < max; count++) + { + if (count > 0) + [flattenedValues appendString: @";"]; + sValues = [orderedValues objectAtIndex: count]; + sMax = [sValues count]; + for (sCount = 0; sCount < sMax; sCount++) + { + if (sCount > 0) + [flattenedValues appendString: @","]; + realValue = [sValues objectAtIndex: sCount]; + if ([encoding isEqualToString: @"quoted-printable"]) + value = [realValue stringByDecodingQuotedPrintable]; + else if ([encoding isEqualToString: @"base64"]) + value = [realValue stringByDecodingBase64]; + else + { + value = realValue; + if ([encoding length] && ![encoding isEqualToString: @"8bit"]) + [self logWithFormat: @"unknown encoding '%@'", encoding]; + } + + [flattenedValues appendString: value]; + } + } + + return flattenedValues; +} + +/* attributes */ + +- (NSMutableDictionary *) attributes +{ + return attributes; +} + +- (void) setAttributesAsCopy: (NSMutableDictionary *) someAttributes +{ + ASSIGN (attributes, someAttributes); } - (void) addAttribute: (NSString *) anAttribute @@ -219,26 +360,6 @@ [self addAttribute: @"type" value: aType]; } -- (void) setValues: (NSArray *) newValues -{ - if (![newValues isKindOfClass: [NSMutableArray class]]) - { - newValues = [newValues mutableCopy]; - [newValues autorelease]; - } - ASSIGN (values, newValues); -} - -- (NSArray *) values -{ - return values; -} - -- (NSDictionary *) attributes -{ - return attributes; -} - - (BOOL) hasAttribute: (NSString *) anAttribute havingValue: (NSString *) aValue { @@ -249,141 +370,6 @@ return (attribute && [attribute hasCaseInsensitiveString: aValue]); } -- (void) setValue: (unsigned int) anInt - to: (NSString *) aValue -{ - unsigned int count, max; - - if (!aValue) - aValue = @""; - max = [values count]; - for (count = max; count <= anInt; count++) - [self addValue: @""]; - - [values replaceObjectAtIndex: anInt withObject: aValue]; -} - -- (NSString *) value: (unsigned int) anInt -{ - NSString *realValue, *value, *encoding; - - if ([values count] <= anInt) - value = @""; - else - { - realValue = [values objectAtIndex: anInt]; - encoding = [[self value: 0 ofAttribute: @"encoding"] lowercaseString]; - if ([encoding length]) - { - if ([encoding isEqualToString: @"quoted-printable"]) - value = [realValue stringByDecodingQuotedPrintable]; - else if ([encoding isEqualToString: @"base64"]) - value = [realValue stringByDecodingBase64]; - else - { - value = realValue; - if (![encoding isEqualToString: @"8bit"]) - [self logWithFormat: @"unknown encoding '%@'", encoding]; - } - } - else - value = realValue; - } - - return value; -} - -- (unsigned int) _namedValue: (NSString *) aValueName -{ - NSString *prefix, *value; - unsigned int count, max, result; - - result = NSNotFound; - - prefix = [NSString stringWithFormat: @"%@=", [aValueName uppercaseString]]; - max = [values count]; - count = 0; - - while (result == NSNotFound && count < max) - { - value = [[values objectAtIndex: count] uppercaseString]; - if ([value hasPrefix: prefix]) - result = count; - else - count++; - } - - return result; -} - -- (void) setNamedValue: (NSString *) aValueName - to: (NSString *) aValue -{ - NSString *newValue; - unsigned int index; - - if (!aValue) - aValue = @""; - newValue = [NSString stringWithFormat: @"%@=%@", - [aValueName uppercaseString], - aValue]; - index = [self _namedValue: aValueName]; - if (index == NSNotFound) - { - if ([aValue length]) - [self addValue: newValue]; - } - else - { - if ([aValue length]) - [self setValue: index to: newValue]; - else - [values removeObjectAtIndex: index]; - } -} - -- (NSString *) namedValue: (NSString *) aValueName -{ - unsigned int index; - NSRange equalSign; - NSString *value; - - index = [self _namedValue: aValueName]; - if (index == NSNotFound) - value = @""; - else - { - value = [values objectAtIndex: index]; - equalSign = [value rangeOfString: @"="]; - if (equalSign.location != NSNotFound) - value = [value substringFromIndex: equalSign.location + 1]; - } - - return value; -} - -- (void) setCommaSeparatedValues: (NSArray *) newValues -{ - NSMutableString *newValue; - NSUInteger count, max; - NSString *currentValue; - - newValue = [NSMutableString stringWithCapacity: 250]; - - max = [newValues count]; - for (count = 0; count < max; count++) - { - currentValue = [[newValues objectAtIndex: count] - stringByReplacingString: @"," - withString: @"\\,"]; - if (count > 0) - [newValue appendFormat: @",%@", currentValue]; - else - [newValue appendString: currentValue]; - } - [self setValues: [NSArray arrayWithObject: newValue]]; -} - - (void) setValue: (unsigned int) anInt ofAttribute: (NSString *) anAttribute to: (NSString *) aValue @@ -423,10 +409,7 @@ - (NSString *) description { - NSArray *attrs; NSMutableString *str; - unsigned int count, max; - NSString *attr; str = [NSMutableString stringWithCapacity:64]; [str appendFormat:@"<%p[%@]:", self, NSStringFromClass([self class])]; @@ -435,48 +418,56 @@ else [str appendFormat: @"%@\n", tag, group]; - attrs = [attributes allKeys]; - max = [attrs count]; - if (max > 0) - { - [str appendFormat: @"\n %d attributes: {\n", [attrs count]]; - for (count = 0; count < max; count++) - { - attr = [attrs objectAtIndex: count]; - [str appendFormat: @" %@: %@\n", - attr, [attributes objectForKey: attr]]; - } - [str appendFormat: @"}"]; - } - - max = [values count]; - if (max > 0) - { - [str appendFormat: @"\n %d values: {\n", [values count]]; - for (count = 0; count < max; count++) - [str appendFormat: @" %@\n", [values objectAtIndex: count]]; - [str appendFormat: @"}"]; - } - - [str appendString:@">"]; + [str appendString: [self versitString]]; return str; } -- (BOOL) isVoid +static inline BOOL +_subValuesAreVoid (NSArray *subValues) { - BOOL result; - NSString *value; - NSEnumerator *enu; + BOOL result = YES; + NSUInteger count, max; result = YES; - enu = [values objectEnumerator]; - value = [enu nextObject]; - while (value && result) + max = [subValues count]; + for (count = 0; result && count < max; count++) + result = ([[subValues objectAtIndex: count] length] == 0); + + return result; +} + +static inline BOOL +_orderedValuesAreVoid (NSArray *orderedValues) +{ + BOOL result = YES; + NSUInteger count, max; + + result = YES; + + max = [orderedValues count]; + for (count = 0; result && count < max; count++) + result = _subValuesAreVoid ([orderedValues objectAtIndex: count]); + + return result; +} + +- (BOOL) isVoid +{ + BOOL result = YES; + NSArray *keys; + NSMutableArray *orderedValues; + NSUInteger count, max; + + result = YES; + + keys = [values allKeys]; + max = [keys count]; + for (count = 0; result && count < max; count++) { - result = ([value length] == 0); - value = [enu nextObject]; + orderedValues = [values objectForKey: [keys objectAtIndex: count]]; + result = _orderedValuesAreVoid (orderedValues); } return result; @@ -523,11 +514,6 @@ return newElement; } -- (void) setAttributesAsCopy: (NSMutableDictionary *) someAttributes -{ - ASSIGN (attributes, someAttributes); -} - - (CardGroup *) searchParentOfClass: (Class) parentClass { CardGroup *current; @@ -594,7 +580,7 @@ newGroup = [group copyWithZone: aZone]; [new setGroup: newGroup]; [newGroup release]; - [new setValues: [self deepCopyOfArray: values withZone: aZone]]; + [new setValues: [self deepCopyOfDictionary: values withZone: aZone]]; [new setAttributesAsCopy: [self deepCopyOfDictionary: attributes withZone: aZone]]; @@ -604,22 +590,7 @@ /* NSMutableCopying */ - (id) mutableCopyWithZone: (NSZone *) aZone { -#warning this method really is the same as "copyWithZone:" - CardElement *new; - NSString *newTag, *newGroup; - - new = [[self class] new]; - newTag = [tag copyWithZone: aZone]; - [new setTag: newTag]; - [newTag release]; - newGroup = [group copyWithZone: aZone]; - [new setGroup: newGroup]; - [newGroup release]; - [new setValues: [self deepCopyOfArray: values withZone: aZone]]; - [new setAttributesAsCopy: [self deepCopyOfDictionary: attributes - withZone: aZone]]; - - return new; + return [self copyWithZone: aZone]; } @end diff --git a/SOPE/NGCards/CardGroup.m b/SOPE/NGCards/CardGroup.m index 8af47506b..c6ef8bb62 100644 --- a/SOPE/NGCards/CardGroup.m +++ b/SOPE/NGCards/CardGroup.m @@ -281,7 +281,7 @@ static NGCardsSaxHandler *sax = nil; havingValue: (NSString *) aValue { return [children cardElementsWithAttribute: anAttribute - havingValue: aValue]; + havingValue: aValue]; } - (NSArray *) childrenWithTag: (NSString *) aTag @@ -293,7 +293,7 @@ static NGCardsSaxHandler *sax = nil; elements = [self childrenWithTag: aTag]; return [elements cardElementsWithAttribute: anAttribute - havingValue: aValue]; + havingValue: aValue]; } - (NSArray *) childrenGroupWithTag: (NSString *) aTag @@ -313,7 +313,8 @@ static NGCardsSaxHandler *sax = nil; { if ([element isKindOfClass: [CardGroup class]]) { - value = [[element uniqueChildWithTag: aChild] value: 0]; + value = [[element uniqueChildWithTag: aChild] + flattenedValuesForKey: @""]; if ([value isEqualToString: aValue]) [elements addObject: element]; } diff --git a/SOPE/NGCards/CardVersitRenderer.m b/SOPE/NGCards/CardVersitRenderer.m index 419aabf43..9c4521d4a 100644 --- a/SOPE/NGCards/CardVersitRenderer.m +++ b/SOPE/NGCards/CardVersitRenderer.m @@ -31,7 +31,7 @@ #import "CardGroup.h" #import "NSString+NGCards.h" -#import "NSArray+NGCards.h" +#import "NSDictionary+NGCards.h" #import "CardVersitRenderer.h" @@ -55,9 +55,8 @@ { NSMutableString *rendering; NSDictionary *attributes; - NSEnumerator *keys; - NSArray *values, *renderedAttrs; - NSString *key, *finalRendering, *tag; + NSMutableDictionary *values; + NSString *finalRendering, *tag; if (![anElement isVoid]) { @@ -140,12 +139,8 @@ groupTag = [groupTag uppercaseString]; [rendering appendFormat: @"BEGIN:%@\r\n", groupTag]; children = [[aGroup children] objectEnumerator]; - currentChild = [children nextObject]; - while (currentChild) - { - [rendering appendString: [self render: currentChild]]; - currentChild = [children nextObject]; - } + while ((currentChild = [children nextObject])) + [rendering appendString: [self render: currentChild]]; [rendering appendFormat: @"END:%@\r\n", groupTag]; return rendering; diff --git a/SOPE/NGCards/NGCardsSaxHandler.h b/SOPE/NGCards/NGCardsSaxHandler.h index 189fc777a..0dc2fa354 100644 --- a/SOPE/NGCards/NGCardsSaxHandler.h +++ b/SOPE/NGCards/NGCardsSaxHandler.h @@ -69,7 +69,7 @@ /* content */ - (void) startCollectingContent; -- (NSArray *) finishCollectingContent; +- (NSMutableDictionary *) finishCollectingContent; - (void) startGroupElement: (NSString *) _localName; - (void) endGroupElement; diff --git a/SOPE/NGCards/NGCardsSaxHandler.m b/SOPE/NGCards/NGCardsSaxHandler.m index cf5beac68..f8cddc53e 100644 --- a/SOPE/NGCards/NGCardsSaxHandler.m +++ b/SOPE/NGCards/NGCardsSaxHandler.m @@ -181,7 +181,7 @@ else if ([_localName isEqualToString: @"container"]) [self endGroupElement]; else - [currentElement addValues: [self finishCollectingContent]]; + [currentElement setValues: [self finishCollectingContent]]; } /* content */ @@ -197,9 +197,9 @@ vcs.collectContent = 1; } -- (NSArray *) finishCollectingContent +- (NSMutableDictionary *) finishCollectingContent { - NSArray *contentValues; + NSMutableDictionary *contentValues; NSString *s; vcs.collectContent = 0; @@ -211,7 +211,7 @@ free (content); content = NULL; // NSLog (@"content: '%@'", s); - contentValues = [s vCardSubvaluesWithSeparator: ';']; + contentValues = [s vCardSubvalues]; } else contentValues = nil; diff --git a/SOPE/NGCards/NGVCard.h b/SOPE/NGCards/NGVCard.h index 7e11bd6a0..1d3696c63 100644 --- a/SOPE/NGCards/NGVCard.h +++ b/SOPE/NGCards/NGVCard.h @@ -117,11 +117,12 @@ typedef enum additional: (NSString *) additional prefixes: (NSString *) prefixes suffixes: (NSString *) suffixes; -- (NSArray *) n; +/* returns an array of single values */ +- (CardElement *) n; - (void) setOrg: (NSString *) anOrg units: (NSArray *) someUnits; -- (NSArray *) org; +- (CardElement *) org; - (void) setCategories: (NSArray *) newCategories; - (NSArray *) categories; diff --git a/SOPE/NGCards/NGVCard.m b/SOPE/NGCards/NGVCard.m index 2d44cfd72..97bcc92a0 100644 --- a/SOPE/NGCards/NGVCard.m +++ b/SOPE/NGCards/NGVCard.m @@ -116,134 +116,134 @@ /* accessors */ -- (void) setVersion: (NSString *) _version +- (void) setVersion: (NSString *) _value { - [[self uniqueChildWithTag: @"version"] setValue: 0 to: _version]; + [[self uniqueChildWithTag: @"version"] setSingleValue: _value forKey: @""]; } - (NSString *) version { - return [[self uniqueChildWithTag: @"version"] value: 0]; + return [[self uniqueChildWithTag: @"version"] flattenedValuesForKey: @""]; } -- (void) setUid: (NSString *) _uid +- (void) setUid: (NSString *) _value { - [[self uniqueChildWithTag: @"uid"] setValue: 0 to: _uid]; + [[self uniqueChildWithTag: @"uid"] setSingleValue: _value forKey: @""]; } - (NSString *) uid { - return [[self uniqueChildWithTag: @"uid"] value: 0]; + return [[self uniqueChildWithTag: @"uid"] flattenedValuesForKey: @""]; } -- (void) setVClass: (NSString *) _class +- (void) setVClass: (NSString *) _value { - [[self uniqueChildWithTag: @"class"] setValue: 0 to: _class]; + [[self uniqueChildWithTag: @"class"] setSingleValue: _value forKey: @""]; } - (NSString *) vClass { - return [[self uniqueChildWithTag: @"class"] value: 0]; + return [[self uniqueChildWithTag: @"class"] flattenedValuesForKey: @""]; } - (void) setProdID: (NSString *) _value { - [[self uniqueChildWithTag: @"prodid"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"prodid"] setSingleValue: _value forKey: @""]; } - (NSString *) prodID { - return [[self uniqueChildWithTag: @"prodid"] value: 0]; + return [[self uniqueChildWithTag: @"prodid"] flattenedValuesForKey: @""]; } - (void) setProfile: (NSString *) _value { - [[self uniqueChildWithTag: @"profile"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"profile"] setSingleValue: _value forKey: @""]; } - (NSString *) profile { - return [[self uniqueChildWithTag: @"profile"] value: 0]; + return [[self uniqueChildWithTag: @"profile"] flattenedValuesForKey: @""]; } - (void) setSource: (NSString *) _value { - [[self uniqueChildWithTag: @"source"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"source"] setSingleValue: _value forKey: @""]; } - (NSString *) source { - return [[self uniqueChildWithTag: @"source"] value: 0]; + return [[self uniqueChildWithTag: @"source"] flattenedValuesForKey: @""]; } - (void) setFn: (NSString *) _value { - [[self uniqueChildWithTag: @"fn"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"fn"] setSingleValue: _value forKey: @""]; } - (NSString *) fn { - return [[self uniqueChildWithTag: @"fn"] value: 0]; + return [[self uniqueChildWithTag: @"fn"] flattenedValuesForKey: @""]; } - (void) setRole: (NSString *) _value { - [[self uniqueChildWithTag: @"role"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"role"] setSingleValue: _value forKey: @""]; } - (NSString *) role { - return [[self uniqueChildWithTag: @"role"] value: 0]; + return [[self uniqueChildWithTag: @"role"] flattenedValuesForKey: @""]; } -- (void) setTitle: (NSString *) _title +- (void) setTitle: (NSString *) _value { - [[self uniqueChildWithTag: @"title"] setValue: 0 to: _title]; + [[self uniqueChildWithTag: @"title"] setSingleValue: _value forKey: @""]; } - (NSString *) title { - return [[self uniqueChildWithTag: @"title"] value: 0]; + return [[self uniqueChildWithTag: @"title"] flattenedValuesForKey: @""]; } - (void) setBday: (NSString *) _value { - [[self uniqueChildWithTag: @"bday"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"bday"] setSingleValue: _value forKey: @""]; } - (NSString *) bday { - return [[self uniqueChildWithTag: @"bday"] value: 0]; + return [[self uniqueChildWithTag: @"bday"] flattenedValuesForKey: @""]; } - (void) setNote: (NSString *) _value { - [[self uniqueChildWithTag: @"note"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"note"] setSingleValue: _value forKey: @""]; } - (NSString *) note { - return [[self uniqueChildWithTag: @"note"] value: 0]; + return [[self uniqueChildWithTag: @"note"] flattenedValuesForKey: @""]; } - (void) setTz: (NSString *) _value { - [[self uniqueChildWithTag: @"tz"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"tz"] setSingleValue: _value forKey: @""]; } - (NSString *) tz { - return [[self uniqueChildWithTag: @"tz"] value: 0]; + return [[self uniqueChildWithTag: @"tz"] flattenedValuesForKey: @""]; } - (void) setNickname: (NSString *) _value { - [[self uniqueChildWithTag: @"nickname"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"nickname"] setSingleValue: _value forKey: @""]; } - (NSString *) nickname { - return [[self uniqueChildWithTag: @"nickname"] value: 0]; + return [[self uniqueChildWithTag: @"nickname"] flattenedValuesForKey: @""]; } - (void) addTel: (NSString *) phoneNumber @@ -270,20 +270,20 @@ n = [self uniqueChildWithTag: @"n"]; if (family) - [n setValue: 0 to: family]; + [n setSingleValue: family atIndex: 0 forKey: @""]; if (given) - [n setValue: 1 to: given]; + [n setSingleValue: given atIndex: 1 forKey: @""]; if (additional) - [n setValue: 2 to: additional]; + [n setSingleValue: additional atIndex: 2 forKey: @""]; if (prefixes) - [n setValue: 3 to: prefixes]; + [n setSingleValue: prefixes atIndex: 3 forKey: @""]; if (suffixes) - [n setValue: 4 to: suffixes]; + [n setSingleValue: suffixes atIndex: 4 forKey: @""]; } -- (NSArray *) n +- (CardElement *) n { - return [[self uniqueChildWithTag: @"n"] values]; + return [self uniqueChildWithTag: @"n"]; } - (void) setOrg: (NSString *) anOrg @@ -294,35 +294,30 @@ org = [self uniqueChildWithTag: @"org"]; if (anOrg) - [org setValue: 0 to: anOrg]; + [org setSingleValue: anOrg atIndex: 0 forKey: @""]; if (someUnits) { max = [someUnits count]; for (count = 0; count < max; count++) - [org setValue: count + 1 to: [someUnits objectAtIndex: count]]; + [org setSingleValue: [someUnits objectAtIndex: count] + atIndex: count + 1 forKey: @""]; } } -- (NSArray *) org +- (CardElement *) org { - NSArray *elements, *org; - - elements = [self childrenWithTag: @"org"]; - if ([elements count] > 0) - org = [[elements objectAtIndex: 0] values]; - else - org = nil; - - return org; + return [self uniqueChildWithTag: @"org"]; } - (void) setCategories: (NSArray *) newCategories { CardElement *cats; + NSMutableArray *copy; cats = [self uniqueChildWithTag: @"categories"]; - - [cats setCommaSeparatedValues: newCategories]; + copy = [newCategories mutableCopy]; + [cats setValues: copy atIndex: 0 forKey: @""]; + [copy release]; } - (NSArray *) categories @@ -331,7 +326,7 @@ cats = [self uniqueChildWithTag: @"categories"]; - return [[cats value: 0] vCardSubvaluesWithSeparator: ',']; + return [cats valuesAtIndex: 0 forKey: @""]; } // - (void) setOrg: (NGVCardOrg *) _v @@ -461,12 +456,12 @@ - (NSString *) preferredEMail { - return [[self _preferredElementWithTag: @"email"] value: 0]; + return [[self _preferredElementWithTag: @"email"] flattenedValuesForKey: @""]; } - (NSString *) preferredTel { - return [[self _preferredElementWithTag: @"tel"] value: 0]; + return [[self _preferredElementWithTag: @"tel"] flattenedValuesForKey: @""]; } - (CardElement *) preferredAdr diff --git a/SOPE/NGCards/NGVCardPhoto.m b/SOPE/NGCards/NGVCardPhoto.m index 263054f59..427d50389 100644 --- a/SOPE/NGCards/NGVCardPhoto.m +++ b/SOPE/NGCards/NGVCardPhoto.m @@ -21,6 +21,7 @@ */ #import +#import #import #import @@ -61,9 +62,11 @@ { /* We bypass -[values:] because we want to obtain the undecoded value first. */ - if ([values count] > 0) + if ([values count] > 0 && [[values objectForKey: @""] count] > 0 && + [[[values objectForKey: @""] objectAtIndex: 0] count] > 0) { - value = [values objectAtIndex: 0]; + value = [[[values objectForKey: @""] objectAtIndex: 0] + componentsJoinedByString: @","]; decodedContent = [value dataByDecodingBase64]; } else diff --git a/SOPE/NGCards/NGVCardReference.m b/SOPE/NGCards/NGVCardReference.m index 31da7fa51..389fcba7f 100644 --- a/SOPE/NGCards/NGVCardReference.m +++ b/SOPE/NGCards/NGVCardReference.m @@ -48,12 +48,12 @@ - (void) setReference: (NSString *) newReference { - [self setValue: 0 to: newReference]; + [self setSingleValue: newReference forKey: @""]; } - (NSString *) reference { - return [self value: 0]; + return [self flattenedValuesForKey: @""]; } @end diff --git a/SOPE/NGCards/NGVList.m b/SOPE/NGCards/NGVList.m index 3e85fa6ea..fc9d927b1 100644 --- a/SOPE/NGCards/NGVList.m +++ b/SOPE/NGCards/NGVList.m @@ -77,45 +77,45 @@ - (void) setProdID: (NSString *) newProdID { - [[self uniqueChildWithTag: @"prodid"] setValue: 0 to: newProdID]; + [[self uniqueChildWithTag: @"prodid"] setSingleValue: newProdID forKey: @""]; } - (NSString *) prodID { - return [[self uniqueChildWithTag: @"prodid"] value: 0]; + return [[self uniqueChildWithTag: @"prodid"] flattenedValuesForKey: @""]; } - (void) setVersion: (NSString *) newVersion { - [[self uniqueChildWithTag: @"version"] setValue: 0 - to: newVersion]; + [[self uniqueChildWithTag: @"version"] setSingleValue: newVersion + forKey: @""]; } - (NSString *) version { - return [[self uniqueChildWithTag: @"version"] value: 0]; + return [[self uniqueChildWithTag: @"version"] flattenedValuesForKey: @""]; } - (void) setUid: (NSString *) newUid { - [[self uniqueChildWithTag: @"uid"] setValue: 0 - to: newUid]; + [[self uniqueChildWithTag: @"uid"] setSingleValue: newUid + forKey: @""]; } - (NSString *) uid { - return [[self uniqueChildWithTag: @"uid"] value: 0]; + return [[self uniqueChildWithTag: @"uid"] flattenedValuesForKey: @""]; } - (void) setAccessClass: (NSString *) newAccessClass { - [[self uniqueChildWithTag: @"class"] setValue: 0 - to: newAccessClass]; + [[self uniqueChildWithTag: @"class"] setSingleValue: newAccessClass + forKey: @""]; } - (NSString *) accessClass { - return [[self uniqueChildWithTag: @"class"] value: 0]; + return [[self uniqueChildWithTag: @"class"] flattenedValuesForKey: @""]; } - (NGCardsAccessClass) symbolicAccessClass @@ -141,34 +141,34 @@ - (void) setFn: (NSString *) newFn { - [[self uniqueChildWithTag: @"fn"] setValue: 0 to: newFn]; + [[self uniqueChildWithTag: @"fn"] setSingleValue: newFn forKey: @""]; } - (NSString *) fn { - return [[self uniqueChildWithTag: @"fn"] value: 0]; + return [[self uniqueChildWithTag: @"fn"] flattenedValuesForKey: @""]; } - (void) setNickname: (NSString *) newNickname { - [[self uniqueChildWithTag: @"nickname"] setValue: 0 - to: newNickname]; + [[self uniqueChildWithTag: @"nickname"] setSingleValue: newNickname + forKey: @""]; } - (NSString *) nickname { - return [[self uniqueChildWithTag: @"nickname"] value: 0]; + return [[self uniqueChildWithTag: @"nickname"] flattenedValuesForKey: @""]; } - (void) setDescription: (NSString *) newDescription { - [[self uniqueChildWithTag: @"description"] setValue: 0 - to: newDescription]; + [[self uniqueChildWithTag: @"description"] setSingleValue: newDescription + forKey: @""]; } - (NSString *) description { - return [[self uniqueChildWithTag: @"description"] value: 0]; + return [[self uniqueChildWithTag: @"description"] flattenedValuesForKey: @""]; } - (void) addCardReference: (NGVCardReference *) newCardRef diff --git a/SOPE/NGCards/NSArray+NGCards.h b/SOPE/NGCards/NSArray+NGCards.h index 9b6c23ad0..d77965165 100644 --- a/SOPE/NGCards/NSArray+NGCards.h +++ b/SOPE/NGCards/NSArray+NGCards.h @@ -35,8 +35,6 @@ - (NSArray *) cardElementsWithAttribute: (NSString *) anAttribute havingValue: (NSString *) aValue; -- (NSArray *) renderedForCards; - @end #endif /* NSARRAY_NGCARDS_H */ diff --git a/SOPE/NGCards/NSArray+NGCards.m b/SOPE/NGCards/NSArray+NGCards.m index edda2f7a7..84ff07e38 100644 --- a/SOPE/NGCards/NSArray+NGCards.m +++ b/SOPE/NGCards/NSArray+NGCards.m @@ -99,32 +99,4 @@ return matchingElements; } -- (NSArray *) renderedForCards -{ - NSMutableArray *purified; - NSString *string; - int count, max, lastInsert; - - max = [self count]; - purified = [NSMutableArray arrayWithCapacity: max]; - - lastInsert = -1; - for (count = 0; count < max; count++) - { - string = [self objectAtIndex: count]; - if ([string length] > 0) - { - while (lastInsert < (count - 1)) - { - [purified addObject: @""]; - lastInsert++; - } - [purified addObject: [string escapedForCards]]; - lastInsert = count; - } - } - - return purified; -} - @end diff --git a/SOPE/NGCards/NSDictionary+NGCards.h b/SOPE/NGCards/NSDictionary+NGCards.h index 00249762c..8def7d6a1 100644 --- a/SOPE/NGCards/NSDictionary+NGCards.h +++ b/SOPE/NGCards/NSDictionary+NGCards.h @@ -29,6 +29,9 @@ - (id) objectForCaseInsensitiveKey: (NSString *) aKey; +- (void) versitRenderInString: (NSMutableString *) aString + asAttributes: (BOOL) asAttribute; /* handling of ":" */ + @end #endif /* NSDICTIONARY_NGCARDS_H */ diff --git a/SOPE/NGCards/NSDictionary+NGCards.m b/SOPE/NGCards/NSDictionary+NGCards.m index fe890085d..9360154bd 100644 --- a/SOPE/NGCards/NSDictionary+NGCards.m +++ b/SOPE/NGCards/NSDictionary+NGCards.m @@ -20,10 +20,95 @@ * Boston, MA 02111-1307, USA. */ +#import +#import + #import "NSArray+NGCards.h" +#import "NSString+NGCards.h" #import "NSDictionary+NGCards.h" +@interface NSArray (NGCardsVersit) + +- (BOOL) _renderAsSubValuesInString: (NSMutableString *) aString + asAttributes: (BOOL) asAttributes; +- (BOOL) _renderAsOrderedValuesInString: (NSMutableString *) aString + withKey: (NSString *) key; + +@end + +@implementation NSArray (NGCardsVersit) + +- (BOOL) _renderAsSubValuesInString: (NSMutableString *) aString + asAttributes: (BOOL) asAttributes +{ + NSUInteger count, max; + NSString *subValue, *escaped; + BOOL previousWasEmpty = YES, rendered = NO; + + max = [self count]; + for (count = 0; count < max; count++) + { + if (!previousWasEmpty) + [aString appendString: @","]; + subValue = [self objectAtIndex: count]; + + /* We MUST quote attribute values that have a ":" in them + and that not already quoted */ + if (asAttributes && [subValue length] > 2 + && [subValue rangeOfString: @":"].length + && [subValue characterAtIndex: 0] != '"' + && ![subValue hasSuffix: @"\""]) + subValue = [NSString stringWithFormat: @"\"%@\"", subValue]; + + escaped = [subValue escapedForCards]; + if ([escaped length] > 0) + { + [aString appendString: escaped]; + previousWasEmpty = NO; + rendered = YES; + } + else + previousWasEmpty = YES; + } + + return rendered; +} + +- (BOOL) _renderAsOrderedValuesInString: (NSMutableString *) aString + withKey: (NSString *) key +{ + NSUInteger count, max, lastRendered = 0; + BOOL rendered = NO; + NSArray *subValues; + NSMutableString *substring; + + max = [self count]; + for (count = 0; count < max; count++) + { + subValues = [self objectAtIndex: count]; + substring = [NSMutableString string]; + if ([subValues _renderAsSubValuesInString: substring + asAttributes: NO]) + { + if (lastRendered == 0 && [key length] > 0) + [aString appendFormat: @"%@=", key]; + + while (lastRendered < count) + { + [aString appendString: @";"]; + lastRendered++; + } + [aString appendString: substring]; + rendered = YES; + } + } + + return rendered; +} + +@end + @implementation NSDictionary (NGCardsExtension) - (id) objectForCaseInsensitiveKey: (NSString *) aKey @@ -37,4 +122,43 @@ return ((realKey) ? [self objectForKey: realKey] : nil); } +- (void) versitRenderInString: (NSMutableString *) aString + asAttributes: (BOOL) asAttributes +{ + NSArray *keys; + NSUInteger count, max, rendered = 0; + NSArray *orderedValues; + NSString *key; + NSMutableString *substring; + + keys = [self allKeys]; + max = [keys count]; + for (count = 0; count < max; count++) + { + key = [keys objectAtIndex: count]; + orderedValues = [self objectForKey: key]; + substring = [NSMutableString string]; + if (asAttributes) + { + if ([orderedValues _renderAsSubValuesInString: substring + asAttributes: YES]) + { + if (rendered > 0) + [aString appendString: @";"]; + [aString appendFormat: @"%@=%@", + [key uppercaseString], substring]; + rendered++; + } + } + else if ([orderedValues _renderAsOrderedValuesInString: substring + withKey: [key uppercaseString]]) + { + if (rendered > 0) + [aString appendString: @";"]; + [aString appendString: substring]; + rendered++; + } + } +} + @end diff --git a/SOPE/NGCards/NSString+NGCards.h b/SOPE/NGCards/NSString+NGCards.h index 1fdb9bfb0..b4cca93ff 100644 --- a/SOPE/NGCards/NSString+NGCards.h +++ b/SOPE/NGCards/NSString+NGCards.h @@ -27,6 +27,7 @@ @class NSArray; @class NSCalendarDate; +@class NSMutableDictionary; @class NSTimeZone; @interface NSString (NGCardsExtensions) @@ -40,7 +41,7 @@ - (NSCalendarDate *) asCalendarDate; - (BOOL) isAllDayDate; -- (NSArray *) vCardSubvaluesWithSeparator: (unichar) separator; +- (NSMutableDictionary *) vCardSubvalues; @end diff --git a/SOPE/NGCards/NSString+NGCards.m b/SOPE/NGCards/NSString+NGCards.m index 0387b9b6e..f27b964c3 100644 --- a/SOPE/NGCards/NSString+NGCards.m +++ b/SOPE/NGCards/NSString+NGCards.m @@ -21,6 +21,7 @@ */ #import +#import #import #import #import @@ -316,25 +317,35 @@ return ([self length] == 8); } -- (NSArray *) vCardSubvaluesWithSeparator: (unichar) separator +- (NSMutableDictionary *) vCardSubvalues { - NSMutableArray *components; + /* This schema enables things like this: + ELEM;...:KEY1=subvalue1;KEY2=subvalue1,subvalue2 + or + ELEM;...:subvalue1;subvalue1,subvalue2 (where KEY = @"") */ + NSMutableDictionary *values; /* key <> ordered values associations */ + NSMutableArray *orderedValues = nil; /* those are separated by ';' and contain + subvalues, may or may not be named */ + NSMutableArray *subValues = nil; /* those are separeted by ',' */ unichar *stringBuffer, *substringBuffer; - NSString *substring; + NSString *valuesKey, *substring; unichar currentChar; NSUInteger substringLength, count, max; - BOOL escaped; + BOOL escaped = NO; - components = [NSMutableArray arrayWithCapacity: 5]; + values = [NSMutableDictionary dictionary]; + valuesKey = @""; max = [self length]; - stringBuffer = NSZoneMalloc (NULL, sizeof (unichar) * max); + stringBuffer = NSZoneMalloc (NULL, sizeof (unichar) * max + 1); [self getCharacters: stringBuffer]; - substringLength = 0; - escaped = NO; + stringBuffer[max] = 0; substringBuffer = NSZoneMalloc (NULL, sizeof (unichar) * max); + substringLength = 0; + max += 1; /* we add one step to force the inclusion of the ending '\0' in + the loop */ for (count = 0; count < max; count++) { currentChar = stringBuffer[count]; @@ -343,7 +354,7 @@ escaped = NO; if (currentChar == 'n' || currentChar == 'N') substringBuffer[substringLength] = '\n'; - else if (currentChar == 'r') + else if (currentChar == 'r' || currentChar == 'R') substringBuffer[substringLength] = '\r'; else substringBuffer[substringLength] = currentChar; @@ -353,16 +364,48 @@ { if (currentChar == '\\') escaped = YES; - else if (currentChar == separator) + else if (currentChar == ',' || currentChar == ';' || currentChar == 0) { substring - = [[NSString alloc] initWithCharactersNoCopy: substringBuffer - length: substringLength - freeWhenDone: YES]; - [components addObject: substring]; - [substring release]; - substringBuffer = NSZoneMalloc (NULL, sizeof (unichar) * max); + = [[NSString alloc] initWithCharacters: substringBuffer + length: substringLength]; substringLength = 0; + + orderedValues = [values objectForKey: valuesKey]; + if (!orderedValues) + { + orderedValues = [NSMutableArray new]; + [values setObject: orderedValues forKey: valuesKey]; + [orderedValues release]; + } + if (!subValues) + { + subValues = [NSMutableArray new]; + [orderedValues addObject: subValues]; + [subValues release]; + } + if ([substring length] > 0) + [subValues addObject: substring]; + [substring release]; + + if (currentChar != ',') + { + orderedValues = nil; + subValues = nil; + valuesKey = @""; + } + } + /* hack: 16 chars is an arbitrary limit to distinguish between + "named properties" and the base64 padding character. This might + need further tweaking... */ + else if (currentChar == '=' && substringLength < 16) + { + substring + = [[NSString alloc] initWithCharacters: substringBuffer + length: substringLength]; + [substring autorelease]; + substringLength = 0; + valuesKey = [substring lowercaseString]; } else { @@ -372,21 +415,12 @@ } } - if (substringLength > 0) - { - substring = [[NSString alloc] initWithCharactersNoCopy: substringBuffer - length: substringLength - freeWhenDone: YES]; - [components addObject: substring]; - [substring release]; - } - NSZoneFree (NULL, stringBuffer); + NSZoneFree (NULL, substringBuffer); - return components; + return values; } - - (NSString *) rfc822Email { unsigned idx; diff --git a/SOPE/NGCards/iCalAlarm.m b/SOPE/NGCards/iCalAlarm.m index 2eab33947..7922f567a 100644 --- a/SOPE/NGCards/iCalAlarm.m +++ b/SOPE/NGCards/iCalAlarm.m @@ -82,41 +82,41 @@ - (void) setAction: (NSString *) _value { - [[self uniqueChildWithTag: @"action"] setValue: 0 - to: _value]; + [[self uniqueChildWithTag: @"action"] setSingleValue: _value + forKey: @""]; } - (NSString *) action { - return [[self uniqueChildWithTag: @"action"] value: 0]; + return [[self uniqueChildWithTag: @"action"] flattenedValuesForKey: @""]; } - (void) setSummary: (NSString *) _value { - [[self uniqueChildWithTag: @"summary"] setValue: 0 - to: _value]; + [[self uniqueChildWithTag: @"summary"] setSingleValue: _value + forKey: @""]; } - (NSString *) summary { - return [[self uniqueChildWithTag: @"summary"] value: 0]; + return [[self uniqueChildWithTag: @"summary"] flattenedValuesForKey: @""]; } - (void) setComment: (NSString *) _value { - [[self uniqueChildWithTag: @"description"] setValue: 0 - to: _value]; + [[self uniqueChildWithTag: @"description"] setSingleValue: _value + forKey: @""]; } - (NSString *) comment { - return [[self uniqueChildWithTag: @"description"] value: 0]; + return [[self uniqueChildWithTag: @"description"] flattenedValuesForKey: @""]; } -- (void) setRecurrenceRule: (NSString *) _recurrenceRule +- (void) setRecurrenceRule: (NSString *) _value { - [[self uniqueChildWithTag: @"rrule"] setValue: 0 - to: _recurrenceRule]; + [[self uniqueChildWithTag: @"rrule"] setSingleValue: _value + forKey: @""]; } - (void) setAttendees: (NSArray *) attendees @@ -137,7 +137,7 @@ - (NSString *) recurrenceRule { - return [[self uniqueChildWithTag: @"rrule"] value: 0]; + return [[self uniqueChildWithTag: @"rrule"] flattenedValuesForKey: @""]; } - (NSCalendarDate *) nextAlarmDate @@ -161,7 +161,8 @@ == NSOrderedSame) { relation = [aTrigger relationType]; - anInterval = [[aTrigger value] durationAsTimeInterval]; + anInterval = [[aTrigger flattenedValuesForKey: @""] + durationAsTimeInterval]; if ([relation caseInsensitiveCompare: @"END"] == NSOrderedSame) { if ([parent isKindOfClass: [iCalEvent class]]) diff --git a/SOPE/NGCards/iCalAttachment.h b/SOPE/NGCards/iCalAttachment.h index 0b326b7ca..ccba3cdb2 100644 --- a/SOPE/NGCards/iCalAttachment.h +++ b/SOPE/NGCards/iCalAttachment.h @@ -28,9 +28,6 @@ @interface iCalAttachment : CardElement -- (void) setValue: (NSString *) aValue; -- (NSString *) value; - - (void) setValueType: (NSString *) aType; - (NSString *) valueType; diff --git a/SOPE/NGCards/iCalAttachment.m b/SOPE/NGCards/iCalAttachment.m index 6963abd69..c46084986 100644 --- a/SOPE/NGCards/iCalAttachment.m +++ b/SOPE/NGCards/iCalAttachment.m @@ -26,16 +26,6 @@ /* accessors */ -- (void) setValue: (NSString *) _value -{ - [self setValue: 0 to: _value]; -} - -- (NSString *) value -{ - return [self value: 0]; -} - - (void) setValueType: (NSString *) _value { [self setValue: 0 ofAttribute: @"type" to: _value]; diff --git a/SOPE/NGCards/iCalCalendar.m b/SOPE/NGCards/iCalCalendar.m index da567a1d5..75b266641 100644 --- a/SOPE/NGCards/iCalCalendar.m +++ b/SOPE/NGCards/iCalCalendar.m @@ -69,45 +69,44 @@ /* accessors */ -- (void) setCalscale: (NSString *) _calscale +- (void) setCalscale: (NSString *) _value { - [[self uniqueChildWithTag: @"calscale"] setValue: 0 to: _calscale]; + [[self uniqueChildWithTag: @"calscale"] setSingleValue: _value forKey: @""]; } - (NSString *) calscale { - return [[self uniqueChildWithTag: @"calscale"] value: 0]; + return [[self uniqueChildWithTag: @"calscale"] flattenedValuesForKey: @""]; } -- (void) setVersion: (NSString *) _version +- (void) setVersion: (NSString *) _value { - [[self uniqueChildWithTag: @"version"] setValue: 0 to: _version]; + [[self uniqueChildWithTag: @"version"] setSingleValue: _value forKey: @""]; } - (NSString *) version { - return [[self uniqueChildWithTag: @"version"] value: 0]; + return [[self uniqueChildWithTag: @"version"] flattenedValuesForKey: @""]; } - (void) setProdID: (NSString *) _value { - [[self uniqueChildWithTag: @"prodid"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"prodid"] setSingleValue: _value forKey: @""]; } - (NSString *) prodId { - return [[self uniqueChildWithTag: @"prodid"] value: 0]; + return [[self uniqueChildWithTag: @"prodid"] flattenedValuesForKey: @""]; } - (void) setMethod: (NSString *) _value { - [[self uniqueChildWithTag: @"method"] setValue: 0 - to: [_value uppercaseString]]; + [[self uniqueChildWithTag: @"method"] setSingleValue: _value forKey: @""]; } - (NSString *) method { - return [[self uniqueChildWithTag: @"method"] value: 0]; + return [[self uniqueChildWithTag: @"method"] flattenedValuesForKey: @""]; } - (void) addToEvents: (iCalEvent *) _event diff --git a/SOPE/NGCards/iCalDateTime.m b/SOPE/NGCards/iCalDateTime.m index f572f9881..251c46d0a 100644 --- a/SOPE/NGCards/iCalDateTime.m +++ b/SOPE/NGCards/iCalDateTime.m @@ -137,7 +137,7 @@ else timeString = @""; - [self setValue: 0 to: timeString]; + [self setSingleValue: timeString forKey: @""]; } - (void) setDateTime: (NSCalendarDate *) dateTime @@ -160,15 +160,17 @@ iCalTimeZone *iTZ; NSString *date; NSCalendarDate *initialDate, *dateTime; + NSArray *subValues; NSMutableArray *dates; //NSTimeZone *tz; unsigned count, i; - count = [[self values] count]; + subValues = [self valuesAtIndex: 0 forKey: @""]; + count = [subValues count]; dates = [NSMutableArray arrayWithCapacity: count]; for (i = 0; i < count; i++) { - date = [self value: i]; + date = [subValues objectAtIndex: i]; iTZ = [self timeZone]; if (iTZ) @@ -204,7 +206,7 @@ - (BOOL) isAllDay { - return [[self value: 0] isAllDayDate]; + return [[self flattenedValuesForKey: @""] isAllDayDate]; } @end diff --git a/SOPE/NGCards/iCalEntityObject.h b/SOPE/NGCards/iCalEntityObject.h index 36491e206..0fd9dc4f2 100644 --- a/SOPE/NGCards/iCalEntityObject.h +++ b/SOPE/NGCards/iCalEntityObject.h @@ -67,8 +67,8 @@ typedef enum - (void) setPriority: (NSString *) _value; - (NSString *) priority; -- (void) setCategories: (NSString *) _value; -- (NSString *)categories; +- (void) setCategories: (NSArray *) _value; +- (NSArray *) categories; - (void) setUserComment: (NSString *) _userComment; - (NSString *) userComment; diff --git a/SOPE/NGCards/iCalEntityObject.m b/SOPE/NGCards/iCalEntityObject.m index bbe7fa262..d28a4f78a 100644 --- a/SOPE/NGCards/iCalEntityObject.m +++ b/SOPE/NGCards/iCalEntityObject.m @@ -145,42 +145,47 @@ - (void) setPriority: (NSString *) _value { - [[self uniqueChildWithTag: @"priority"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"priority"] setSingleValue: _value forKey: @""]; } - (NSString *) priority { - return [[self uniqueChildWithTag: @"priority"] value: 0]; + return [[self uniqueChildWithTag: @"priority"] flattenedValuesForKey: @""]; } -- (void) setCategories: (NSString *) _value +- (void) setCategories: (NSArray *) _value { - [[self uniqueChildWithTag: @"categories"] setValue: 0 to: _value]; + NSMutableArray *copy; + + copy = [_value mutableCopy]; + [[self uniqueChildWithTag: @"categories"] setValues: copy atIndex: 0 + forKey: @""]; + [copy release]; } -- (NSString *) categories +- (NSArray *) categories { - return [[self uniqueChildWithTag: @"categories"] value: 0]; + return [[self uniqueChildWithTag: @"categories"] valuesAtIndex: 0 forKey: @""]; } - (void) setUserComment: (NSString *) _value { - [[self uniqueChildWithTag: @"usercomment"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"usercomment"] setSingleValue: _value forKey: @""]; } - (NSString *) userComment { - return [[self uniqueChildWithTag: @"usercomment"] value: 0]; + return [[self uniqueChildWithTag: @"usercomment"] flattenedValuesForKey: @""]; } - (void) setStatus: (NSString *) _value { - [[self uniqueChildWithTag: @"status"] setValue: 0 to: _value]; + [[self uniqueChildWithTag: @"status"] setSingleValue: _value forKey: @""]; } - (NSString *) status { - return [[self uniqueChildWithTag: @"status"] value: 0]; + return [[self uniqueChildWithTag: @"status"] flattenedValuesForKey: @""]; } - (void) setSequence: (NSNumber *)_value @@ -188,15 +193,15 @@ NSString *sequence; sequence = [NSString stringWithFormat: @"%@", _value]; - [[self uniqueChildWithTag: @"sequence"] setValue: 0 - to: sequence];; + [[self uniqueChildWithTag: @"sequence"] setSingleValue: sequence + forKey: @""]; } - (NSNumber *) sequence { NSString *sequence; - sequence = [[self uniqueChildWithTag: @"sequence"] value: 0]; + sequence = [[self uniqueChildWithTag: @"sequence"] flattenedValuesForKey: @""]; return [NSNumber numberWithInt: [sequence intValue]]; } @@ -390,16 +395,16 @@ - (void) setAttach: (id) _value { - NSString *asString; + NSString *aString; if ([_value isKindOfClass: [NSString class]]) - asString = _value; + aString = _value; else if ([_value isKindOfClass: [NSURL class]]) - asString = [_value absoluteString]; + aString = [_value absoluteString]; else - asString = @""; + aString = @""; - [[self uniqueChildWithTag: @"attach"] setValue: 0 to: asString]; + [[self uniqueChildWithTag: @"attach"] setSingleValue: aString forKey: @""]; } - (NSURL *) attach @@ -407,7 +412,7 @@ NSString *stringAttach; NSURL *url; - stringAttach = [[self uniqueChildWithTag: @"attach"] value: 0]; + stringAttach = [[self uniqueChildWithTag: @"attach"] flattenedValuesForKey: @""]; url = [NSURL URLWithString: stringAttach]; if (!url && [stringAttach length] > 0) @@ -418,23 +423,23 @@ - (void) setUrl: (id) _value { - NSString *asString; + NSString *aString; if ([_value isKindOfClass: [NSString class]]) - asString = _value; + aString = _value; else if ([_value isKindOfClass: [NSURL class]]) - asString = [_value absoluteString]; + aString = [_value absoluteString]; else - asString = @""; + aString = @""; - [[self uniqueChildWithTag: @"url"] setValue: 0 to: asString]; + [[self uniqueChildWithTag: @"url"] setSingleValue: aString forKey: @""]; } - (NSURL *) url { NSString *stringUrl; - stringUrl = [[self uniqueChildWithTag: @"url"] value: 0]; + stringUrl = [[self uniqueChildWithTag: @"url"] flattenedValuesForKey: @""]; return [NSURL URLWithString: stringUrl]; } diff --git a/SOPE/NGCards/iCalEvent.m b/SOPE/NGCards/iCalEvent.m index e8754c869..9d230a2d7 100644 --- a/SOPE/NGCards/iCalEvent.m +++ b/SOPE/NGCards/iCalEvent.m @@ -104,13 +104,12 @@ - (void) setDuration: (NSString *) _value { - [[self uniqueChildWithTag: @"duration"] setValue: 0 - to: _value]; + [[self uniqueChildWithTag: @"duration"] setSingleValue: _value forKey: @""]; } - (NSString *) duration { - return [[self uniqueChildWithTag: @"duration"] value: 0]; + return [[self uniqueChildWithTag: @"duration"] flattenedValuesForKey: @""]; } - (BOOL) hasDuration @@ -148,15 +147,14 @@ return interval; } -- (void) setTransparency: (NSString *) _transparency +- (void) setTransparency: (NSString *) _value { - [[self uniqueChildWithTag: @"transp"] setValue: 0 - to: _transparency]; + [[self uniqueChildWithTag: @"transp"] setSingleValue: _value forKey: @""]; } - (NSString *) transparency { - return [[self uniqueChildWithTag: @"transp"] value: 0]; + return [[self uniqueChildWithTag: @"transp"] flattenedValuesForKey: @""]; } /* convenience */ diff --git a/SOPE/NGCards/iCalPerson.m b/SOPE/NGCards/iCalPerson.m index 9fc728e02..fbcc92570 100644 --- a/SOPE/NGCards/iCalPerson.m +++ b/SOPE/NGCards/iCalPerson.m @@ -57,13 +57,13 @@ { /* iCal.app compatibility: - "mailto" prefix must be in lowercase; */ - [self setValue: 0 - to: [NSString stringWithFormat: @"mailto:%@", _s]]; + [self setSingleValue: [NSString stringWithFormat: @"mailto:%@", _s] + forKey: @""]; } - (NSString *) email { - return [self value: 0]; + return [self flattenedValuesForKey: @""]; } - (NSString *) rfc822Email diff --git a/SOPE/NGCards/iCalRecurrenceRule.m b/SOPE/NGCards/iCalRecurrenceRule.m index 03238e42f..ff66370ab 100644 --- a/SOPE/NGCards/iCalRecurrenceRule.m +++ b/SOPE/NGCards/iCalRecurrenceRule.m @@ -194,6 +194,7 @@ #import "NSCalendarDate+NGCards.h" #import "NSString+NGCards.h" +#import "CardGroup.h" #import "iCalByDayMask.h" #import "iCalRecurrenceRule.h" @@ -234,8 +235,7 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", iCalRecurrenceRule *rule; rule = [self elementWithTag: @"rrule"]; - if ([_iCalRep length] > 0) - [rule addValues: [_iCalRep componentsSeparatedByString: @";"]]; + [rule setRrule: _iCalRep]; return rule; } @@ -269,15 +269,19 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", - (void) setRrule: (NSString *) _rrule { - NSEnumerator *newValues; - NSString *newValue; + CardGroup *mockParent; + NSString *wrappedRule; + CardElement *mockRule; - newValues = [[_rrule componentsSeparatedByString: @";"] objectEnumerator]; - newValue = [newValues nextObject]; - while (newValue) + if ([_rrule length] > 0) { - [self addValue: newValue]; - newValue = [newValues nextObject]; + wrappedRule = [NSString stringWithFormat: + @"BEGIN:MOCK\r\nRRULE:%@\r\nEND:MOCK", + _rrule]; + mockParent = [CardGroup parseSingleFromSource: wrappedRule]; + mockRule = [mockParent uniqueChildWithTag: @"rrule"]; + [values release]; + values = [[mockRule values] mutableCopy]; } } @@ -350,75 +354,73 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", - (void) setFrequency: (iCalRecurrenceFrequency) _frequency { - [self setNamedValue: @"freq" to: [self frequencyForValue: _frequency]]; + [self setSingleValue: [self frequencyForValue: _frequency] forKey: @"freq"]; } - (iCalRecurrenceFrequency) frequency { - return [self valueForFrequency: [self namedValue: @"freq"]]; -} - -- (void) setRepeatCount: (int) _repeatCount -{ - [self setNamedValue: @"count" - to: [NSString stringWithFormat: @"%d", _repeatCount]]; -} - -- (int) repeatCount -{ - return [[self namedValue: @"count"] intValue]; + return [self valueForFrequency: [self flattenedValuesForKey: @"freq"]]; } - (void) setUntilDate: (NSCalendarDate *) _untilDate { - [self setNamedValue: @"until" - to: [_untilDate icalString]]; + [self setSingleValue: [_untilDate icalString] forKey: @"until"]; } - (NSCalendarDate *) untilDate { #warning handling of default timezone needs to be implemented - return [[self namedValue: @"until"] asCalendarDate]; + return [[self flattenedValuesForKey: @"until"] asCalendarDate]; } - (void) setInterval: (NSString *) _interval { - if (_interval && [_interval intValue] == 1) - [self setNamedValue: @"interval" to: nil]; + if ([_interval intValue] < 2) + [self setSingleValue: nil forKey: @"interval"]; else - [self setNamedValue: @"interval" to: _interval]; -} - -- (void) setCount: (NSString *) _count -{ - [self setNamedValue: @"count" to: _count]; -} - -- (void) setUntil: (NSString *) _until -{ - [self setNamedValue: @"until" to: _until]; + [self setSingleValue: _interval forKey: @"interval"]; } - (void) setRepeatInterval: (int) _repeatInterval { - [self setNamedValue: @"interval" - to: [NSString stringWithFormat: @"%d", _repeatInterval]]; + [self setInterval: [NSString stringWithFormat: @"%d", _repeatInterval]]; } - (int) repeatInterval { int interval; - interval = [[self namedValue: @"interval"] intValue]; + interval = [[self flattenedValuesForKey: @"interval"] intValue]; if (interval < 1) interval = 1; return interval; } +- (void) setRepeatCount: (int) _repeatCount +{ + [self setSingleValue: [NSString stringWithFormat: @"%d", _repeatCount] + forKey: @"count"]; +} + +- (int) repeatCount +{ + return [[self flattenedValuesForKey: @"count"] intValue]; +} + +- (void) setCount: (NSString *) _count +{ + [self setSingleValue: _count forKey: @"count"]; +} + +- (void) setUntil: (NSString *) _until +{ + [self setSingleValue: _until forKey: @"until"]; +} + - (void) setWkst: (NSString *) _weekStart { - [self setNamedValue: @"wkst" to: _weekStart]; + [self setSingleValue: _weekStart forKey: @"wkst"]; } #warning we also should handle the user weekstarts @@ -426,7 +428,7 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", { NSString *start; - start = [self namedValue: @"wkst"]; + start = [self flattenedValuesForKey: @"wkst"]; if (![start length]) start = @"MO"; @@ -445,12 +447,16 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", - (void) setByDay: (NSString *) newByDay { - [self setNamedValue: @"byday" to: newByDay]; + NSMutableArray *byDays; + + byDays = [[newByDay componentsSeparatedByString: @","] mutableCopy]; + [self setValues: byDays atIndex: 0 forKey: @"byday"]; + [byDays release]; } - (NSString *) byDay { - return [self namedValue: @"byday"]; + return [self flattenedValuesForKey: @"byday"]; } - (void) setByDayMask: (iCalByDayMask *) newByDayMask @@ -472,12 +478,9 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", - (NSArray *) byMonthDay { NSArray *byMonthDay; - NSString *byMonthDayStr; - byMonthDayStr = [self namedValue: @"bymonthday"]; - if ([byMonthDayStr length]) - byMonthDay = [byMonthDayStr componentsSeparatedByString: @","]; - else + byMonthDay = [self valuesAtIndex: 0 forKey: @"bymonthday"]; + if (![byMonthDay count]) byMonthDay = nil; return byMonthDay; @@ -486,12 +489,9 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", - (NSArray *) byMonth { NSArray *byMonth; - NSString *byMonthStr; - byMonthStr = [self namedValue: @"bymonth"]; - if ([byMonthStr length]) - byMonth = [byMonthStr componentsSeparatedByString: @","]; - else + byMonth = [self valuesAtIndex: 0 forKey: @"bymonth"]; + if (![byMonth count]) byMonth = nil; return byMonth; @@ -507,9 +507,9 @@ NSString *iCalWeekDayString[] = { @"SU", @"MO", @"TU", @"WE", @"TH", @"FR", * - BYSECOND * - BYSETPOS */ - return ([[self namedValue: @"bymonthday"] length] || - [[self namedValue: @"byday"] length] || - [[self namedValue: @"bymonth"] length]); + return ([[self valuesAtIndex: 0 forKey: @"bymonthday"] count] || + [[self valuesAtIndex: 0 forKey: @"byday"] count] || + [[self valuesAtIndex: 0 forKey: @"bymonth"] count]); } - (BOOL) isInfinite diff --git a/SOPE/NGCards/iCalRepeatableEntityObject.m b/SOPE/NGCards/iCalRepeatableEntityObject.m index a6e485a86..b2e9a00e6 100644 --- a/SOPE/NGCards/iCalRepeatableEntityObject.m +++ b/SOPE/NGCards/iCalRepeatableEntityObject.m @@ -247,7 +247,7 @@ while ((dateString = [dateList nextObject])) { - exDates = [(iCalDateTime*) dateString values]; + exDates = [(iCalDateTime*) dateString valuesAtIndex: 0 forKey: @""]; for (i = 0; i < [exDates count]; i++) { dateString = [exDates objectAtIndex: i]; diff --git a/SOPE/NGCards/iCalTimeZone.m b/SOPE/NGCards/iCalTimeZone.m index 510db0759..9e645af09 100644 --- a/SOPE/NGCards/iCalTimeZone.m +++ b/SOPE/NGCards/iCalTimeZone.m @@ -179,12 +179,12 @@ static NSArray *knownTimeZones; - (void) setTzId: (NSString *) tzId { - [[self uniqueChildWithTag: @"tzid"] setValue: 0 to: tzId]; + [[self uniqueChildWithTag: @"tzid"] setSingleValue: tzId forKey: @""]; } - (NSString *) tzId { - return [[self uniqueChildWithTag: @"tzid"] value: 0]; + return [[self uniqueChildWithTag: @"tzid"] flattenedValuesForKey: @""]; } - (NSCalendarDate *) _occurrenceForPeriodNamed: (NSString *) pName diff --git a/SOPE/NGCards/iCalTimeZonePeriod.m b/SOPE/NGCards/iCalTimeZonePeriod.m index 7a774ca99..b161f55ab 100644 --- a/SOPE/NGCards/iCalTimeZonePeriod.m +++ b/SOPE/NGCards/iCalTimeZonePeriod.m @@ -20,6 +20,7 @@ * Boston, MA 02111-1307, USA. */ +#import #import #import #import @@ -63,7 +64,7 @@ seconds = 0; offsetTo = [[self uniqueChildWithTag: offsetName] - value: 0]; + flattenedValuesForKey: @""]; length = [offsetTo length]; negative = [offsetTo hasPrefix: @"-"]; if (negative) @@ -140,7 +141,7 @@ [tzStart setTimeZone: [NSTimeZone timeZoneWithName: @"GMT"]]; tmpDate = [NSCalendarDate dateWithYear: [refDate yearOfCommonEra] - month: [[rrule namedValue: @"bymonth"] intValue] + month: [[[rrule byMonth] objectAtIndex: 0] intValue] day: 1 hour: [tzStart hourOfDay] minute: [tzStart minuteOfHour] second: 0 timeZone: [NSTimeZone timeZoneWithName: @"GMT"]]; diff --git a/SOPE/NGCards/iCalToDo.m b/SOPE/NGCards/iCalToDo.m index 21a01560b..08f5d3f7b 100644 --- a/SOPE/NGCards/iCalToDo.m +++ b/SOPE/NGCards/iCalToDo.m @@ -47,13 +47,14 @@ - (void) setPercentComplete: (NSString *) _value { - [[self uniqueChildWithTag: @"percent-complete"] setValue: 0 - to: _value]; + [[self uniqueChildWithTag: @"percent-complete"] setSingleValue: _value + forKey: @""]; } - (NSString *) percentComplete { - return [[self uniqueChildWithTag: @"percent-complete"] value: 0]; + return [[self uniqueChildWithTag: @"percent-complete"] + flattenedValuesForKey: @""]; } - (void) setDue: (NSCalendarDate *) newDueDate diff --git a/SOPE/NGCards/iCalTrigger.h b/SOPE/NGCards/iCalTrigger.h index a7e12f39a..e1a446fcd 100644 --- a/SOPE/NGCards/iCalTrigger.h +++ b/SOPE/NGCards/iCalTrigger.h @@ -26,9 +26,6 @@ @interface iCalTrigger : CardElement -- (void) setValue: (NSString *) theValue; -- (NSString *) value; - - (void) setValueType: (NSString *) theType; - (NSString *) valueType; diff --git a/SOPE/NGCards/iCalTrigger.m b/SOPE/NGCards/iCalTrigger.m index f9e590e0b..11efcd4e2 100644 --- a/SOPE/NGCards/iCalTrigger.m +++ b/SOPE/NGCards/iCalTrigger.m @@ -27,16 +27,6 @@ /* accessors */ -- (void) setValue: (NSString *) theValue -{ - [self setValue: 0 to: theValue]; -} - -- (NSString *) value -{ - return [self value: 0]; -} - - (void) setValueType: (NSString *) theValue { [self setValue: 0 ofAttribute: @"value" to: theValue]; diff --git a/SOPE/NGCards/iCalXMLRenderer.m b/SOPE/NGCards/iCalXMLRenderer.m index 1911ef76a..b31f57018 100644 --- a/SOPE/NGCards/iCalXMLRenderer.m +++ b/SOPE/NGCards/iCalXMLRenderer.m @@ -21,7 +21,7 @@ */ /* This class implements most of the XML iCalendar spec as defined here: - http://tools.ietf.org/html/draft-daboo-et-al-icalendar-in-xml-04 */ + http://tools.ietf.org/html/rfc6321 */ #import #import @@ -161,28 +161,38 @@ - (NSString *) _xmlRenderValue { NSMutableString *rendering; - NSString *valueTag, *currentValue; - int count, max; - BOOL displayed; + NSArray *keys, *orderedValues, *subValues; + NSString *key, *valueTag; + NSUInteger count, max, oCount, oMax, sCount, sMax; + +#warning this code should be fix to comply better with the RFC + rendering = [NSMutableString stringWithCapacity: 64]; valueTag = [self xmlValueTag]; - rendering = [NSMutableString stringWithCapacity: 64]; - max = [values count]; - displayed = NO; + + keys = [values allKeys]; + max = [keys count]; for (count = 0; count < max; count++) { - currentValue = [[values objectAtIndex: count] - stringByEscapingXMLString]; - if ([currentValue length] > 0) + key = [keys objectAtIndex: count]; + orderedValues = [values objectForKey: key]; + oMax = [orderedValues count]; + for (oCount = 0; oCount < oMax; oCount++) { - if (!displayed) - { - [self _appendPaddingValues: count withTag: valueTag - intoString: rendering]; - displayed = YES; - } - [rendering appendFormat: @"<%@>%@", - valueTag, currentValue, valueTag]; + if ([key length] > 0) + [rendering appendFormat: @"<%@>", [key lowercaseString]]; + else + [rendering appendFormat: @"<%@>", valueTag]; + + subValues = [orderedValues objectAtIndex: oCount]; + sMax = [subValues count]; + for (sCount = 0; sCount < sMax; sCount++) + [rendering appendString: [[subValues objectAtIndex: sCount] stringByEscapingXMLString]]; + + if ([key length] > 0) + [rendering appendFormat: @"", [key lowercaseString]]; + else + [rendering appendFormat: @"", valueTag]; } } @@ -247,39 +257,39 @@ @end -@implementation iCalRecurrenceRule (iCalXMLExtension) +// @implementation iCalRecurrenceRule (iCalXMLExtension) -- (NSString *) _xmlRenderValue -{ - NSMutableString *rendering; - NSArray *valueParts; - NSString *valueTag, *currentValue; - int count, max; +// - (NSString *) _xmlRenderValue +// { +// NSMutableString *rendering; +// NSArray *valueParts; +// NSString *valueTag, *currentValue; +// int count, max; - max = [values count]; - rendering = [NSMutableString stringWithCapacity: 64]; - for (count = 0; count < max; count++) - { - currentValue = [[values objectAtIndex: count] - stringByEscapingXMLString]; - if ([currentValue length] > 0) - { - valueParts = [currentValue componentsSeparatedByString: @"="]; - if ([valueParts count] == 2) - { - valueTag = [[valueParts objectAtIndex: 0] lowercaseString]; - [rendering appendFormat: @"<%@>%@", - valueTag, - [valueParts objectAtIndex: 1], - valueTag]; - } - } - } +// max = [values count]; +// rendering = [NSMutableString stringWithCapacity: 64]; +// for (count = 0; count < max; count++) +// { +// currentValue = [[values objectAtIndex: count] +// stringByEscapingXMLString]; +// if ([currentValue length] > 0) +// { +// valueParts = [currentValue componentsSeparatedByString: @"="]; +// if ([valueParts count] == 2) +// { +// valueTag = [[valueParts objectAtIndex: 0] lowercaseString]; +// [rendering appendFormat: @"<%@>%@", +// valueTag, +// [valueParts objectAtIndex: 1], +// valueTag]; +// } +// } +// } - return rendering; -} +// return rendering; +// } -@end +// @end @implementation iCalUTCOffset (iCalXMLExtension) diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 5561a004e..37d24370a 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -857,8 +857,9 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir NSArray *rules, *exRules, *exDates, *ranges; NSArray *elements, *components; NSString *content; + NSCalendarDate *checkStartDate, *checkEndDate, *firstStartDate, + *firstEndDate; iCalDateTime *dtstart; - NSCalendarDate *checkStartDate, *checkEndDate, *firstStartDate, *firstEndDate; iCalEvent *component; iCalTimeZone *eventTimeZone; unsigned count, max, offset; @@ -898,8 +899,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir { // Retrieve the range of the first/master event component = [components objectAtIndex: 0]; - dtstart = (iCalDateTime *)[component uniqueChildWithTag: @"dtstart"]; - firstStartDate = [[[dtstart values] lastObject] asCalendarDate]; + dtstart = (iCalDateTime *) [component uniqueChildWithTag: @"dtstart"]; + firstStartDate = [dtstart dateTime]; firstEndDate = [firstStartDate addTimeInterval: [component occurenceInterval]]; firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: firstStartDate @@ -2655,7 +2656,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir tz = nil; element = [components objectAtIndex: i]; // Use the timezone of the start date. - startDate = (iCalDateTime *)[element uniqueChildWithTag: @"dtstart"]; + startDate = (iCalDateTime *) [element uniqueChildWithTag: @"dtstart"]; if (startDate) { timezone = [startDate timeZone]; diff --git a/SoObjects/Appointments/iCalEvent+SOGo.m b/SoObjects/Appointments/iCalEvent+SOGo.m index 4cb974c1e..a9b4e1a76 100644 --- a/SoObjects/Appointments/iCalEvent+SOGo.m +++ b/SoObjects/Appointments/iCalEvent+SOGo.m @@ -271,21 +271,16 @@ */ - (NGCalendarDateRange *) firstOccurenceRange { - iCalDateTime *firstStartDate; NSCalendarDate *start, *end; NGCalendarDateRange *firstRange; firstRange = nil; - firstStartDate = (iCalDateTime *)[self uniqueChildWithTag: @"dtstart"]; - if ([[firstStartDate values] count] > 0) - { - start = [[[firstStartDate values] lastObject] asCalendarDate]; - end = [start addTimeInterval: [self occurenceInterval]]; + start = [self startDate]; + end = [start addTimeInterval: [self occurenceInterval]]; - firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start - endDate: end]; - } + firstRange = [NGCalendarDateRange calendarDateRangeWithStartDate: start + endDate: end]; return firstRange; } diff --git a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m index 00276a1d6..e57d02faf 100644 --- a/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m +++ b/SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m @@ -27,10 +27,12 @@ #import #import +#import #import #import #import #import +#import #import #import "iCalRepeatableEntityObject+SOGo.h" @@ -40,10 +42,12 @@ - (NSArray *) _indexedRules: (NSArray *) rules { NSMutableArray *ma; - unsigned int i, count; - NSString *valuesString; + NSUInteger i, count; + NSMutableString *ruleString; iCalRecurrenceRule *rule; +#warning we could return an NSArray instead and feed it as such to the iCalRecurrenceRule in SOGoAppointmentFolder... + ma = nil; count = [rules count]; @@ -53,9 +57,11 @@ for (i = 0; i < count; i++) { rule = [rules objectAtIndex:i]; -#warning we could return an NSArray instead and feed it as such to the iCalRecurrenceRule in SOGoAppointmentFolder... - valuesString = [[rule values] componentsJoinedByString: @";"]; - [ma addObject: valuesString]; + ruleString = [NSMutableString new]; + [[rule values] versitRenderInString: ruleString + asAttributes: NO]; + [ma addObject: ruleString]; + [ruleString release]; } } @@ -138,7 +144,7 @@ else { startDate = theOccurenceDate; - if ([self isAllDay]) + if ([self isKindOfClass: [iCalEvent class]] && [(iCalEvent *) self isAllDay]) { // The event lasts all-day and has no timezone (floating); we convert the range of the first event // to the occurence's timezone. diff --git a/SoObjects/Contacts/NGVCard+SOGo.m b/SoObjects/Contacts/NGVCard+SOGo.m index 7f0a72dc5..811ade06d 100644 --- a/SoObjects/Contacts/NGVCard+SOGo.m +++ b/SoObjects/Contacts/NGVCard+SOGo.m @@ -38,6 +38,7 @@ NSArray *array; NSMutableArray *marray; NSMutableDictionary *entry; + CardElement *element; id tmp; entry = [NSMutableDictionary dictionary]; @@ -49,13 +50,14 @@ @"organizationalPerson", @"inetOrgPerson", @"mozillaAbPersonObsolete", nil] forKey: @"objectclass"]; - - tmp = ([[self n] count] > 1 ? [[self n] objectAtIndex: 1] : nil); - if (tmp) + + element = [self n]; + tmp = [element flattenedValueAtIndex: 1 forKey: @""]; + if ([tmp length] > 0) [entry setObject: tmp forKey: @"givenName"]; - tmp = ([[self n] count] ? [[self n] objectAtIndex: 0] : nil); - if (tmp) + tmp = [element flattenedValueAtIndex: 0 forKey: @""]; + if ([tmp length] > 0) [entry setObject: tmp forKey: @"sn"]; tmp = [self fn]; @@ -74,11 +76,12 @@ marray = [NSMutableArray arrayWithArray: [self childrenWithTag: @"email"]]; [marray removeObjectsInArray: [self childrenWithTag: @"email" - andAttribute: @"type" - havingValue: @"pref"]]; + andAttribute: @"type" + havingValue: @"pref"]]; if ([marray count]) { - buffer = [[marray objectAtIndex: [marray count]-1] value: 0]; + buffer = [[marray objectAtIndex: [marray count]-1] + flattenedValuesForKey: @""]; if ([buffer caseInsensitiveCompare: [self preferredEMail]] != NSOrderedSame) [entry setObject: buffer forKey: @"mozillaSecondEmail"]; @@ -86,58 +89,75 @@ array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"home"]; if ([array count]) - [entry setObject: [[array objectAtIndex: 0] value: 0] forKey: @"homePhone"]; + [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] + forKey: @"homePhone"]; array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"fax"]; if ([array count]) - [entry setObject: [[array objectAtIndex: 0] value: 0] forKey: @"fax"]; + [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] + forKey: @"fax"]; array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"cell"]; if ([array count]) - [entry setObject: [[array objectAtIndex: 0] value: 0] forKey: @"mobile"]; + [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] + forKey: @"mobile"]; array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"pager"]; if ([array count]) - [entry setObject: [[array objectAtIndex: 0] value: 0] forKey: @"pager"]; + [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] + forKey: @"pager"]; array = [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"home"]; if ([array count]) { tmp = [array objectAtIndex: 0]; - [entry setObject: [tmp value: 1] forKey: @"mozillaHomeStreet2"]; - [entry setObject: [tmp value: 2] forKey: @"homeStreet"]; - [entry setObject: [tmp value: 3] forKey: @"mozillaHomeLocalityName"]; - [entry setObject: [tmp value: 4] forKey: @"mozillaHomeState"]; - [entry setObject: [tmp value: 5] forKey: @"mozillaHomePostalCode"]; - [entry setObject: [tmp value: 6] forKey: @"mozillaHomeCountryName"]; + [entry setObject: [tmp flattenedValueAtIndex: 1 forKey: @""] + forKey: @"mozillaHomeStreet2"]; + [entry setObject: [tmp flattenedValueAtIndex: 2 forKey: @""] + forKey: @"homeStreet"]; + [entry setObject: [tmp flattenedValueAtIndex: 3 forKey: @""] + forKey: @"mozillaHomeLocalityName"]; + [entry setObject: [tmp flattenedValueAtIndex: 4 forKey: @""] + forKey: @"mozillaHomeState"]; + [entry setObject: [tmp flattenedValueAtIndex: 5 forKey: @""] + forKey: @"mozillaHomePostalCode"]; + [entry setObject: [tmp flattenedValueAtIndex: 6 forKey: @""] + forKey: @"mozillaHomeCountryName"]; } - array = [self org]; - if (array && [array count]) - [entry setObject: [array objectAtIndex: 0] forKey: @"o"]; + element = [self org]; + tmp = [element flattenedValueAtIndex: 0 forKey: @""]; + if ([tmp length] > 0) + [entry setObject: tmp forKey: @"o"]; array = [self childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"work"]; if ([array count]) { tmp = [array objectAtIndex: 0]; - [entry setObject: [tmp value: 1] forKey: @"mozillaWorkStreet2"]; - [entry setObject: [tmp value: 2] forKey: @"street"]; - [entry setObject: [tmp value: 3] forKey: @"l"]; - [entry setObject: [tmp value: 4] forKey: @"st"]; - [entry setObject: [tmp value: 5] forKey: @"postalCode"]; - [entry setObject: [tmp value: 6] forKey: @"c"]; + [entry setObject: [tmp flattenedValueAtIndex: 1 forKey: @""] + forKey: @"mozillaWorkStreet2"]; + [entry setObject: [tmp flattenedValueAtIndex: 2 forKey: @""] + forKey: @"street"]; + [entry setObject: [tmp flattenedValueAtIndex: 3 forKey: @""] + forKey: @"l"]; + [entry setObject: [tmp flattenedValueAtIndex: 4 forKey: @""] + forKey: @"st"]; + [entry setObject: [tmp flattenedValueAtIndex: 5 forKey: @""] + forKey: @"postalCode"]; + [entry setObject: [tmp flattenedValueAtIndex: 6 forKey: @""] + forKey: @"c"]; } array = [self childrenWithTag: @"tel" andAttribute: @"type" havingValue: @"work"]; if ([array count]) - [entry setObject: [[array objectAtIndex: 0] value: 0] + [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] forKey: @"telephoneNumber"]; array = [self childrenWithTag: @"url" andAttribute: @"type" havingValue: @"work"]; if ([array count]) - [entry setObject: [[array objectAtIndex: 0] value: 0] + [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] forKey: @"workurl"]; array = [self childrenWithTag: @"url" andAttribute: @"type" havingValue: @"home"]; if ([array count]) - [entry setObject: [[array objectAtIndex: 0] value: 0] + [entry setObject: [[array objectAtIndex: 0] flattenedValuesForKey: @""] forKey: @"homeurl"]; tmp = [self note]; diff --git a/SoObjects/Contacts/SOGoContactEntryPhoto.m b/SoObjects/Contacts/SOGoContactEntryPhoto.m index 850ce59f5..d90750460 100644 --- a/SoObjects/Contacts/SOGoContactEntryPhoto.m +++ b/SoObjects/Contacts/SOGoContactEntryPhoto.m @@ -77,7 +77,8 @@ if ([photo isInline]) data = [photo decodedContent]; else - data = [[photo value: 0] dataUsingEncoding: NSISOLatin1StringEncoding]; + data = [[photo flattenedValuesForKey: @""] + dataUsingEncoding: NSISOLatin1StringEncoding]; if (data) { response = [localContext response]; diff --git a/SoObjects/Contacts/SOGoContactLDIFEntry.m b/SoObjects/Contacts/SOGoContactLDIFEntry.m index 994a5181b..17d562709 100644 --- a/SoObjects/Contacts/SOGoContactLDIFEntry.m +++ b/SoObjects/Contacts/SOGoContactLDIFEntry.m @@ -178,36 +178,34 @@ if (!country) country = [ldifEntry objectForKey: @"countryname"]; - element = [CardElement elementWithTag: @"adr" - attributes: nil values: nil]; + element = [CardElement elementWithTag: @"adr"]; [element setValue: 0 ofAttribute: @"type" to: @"work"]; if (streetAddress) - [element setValue: 2 to: streetAddress]; + [element setSingleValue: streetAddress atIndex: 2 forKey: @""]; if (location) - [element setValue: 3 to: location]; + [element setSingleValue: location atIndex: 3 forKey: @""]; if (region) - [element setValue: 4 to: region]; + [element setSingleValue: region atIndex: 4 forKey: @""]; if (postalCode) - [element setValue: 5 to: postalCode]; + [element setSingleValue: postalCode atIndex: 5 forKey: @""]; if (country) - [element setValue: 6 to: country]; + [element setSingleValue: country atIndex: 6 forKey: @""]; if (streetAddress || location || region || postalCode || country) [vcard addChild: element]; // We handle the org/orgunit stuff - element = [CardElement elementWithTag: @"org" - attributes: nil values: nil]; + element = [CardElement elementWithTag: @"org"]; org = [ldifEntry objectForKey: @"o"]; orgunit = [ldifEntry objectForKey: @"ou"]; if (!orgunit) orgunit = [ldifEntry objectForKey: @"orgunit"]; if (org) - [element setValue: 0 to: org]; + [element setSingleValue: org atIndex: 0 forKey: @""]; if (orgunit) - [element setValue: 1 to: orgunit]; + [element setSingleValue: orgunit atIndex: 1 forKey: @""]; if (org || orgunit) [vcard addChild: element]; diff --git a/Tests/Unit/GNUmakefile b/Tests/Unit/GNUmakefile index 760d13402..5b22f55b0 100644 --- a/Tests/Unit/GNUmakefile +++ b/Tests/Unit/GNUmakefile @@ -15,6 +15,7 @@ $(TEST_TOOL)_OBJC_FILES += \ SOGoTestRunner.m \ SaxXMLReaderFactory+SOGoTests.m \ \ + TestVersit.m \ TestiCalTimeZonePeriod.m \ TestiCalRecurrenceCalculator.m \ \ @@ -26,8 +27,8 @@ $(TEST_TOOL)_OBJC_FILES += \ TEST_TOOL_NAME = $(TEST_TOOL) -ADDITIONAL_INCLUDE_DIRS += \ - -D_GNU_SOURCE -I../SOPE/ -I../SoObjects/ -I../UI/ +TEST_TOOL_CPPFLAGS += \ + -Wall -D_GNU_SOURCE -I../SOPE/ -I../SoObjects/ -I../UI/ ADDITIONAL_LIB_DIRS += \ -L../SoObjects/SOGo -lSOGo -lNGMime -lNGCards -lGDLContentStore -lNGExtensions -lSBJson -lobjc -L/usr/local/lib -lSaxObjC diff --git a/Tests/Unit/SaxXMLReaderFactory+SOGoTests.m b/Tests/Unit/SaxXMLReaderFactory+SOGoTests.m index a2fa8f08e..885b6d912 100644 --- a/Tests/Unit/SaxXMLReaderFactory+SOGoTests.m +++ b/Tests/Unit/SaxXMLReaderFactory+SOGoTests.m @@ -38,14 +38,13 @@ - (NSArray *) saxReaderSearchPathes { NSArray *pathes, *args; - NSString *cwd, *exedir; + NSString *exedir; args = [[NSProcessInfo processInfo] arguments]; - cwd = [[NSFileManager defaultManager] currentDirectoryPath]; exedir = [[args objectAtIndex: 0] stringByDeletingLastPathComponent]; pathes = [NSArray arrayWithObject: - [NSString stringWithFormat: @"%@/%@/%@", - cwd, exedir, + [NSString stringWithFormat: @"%@/%@", + exedir, @"../../../SOPE/NGCards/versitCardsSaxDriver/"]]; return pathes; diff --git a/Tests/Unit/TestVersit.m b/Tests/Unit/TestVersit.m new file mode 100644 index 000000000..41cb7e67a --- /dev/null +++ b/Tests/Unit/TestVersit.m @@ -0,0 +1,201 @@ +/* TestVersit.m - this file is part of $PROJECT_NAME_HERE$ + * + * Copyright (C) 2011 Inverse inc + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import + +#import "SOGoTest.h" + +@interface TestVersit : SOGoTest +@end + +@implementation TestVersit + +- (void) test_rendering +{ + CardElement *element; + CardVersitRenderer *renderer; + NSString *result; + + renderer = [CardVersitRenderer new]; + [renderer autorelease]; + + /* 1. simple value */ + element = [CardElement elementWithTag: @"elem"]; + [element setSingleValue: @"value" forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:value\r\n"); + + /* 2. two values */ + element = [CardElement elementWithTag: @"elem"]; + [element setSingleValue: @"value2" atIndex: 1 + forKey: @""]; + [element setSingleValue: @"value1" atIndex: 0 + forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:value1;value2\r\n"); + + /* 3. one value with commma */ + element = [CardElement elementWithTag: @"elem"]; + [element setSingleValue: @"value1, with a comma" forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:value1\\, with a comma\r\n"); + + /* 4. one value with a semicolon */ + element = [CardElement elementWithTag: @"elem"]; + [element setSingleValue: @"value1; with a semi-colon" forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:value1\\; with a semi-colon\r\n"); + + /* 5. 3 named values: + 1. with multiple subvalues + 2. with commas + 3. with semicolon */ + element = [CardElement elementWithTag: @"elem"]; + [element setValues: [NSArray arrayWithObjects: @"1", @"2", @"3", nil] + atIndex: 0 forKey: @"named1"]; + [element setSingleValue: @"1,2,3" forKey: @"named2"]; + [element setSingleValue: @"text1;text2" forKey: @"named3"]; + result = [renderer render: element]; + testEquals(result, @"ELEM:NAMED1=1,2,3;NAMED2=1\\,2\\,3;NAMED3=text1\\;text2\r\n"); + + /* 6. values with 1 ordered value with a whitespace starting subvalues */ + element = [CardElement elementWithTag: @"elem"]; + [element setValues: [NSArray arrayWithObjects: @"", @"1", nil] + atIndex: 0 forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:1\r\n"); + + /* 7. values with 1 ordered value with a subvalue, a whitespace and another subvalue */ + element = [CardElement elementWithTag: @"elem"]; + [element setValues: [NSArray arrayWithObjects: @"1", @"", @"2", nil] + atIndex: 0 forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:1,2\r\n"); + + /* 8.a. values with 1 empty ordered value and another non-empty one */ + element = [CardElement elementWithTag: @"elem"]; + [element setValues: [NSArray arrayWithObjects: nil] + atIndex: 0 forKey: @""]; + [element setValues: [NSArray arrayWithObjects: @"1", nil] + atIndex: 1 forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:;1\r\n"); + + /* 8.b. a variant thereof: array with spaces */ + [element setValues: [NSArray arrayWithObjects: @"", @"", nil] + atIndex: 0 forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:;1\r\n"); + + /* 8.c. a variant thereof: nil array */ + [element setValues: nil atIndex: 0 forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:;1\r\n"); + + /* 9. values with 1 non-empty ordered value and another empty one */ + element = [CardElement elementWithTag: @"elem"]; + [element setValues: [NSArray arrayWithObjects: @"1", nil] + atIndex: 0 forKey: @""]; + [element setValues: [NSArray arrayWithObjects: nil] + atIndex: 1 forKey: @""]; + result = [renderer render: element]; + testEquals(result, @"ELEM:1\r\n"); + + /* 10. named values with 1 nil value, 1 empty value and another non-nil one */ + element = [CardElement elementWithTag: @"elem"]; + [element setSingleValue: nil forKey: @"empty"]; + [element setSingleValue: nil forKey: @"empty2"]; + [element setSingleValue: @"coucou" forKey: @"nonempty"]; + result = [renderer render: element]; + testEquals(result, @"ELEM:NONEMPTY=coucou\r\n"); + + /** tests about parameters handling could be nice */ +} + +- (void) test_parsing +{ + CardGroup *group; + CardElement *element; + NSString *versit; + + versit = @"BEGIN:GROUP1\r\nELEMENT:value\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element flattenedValueAtIndex: 0 forKey: @""], @"value"); + + versit = @"BEGIN:GROUP1\r\nELEMENT:value1;value2\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element flattenedValueAtIndex: 0 forKey: @""], @"value1"); + testEquals([element flattenedValueAtIndex: 1 forKey: @""], @"value2"); + + versit = @"BEGIN:GROUP1\r\nELEMENT:value\\, with comma\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element flattenedValueAtIndex: 0 forKey: @""], @"value, with comma"); + + versit = @"BEGIN:GROUP1\r\nELEMENT:value,with comma\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element valuesAtIndex: 0 forKey: @""], ([NSArray arrayWithObjects: @"value", @"with comma", nil])); + + versit = @"BEGIN:GROUP1\r\nELEMENT:NAMED1=subvalue;NAMED2=subvalue1,subvalue2\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + /* we avoid this test here as nothing guarantees that the order of named + values will be preserved... */ + // testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element flattenedValueAtIndex: 0 forKey: @"NAMED1"], @"subvalue"); + testEquals([element valuesAtIndex: 0 forKey: @"named2"], + ([NSArray arrayWithObjects: @"subvalue1", @"subvalue2", nil])); + + versit = @"BEGIN:GROUP1\r\nELEMENT;PARAM1=test:value\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element flattenedValueAtIndex: 0 forKey: @""], @"value"); + testEquals([element value: 0 ofAttribute: @"param1"], @"test"); + + versit = @"BEGIN:GROUP1\r\nELEMENT;PARAM1=paramvalue1,paramvalue2:value\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element flattenedValueAtIndex: 0 forKey: @""], @"value"); + testEquals([element value: 0 ofAttribute: @"param1"], @"paramvalue1"); + testEquals([element value: 1 ofAttribute: @"param1"], @"paramvalue2"); + + versit = @"BEGIN:GROUP1\r\nELEMENT;PARAM1=paramvalue1\\, with comma:value\r\nEND:GROUP1"; + group = [CardGroup parseSingleFromSource: versit]; + testEquals([group versitString], versit); + element = [group firstChildWithTag: @"element"]; + testEquals([element flattenedValueAtIndex: 0 forKey: @""], @"value"); + testEquals([element value: 0 ofAttribute: @"param1"], @"paramvalue1, with comma"); +} + +@end diff --git a/Tools/SOGoToolRemoveDoubles.m b/Tools/SOGoToolRemoveDoubles.m index 4f90de62c..a4be92c3f 100644 --- a/Tools/SOGoToolRemoveDoubles.m +++ b/Tools/SOGoToolRemoveDoubles.m @@ -64,7 +64,7 @@ cardReferences = [[self cardReferences] objectEnumerator]; while ((currentReference = [cardReferences nextObject])) - [cardNames addObject: [currentReference value: 0]]; + [cardNames addObject: [currentReference flattenedValuesForKey: @""]]; return cardNames; } diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index c97d58390..dfbc48644 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -366,7 +366,7 @@ for (i = 0; i < [elements count]; i++) { ce = [elements objectAtIndex: i]; - value = [ce value: 0]; + value = [ce flattenedValuesForKey: @""]; if (!aTypeToExclude) break; @@ -398,13 +398,13 @@ if (max > 0) { - potential = [[elements objectAtIndex: 0] value: 0]; + potential = [[elements objectAtIndex: 0] flattenedValuesForKey: @""]; if (!workMail) { if (homeMail && homeMail == potential) { if (max > 1) - workMail = [[elements objectAtIndex: 1] value: 0]; + workMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""]; } else workMail = potential; @@ -412,7 +412,7 @@ if (!homeMail && max > 1) { if (workMail && workMail == potential) - homeMail = [[elements objectAtIndex: 1] value: 0]; + homeMail = [[elements objectAtIndex: 1] flattenedValuesForKey: @""]; else homeMail = potential; } @@ -430,32 +430,32 @@ [self _setSnapshotValue: @"homeMail" to: homeMail]; [self _setSnapshotValue: @"mozillaUseHtmlMail" - to: [[card uniqueChildWithTag: @"x-mozilla-html"] value: 0]]; + to: [[card uniqueChildWithTag: @"x-mozilla-html"] flattenedValuesForKey: @""]]; } - (void) _setupOrgFields { NSMutableArray *orgServices; - NSArray *org; - NSRange aRange; - unsigned int max; + CardElement *org; + NSString *service; + NSUInteger count, max; org = [card org]; - max = [org count]; - if (max > 0) + [self _setSnapshotValue: @"workCompany" + to: [org flattenedValueAtIndex: 0 forKey: @""]]; + max = [[org valuesForKey: @""] count]; + if (max > 1) { - [self _setSnapshotValue: @"workCompany" to: [org objectAtIndex: 0]]; - if (max > 1) + orgServices = [NSMutableArray arrayWithCapacity: max]; + for (count = 1; count < max; count++) { - aRange = NSMakeRange (1, max - 1); - orgServices = [NSMutableArray arrayWithArray: [org subarrayWithRange: aRange]]; - - while ([orgServices containsObject: @""]) - [orgServices removeObject: @""]; - - [self _setSnapshotValue: @"workService" - to: [orgServices componentsJoinedByString: @", "]]; + service = [org flattenedValueAtIndex: count forKey: @""]; + if ([service length] > 0) + [orgServices addObject: service]; } + + [self _setSnapshotValue: @"workService" + to: [orgServices componentsJoinedByString: @", "]]; } } @@ -481,21 +481,14 @@ - (void) initSnapshot { - NSArray *n, *elements; + NSArray *elements; CardElement *element; - unsigned int max; - n = [card n]; - if (n) - { - max = [n count]; - if (max > 0) - { - [self _setSnapshotValue: @"sn" to: [n objectAtIndex: 0]]; - if (max > 1) - [self _setSnapshotValue: @"givenName" to: [n objectAtIndex: 1]]; - } - } + element = [card n]; + [self _setSnapshotValue: @"sn" + to: [element flattenedValueAtIndex: 0 forKey: @""]]; + [self _setSnapshotValue: @"givenName" + to: [element flattenedValueAtIndex: 1 forKey: @""]]; [self _setSnapshotValue: @"fn" to: [card fn]]; [self _setSnapshotValue: @"nickname" to: [card nickname]]; @@ -538,7 +531,7 @@ [self _setupEmailFields]; [self _setSnapshotValue: @"screenName" - to: [[card uniqueChildWithTag: @"x-aim"] value: 0]]; + to: [[card uniqueChildWithTag: @"x-aim"] flattenedValuesForKey: @""]]; elements = [card childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"work"]; @@ -546,17 +539,17 @@ { element = [elements objectAtIndex: 0]; [self _setSnapshotValue: @"workExtendedAddress" - to: [element value: 1]]; + to: [element flattenedValueAtIndex: 1 forKey: @""]]; [self _setSnapshotValue: @"workStreetAddress" - to: [element value: 2]]; + to: [element flattenedValueAtIndex: 2 forKey: @""]]; [self _setSnapshotValue: @"workCity" - to: [element value: 3]]; + to: [element flattenedValueAtIndex: 3 forKey: @""]]; [self _setSnapshotValue: @"workState" - to: [element value: 4]]; + to: [element flattenedValueAtIndex: 4 forKey: @""]]; [self _setSnapshotValue: @"workPostalCode" - to: [element value: 5]]; + to: [element flattenedValueAtIndex: 5 forKey: @""]]; [self _setSnapshotValue: @"workCountry" - to: [element value: 6]]; + to: [element flattenedValueAtIndex: 6 forKey: @""]]; } elements = [card childrenWithTag: @"adr" @@ -565,17 +558,17 @@ { element = [elements objectAtIndex: 0]; [self _setSnapshotValue: @"homeExtendedAddress" - to: [element value: 1]]; + to: [element flattenedValueAtIndex: 1 forKey: @""]]; [self _setSnapshotValue: @"homeStreetAddress" - to: [element value: 2]]; + to: [element flattenedValueAtIndex: 2 forKey: @""]]; [self _setSnapshotValue: @"homeCity" - to: [element value: 3]]; + to: [element flattenedValueAtIndex: 3 forKey: @""]]; [self _setSnapshotValue: @"homeState" - to: [element value: 4]]; + to: [element flattenedValueAtIndex: 4 forKey: @""]]; [self _setSnapshotValue: @"homePostalCode" - to: [element value: 5]]; + to: [element flattenedValueAtIndex: 5 forKey: @""]]; [self _setSnapshotValue: @"homeCountry" - to: [element value: 6]]; + to: [element flattenedValueAtIndex: 6 forKey: @""]]; } elements = [card childrenWithTag: @"url"]; @@ -591,7 +584,7 @@ [elements count] > 0) { [self _setSnapshotValue: @"homeURL" - to: [[elements objectAtIndex: 0] value: 0]]; + to: [[elements objectAtIndex: 0] flattenedValuesForKey: @""]]; } // If we do have a "work" URL but no "home" URL but two // values URLs present, let's add the second one as the home URL @@ -603,11 +596,11 @@ for (i = 0; i < [elements count]; i++) { - if ([[[elements objectAtIndex: i] value: 0] + if ([[[elements objectAtIndex: i] flattenedValuesForKey: @""] caseInsensitiveCompare: [snapshot objectForKey: @"workURL"]] != NSOrderedSame) { [self _setSnapshotValue: @"homeURL" - to: [[elements objectAtIndex: i] value: 0]]; + to: [[elements objectAtIndex: i] flattenedValuesForKey: @""]]; break; } } @@ -615,7 +608,7 @@ [self _setSnapshotValue: @"calFBURL" - to: [[card uniqueChildWithTag: @"FBURL"] value: 0]]; + to: [[card uniqueChildWithTag: @"FBURL"] flattenedValuesForKey: @""]]; [self _setSnapshotValue: @"title" to: [card title]]; [self _setupOrgFields]; @@ -686,7 +679,7 @@ photoURL = [NSString stringWithFormat: @"%@/photo%d", baseInlineURL, count]; else - photoURL = [photo value: 0]; + photoURL = [photo flattenedValuesForKey: @""]; [photosURL addObject: photoURL]; } } @@ -721,17 +714,16 @@ CardElement *phone; phone = [self _elementWithTag: @"tel" ofType: @"work"]; - [phone setValue: 0 to: [snapshot objectForKey: @"telephoneNumber"]]; + [phone setSingleValue: [snapshot objectForKey: @"telephoneNumber"] forKey: @""]; phone = [self _elementWithTag: @"tel" ofType: @"home"]; - [phone setValue: 0 to: [snapshot objectForKey: @"homeTelephoneNumber"]]; + [phone setSingleValue: [snapshot objectForKey: @"homeTelephoneNumber"] forKey: @""]; phone = [self _elementWithTag: @"tel" ofType: @"cell"]; - [phone setValue: 0 to: [snapshot objectForKey: @"mobile"]]; + [phone setSingleValue: [snapshot objectForKey: @"mobile"] forKey: @""]; phone = [self _elementWithTag: @"tel" ofType: @"fax"]; - [phone setValue: 0 - to: [snapshot objectForKey: @"facsimileTelephoneNumber"]]; + [phone setSingleValue: [snapshot objectForKey: @"facsimileTelephoneNumber"] + forKey: @""]; phone = [self _elementWithTag: @"tel" ofType: @"pager"]; - [phone setValue: 0 - to: [snapshot objectForKey: @"pager"]]; + [phone setSingleValue: [snapshot objectForKey: @"pager"] forKey: @""]; } - (void) _saveEmails @@ -739,9 +731,9 @@ CardElement *workMail, *homeMail; workMail = [self _elementWithTag: @"email" ofType: @"work"]; - [workMail setValue: 0 to: [snapshot objectForKey: @"workMail"]]; + [workMail setSingleValue: [snapshot objectForKey: @"workMail"] forKey: @""]; homeMail = [self _elementWithTag: @"email" ofType: @"home"]; - [homeMail setValue: 0 to: [snapshot objectForKey: @"homeMail"]]; + [homeMail setSingleValue: [snapshot objectForKey: @"homeMail"] forKey: @""]; if (preferredEmail) { if ([preferredEmail isEqualToString: @"work"]) @@ -751,8 +743,8 @@ } [[card uniqueChildWithTag: @"x-mozilla-html"] - setValue: 0 - to: [snapshot objectForKey: @"mozillaUseHtmlMail"]]; + setSingleValue: [snapshot objectForKey: @"mozillaUseHtmlMail"] + forKey: @""]; } - (void) _saveSnapshot @@ -773,20 +765,32 @@ [card setTz: [snapshot objectForKey: @"tz"]]; element = [self _elementWithTag: @"adr" ofType: @"home"]; - [element setValue: 1 to: [snapshot objectForKey: @"homeExtendedAddress"]]; - [element setValue: 2 to: [snapshot objectForKey: @"homeStreetAddress"]]; - [element setValue: 3 to: [snapshot objectForKey: @"homeCity"]]; - [element setValue: 4 to: [snapshot objectForKey: @"homeState"]]; - [element setValue: 5 to: [snapshot objectForKey: @"homePostalCode"]]; - [element setValue: 6 to: [snapshot objectForKey: @"homeCountry"]]; + [element setSingleValue: [snapshot objectForKey: @"homeExtendedAddress"] + atIndex: 1 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"homeStreetAddress"] + atIndex: 2 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"homeCity"] + atIndex: 3 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"homeState"] + atIndex: 4 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"homePostalCode"] + atIndex: 5 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"homeCountry"] + atIndex: 6 forKey: @""]; element = [self _elementWithTag: @"adr" ofType: @"work"]; - [element setValue: 1 to: [snapshot objectForKey: @"workExtendedAddress"]]; - [element setValue: 2 to: [snapshot objectForKey: @"workStreetAddress"]]; - [element setValue: 3 to: [snapshot objectForKey: @"workCity"]]; - [element setValue: 4 to: [snapshot objectForKey: @"workState"]]; - [element setValue: 5 to: [snapshot objectForKey: @"workPostalCode"]]; - [element setValue: 6 to: [snapshot objectForKey: @"workCountry"]]; + [element setSingleValue: [snapshot objectForKey: @"workExtendedAddress"] + atIndex: 1 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"workStreetAddress"] + atIndex: 2 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"workCity"] + atIndex: 3 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"workState"] + atIndex: 4 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"workPostalCode"] + atIndex: 5 forKey: @""]; + [element setSingleValue: [snapshot objectForKey: @"workCountry"] + atIndex: 6 forKey: @""]; element = [CardElement simpleElementWithTag: @"fburl" value: [snapshot objectForKey: @"calFBURL"]]; @@ -799,13 +803,13 @@ [self _savePhoneValues]; [self _saveEmails]; [[self _elementWithTag: @"url" ofType: @"home"] - setValue: 0 to: [snapshot objectForKey: @"homeURL"]]; + setSingleValue: [snapshot objectForKey: @"homeURL"] forKey: @""]; [[self _elementWithTag: @"url" ofType: @"work"] - setValue: 0 to: [snapshot objectForKey: @"workURL"]]; + setSingleValue: [snapshot objectForKey: @"workURL"] forKey: @""]; [[card uniqueChildWithTag: @"x-aim"] - setValue: 0 - to: [snapshot objectForKey: @"screenName"]]; + setSingleValue: [snapshot objectForKey: @"screenName"] + forKey: @""]; } - (id ) saveAction @@ -814,7 +818,6 @@ id result; NSString *jsRefreshMethod; SoSecurityManager *sm; - NSException *ex; contact = [self clientObject]; card = [contact vCard]; @@ -836,7 +839,7 @@ if (![sm validatePermission: SoPerm_AddDocumentsImagesAndFiles onObject: componentAddressBook inContext: context]) - ex = [contact moveToFolder: (SOGoGCSFolder *)componentAddressBook]; // TODO: handle exception + [contact moveToFolder: (SOGoGCSFolder *)componentAddressBook]; // TODO: handle exception } } diff --git a/UI/Contacts/UIxContactView.m b/UI/Contacts/UIxContactView.m index 7728b54e7..3c4583c61 100644 --- a/UI/Contacts/UIxContactView.m +++ b/UI/Contacts/UIxContactView.m @@ -58,14 +58,6 @@ /* accessors */ -- (NSString *) _cardStringWithLabel: (NSString *) label - value: (NSString *) value -{ - return [self _cardStringWithLabel: label - value: value - url: nil]; -} - - (NSString *) _cardStringWithLabel: (NSString *) label value: (NSString *) value url: (NSString *) url @@ -89,6 +81,14 @@ return cardString; } +- (NSString *) _cardStringWithLabel: (NSString *) label + value: (NSString *) value +{ + return [self _cardStringWithLabel: label + value: value + url: nil]; +} + - (NSString *) displayName { return [self _cardStringWithLabel: @"Display Name:" @@ -103,25 +103,24 @@ - (NSString *) fullName { - NSArray *n; - NSString *fn; - unsigned int max; + CardElement *n; + NSString *fn, *firstName, *lastName; fn = [card fn]; if ([fn length] == 0) { n = [card n]; - if (n) - { - max = [n count]; - if (max > 0) - { - if (max > 1) - fn = [NSString stringWithFormat: @"%@ %@", [n objectAtIndex: 1], [n objectAtIndex: 0]]; - else - fn = [n objectAtIndex: 0]; - } - } + lastName = [n flattenedValueAtIndex: 0 forKey: @""]; + firstName = [n flattenedValueAtIndex: 1 forKey: @""]; + if ([firstName length] > 0) + { + if ([lastName length] > 0) + fn = [NSString stringWithFormat: @"%@ %@", firstName, lastName]; + else + fn = firstName; + } + else + fn = lastName; } return fn; @@ -172,7 +171,7 @@ for (i = 0; i < [emails count]; i++) { - email = [[emails objectAtIndex: i] value: 0]; + email = [[emails objectAtIndex: i] flattenedValuesForKey: @""]; if ([email caseInsensitiveCompare: [card preferredEMail]] != NSOrderedSame) { @@ -192,7 +191,7 @@ { NSString *screenName, *goim; - screenName = [[card uniqueChildWithTag: @"x-aim"] value: 0]; + screenName = [[card uniqueChildWithTag: @"x-aim"] flattenedValuesForKey: @""]; if ([screenName length] > 0) goim = [NSString stringWithFormat: @"%@", screenName, screenName]; @@ -250,7 +249,7 @@ for (i = 0; i < [elements count]; i++) { ce = [elements objectAtIndex: i]; - phone = [ce value: 0]; + phone = [ce flattenedValuesForKey: @""]; if (!aTypeToExclude) break; @@ -315,17 +314,23 @@ - (NSString *) homePobox { - return [self _cardStringWithLabel: nil value: [homeAdr value: 0]]; + return [self _cardStringWithLabel: nil + value: [homeAdr flattenedValueAtIndex: 0 + forKey: @""]]; } - (NSString *) homeExtendedAddress { - return [self _cardStringWithLabel: nil value: [homeAdr value: 1]]; + return [self _cardStringWithLabel: nil + value: [homeAdr flattenedValueAtIndex: 1 + forKey: @""]]; } - (NSString *) homeStreetAddress { - return [self _cardStringWithLabel: nil value: [homeAdr value: 2]]; + return [self _cardStringWithLabel: nil + value: [homeAdr flattenedValueAtIndex: 2 + forKey: @""]]; } - (NSString *) homeCityAndProv @@ -333,8 +338,8 @@ NSString *city, *prov; NSMutableString *data; - city = [homeAdr value: 3]; - prov = [homeAdr value: 4]; + city = [homeAdr flattenedValueAtIndex: 3 forKey: @""]; + prov = [homeAdr flattenedValueAtIndex: 4 forKey: @""]; data = [NSMutableString string]; [data appendString: city]; @@ -350,8 +355,8 @@ NSString *postalCode, *country; NSMutableString *data; - postalCode = [homeAdr value: 5]; - country = [homeAdr value: 6]; + postalCode = [homeAdr flattenedValueAtIndex: 5 forKey: @""]; + country = [homeAdr flattenedValueAtIndex: 6 forKey: @""]; data = [NSMutableString string]; [data appendString: postalCode]; @@ -391,7 +396,7 @@ andAttribute: @"type" havingValue: aType]; if ([elements count] > 0) - url = [[elements objectAtIndex: 0] value: 0]; + url = [[elements objectAtIndex: 0] flattenedValuesForKey: @""]; else url = nil; @@ -416,7 +421,7 @@ workURL = nil; if ([elements count] > 0) - workURL = [[elements objectAtIndex: 0] value: 0]; + workURL = [[elements objectAtIndex: 0] flattenedValuesForKey: @""]; elements = [card childrenWithTag: @"url"]; @@ -424,9 +429,10 @@ { for (i = 0; i < [elements count]; i++) { - if ([[[elements objectAtIndex: i] value: 0] caseInsensitiveCompare: workURL] != NSOrderedSame) + if ([[[elements objectAtIndex: i] flattenedValuesForKey: @""] + caseInsensitiveCompare: workURL] != NSOrderedSame) { - s = [[elements objectAtIndex: i] value: 0]; + s = [[elements objectAtIndex: i] flattenedValuesForKey: @""]; break; } } @@ -434,7 +440,7 @@ } else if (!workURL && [elements count] > 0) { - s = [[elements objectAtIndex: 0] value: 0]; + s = [[elements objectAtIndex: 0] flattenedValuesForKey: @""]; } if (s && [s length] > 0) @@ -474,18 +480,23 @@ - (NSString *) workService { NSMutableArray *orgServices; - NSArray *org; - NSRange aRange; - NSString *services; + NSArray *values; + CardElement *org; + NSString *service, *services; + NSUInteger count, max; org = [card org]; - if (org && [org count] > 1) + values = [org valuesForKey: @""]; + max = [values count]; + if (max > 1) { - aRange = NSMakeRange(1, [org count]-1); - orgServices = [NSMutableArray arrayWithArray: [org subarrayWithRange: aRange]]; - - while ([orgServices containsObject: @""]) - [orgServices removeObject: @""]; + orgServices = [NSMutableArray arrayWithCapacity: max]; + for (count = 1; count < max; count++) + { + service = [org flattenedValueAtIndex: count forKey: @""]; + if ([service length] > 0) + [orgServices addObject: service]; + } services = [orgServices componentsJoinedByString: @", "]; } @@ -497,13 +508,12 @@ - (NSString *) workCompany { - NSArray *org; + CardElement *org; NSString *company; org = [card org]; - if (org && [org count] > 0) - company = [org objectAtIndex: 0]; - else + company = [org flattenedValueAtIndex: 0 forKey: @""]; + if ([company length] == 0) company = nil; return [self _cardStringWithLabel: nil value: company]; @@ -511,17 +521,23 @@ - (NSString *) workPobox { - return [self _cardStringWithLabel: nil value: [workAdr value: 0]]; + return [self _cardStringWithLabel: nil + value: [workAdr flattenedValueAtIndex: 0 + forKey: @""]]; } - (NSString *) workExtendedAddress { - return [self _cardStringWithLabel: nil value: [workAdr value: 1]]; + return [self _cardStringWithLabel: nil + value: [workAdr flattenedValueAtIndex: 1 + forKey: @""]]; } - (NSString *) workStreetAddress { - return [self _cardStringWithLabel: nil value: [workAdr value: 2]]; + return [self _cardStringWithLabel: nil + value: [workAdr flattenedValueAtIndex: 2 + forKey: @""]]; } - (NSString *) workCityAndProv @@ -529,8 +545,8 @@ NSString *city, *prov; NSMutableString *data; - city = [workAdr value: 3]; - prov = [workAdr value: 4]; + city = [workAdr flattenedValueAtIndex: 3 forKey: @""]; + prov = [workAdr flattenedValueAtIndex: 4 forKey: @""]; data = [NSMutableString string]; [data appendString: city]; @@ -546,8 +562,8 @@ NSString *postalCode, *country; NSMutableString *data; - postalCode = [workAdr value: 5]; - country = [workAdr value: 6]; + postalCode = [workAdr flattenedValueAtIndex: 5 forKey: @""]; + country = [workAdr flattenedValueAtIndex: 6 forKey: @""]; data = [NSMutableString string]; [data appendString: postalCode]; @@ -681,7 +697,7 @@ photoURL = [NSString stringWithFormat: @"%@/photo%d", baseInlineURL, count]; else - photoURL = [photo value: 0]; + photoURL = [photo flattenedValuesForKey: @""]; [photosURL addObject: photoURL]; } } diff --git a/UI/Contacts/UIxListEditor.m b/UI/Contacts/UIxListEditor.m index 0ab577aa7..adb04de20 100644 --- a/UI/Contacts/UIxListEditor.m +++ b/UI/Contacts/UIxListEditor.m @@ -184,7 +184,7 @@ [newWorkMail setTag: @"email"]; [newWorkMail addType: @"work"]; [newCard addChild: newWorkMail]; - [newWorkMail setValue: 0 to: workMail]; + [newWorkMail setSingleValue: workMail forKey: @""]; [newCard setFn: fn]; // Add vCard to current folder diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index a03336e58..8183089e9 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -268,10 +268,9 @@ - (NSString *) formattedMailtoString: (NGVCard *) card { - NSString *email; + NSString *firstName, *lastName, *email; NSMutableString *fn, *rc = nil; - NSArray *n; - unsigned int max; + CardElement *n; if ([card isKindOfClass: [NGVCard class]]) email = [card preferredEMail]; @@ -291,17 +290,17 @@ && [card isKindOfClass: [NGVCard class]]) { n = [card n]; - if (n) + lastName = [n flattenedValueAtIndex: 0 forKey: @""]; + firstName = [n flattenedValueAtIndex: 1 forKey: @""]; + if ([firstName length] > 0) { - max = [n count]; - if (max > 0) - { - if (max > 1) - fn = [NSMutableString stringWithFormat: @"%@ %@", [n objectAtIndex: 1], [n objectAtIndex: 0]]; - else - fn = [NSMutableString stringWithString: [n objectAtIndex: 0]]; - } + if ([lastName length] > 0) + fn = [NSMutableString stringWithFormat: @"%@ %@", firstName, lastName]; + else + fn = [NSMutableString stringWithString: firstName]; } + else if ([lastName length] > 0) + fn = [NSMutableString stringWithString: lastName]; } if ([fn length] > 0) { diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 2e1b84c87..367478dda 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -292,13 +292,13 @@ iRANGE(2); - (void) _loadCategories { - NSString *compCategories, *simpleCategory; + NSString *simpleCategory; + NSArray *compCategories; compCategories = [component categories]; - if ([compCategories length] > 0) + if ([compCategories count] > 0) { - simpleCategory = [[compCategories componentsSeparatedByString: @","] - objectAtIndex: 0]; + simpleCategory = [compCategories objectAtIndex: 0]; ASSIGN (category, simpleCategory); } } @@ -405,7 +405,7 @@ iRANGE(2); { repeatType = @"3"; - if ([[rule namedValue: @"bymonth"] length]) + if ([[rule flattenedValuesForKey: @"bymonth"] length]) { if ([[rule byDay] length]) { @@ -420,13 +420,13 @@ iRANGE(2); [self setRepeat2: @"1"]; [self setRepeat5: [NSString stringWithFormat: @"%d", firstOccurrence]]; [self setRepeat6: [NSString stringWithFormat: @"%d", [dayMask firstDay]]]; - [self setRepeat7: [NSString stringWithFormat: @"%d", [[rule namedValue: @"bymonth"] intValue]-1]]; + [self setRepeat7: [NSString stringWithFormat: @"%d", [[rule flattenedValuesForKey: @"bymonth"] intValue]-1]]; } else { [self setRepeat2: @"0"]; - [self setRepeat3: [rule namedValue: @"bymonthday"]]; - [self setRepeat4: [NSString stringWithFormat: @"%d", [[rule namedValue: @"bymonth"] intValue]-1]]; + [self setRepeat3: [rule flattenedValuesForKey: @"bymonthday"]]; + [self setRepeat4: [NSString stringWithFormat: @"%d", [[rule flattenedValuesForKey: @"bymonth"] intValue]-1]]; } } else if ([rule repeatInterval] == 1) @@ -440,7 +440,7 @@ iRANGE(2); { repeat = @"CUSTOM"; [self setRange1: @"1"]; - [self setRange2: [rule namedValue: @"count"]]; + [self setRange2: [rule flattenedValuesForKey: @"count"]]; } else if ([rule untilDate]) { @@ -519,7 +519,7 @@ iRANGE(2); || [reminderAction isEqualToString: @"email"]) && [[aTrigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame) { - duration = [aTrigger value]; + duration = [aTrigger flattenedValuesForKey: @""]; i = [reminderValues indexOfObject: duration]; if (i == NSNotFound || [reminderAction isEqualToString: @"email"]) @@ -611,8 +611,7 @@ iRANGE(2); ASSIGN (privacy, [component accessClass]); ASSIGN (priority, [component priority]); ASSIGN (status, [component status]); - ASSIGN (categories, - [[component categories] vCardSubvaluesWithSeparator: ',']); + ASSIGN (categories, [component categories]); if ([[[component organizer] rfc822Email] length]) { ASSIGN (organizer, [component organizer]); @@ -1853,6 +1852,7 @@ RANGE(2); { int type, range; + NSMutableArray *values; // We decode the range range = [[self range1] intValue]; @@ -1990,14 +1990,21 @@ RANGE(2); occurence = [[self repeat3] intValue] + 1; if (occurence > 5) // the first/second/third/fourth/fifth .. occurence = -1; // the last .. - [theRule setNamedValue: @"byday" - to: [NSString stringWithFormat: @"%d%@", - occurence, day]]; + [theRule setSingleValue: [NSString stringWithFormat: @"%d%@", + occurence, day] + forKey: @"byday"]; } else { if ([[self repeat5] intValue] > 0) - [theRule setNamedValue: @"bymonthday" to: [self repeat5]]; + { + values = [[[self repeat5] + componentsSeparatedByString: @","] + mutableCopy]; + [theRule setValues: values + atIndex: 0 forKey: @"bymonthday"]; + [values release]; + } } } } @@ -2037,22 +2044,27 @@ RANGE(2); occurence = [[self repeat5] intValue] + 1; if (occurence > 5) // the first/second/third/fourth/fifth .. occurence = -1; // the last .. - [theRule setNamedValue: @"byday" - to: [NSString stringWithFormat: @"%d%@", - occurence, day]]; - [theRule setNamedValue: @"bymonth" - to: [NSString stringWithFormat: @"%d", - [[self repeat7] intValue] + 1]]; + [theRule setSingleValue: [NSString stringWithFormat: @"%d%@", + occurence, day] + forKey: @"byday"]; + [theRule setSingleValue: [NSString stringWithFormat: @"%d", + [[self repeat7] intValue] + 1] + forKey: @"bymonth"]; } else { if ([[self repeat3] intValue] > 0 && [[self repeat4] intValue] > 0) { - [theRule setNamedValue: @"bymonthday" - to: [self repeat3]]; - [theRule setNamedValue: @"bymonth" - to: [NSString stringWithFormat: @"%d", ([[self repeat4] intValue]+1)]]; + values = [[[self repeat3] + componentsSeparatedByString: @","] + mutableCopy]; + [theRule setValues: values atIndex: 0 + forKey: @"bymonthday"]; + [values release]; + [theRule setSingleValue: [NSString stringWithFormat: @"%d", + [[self repeat4] intValue] + 1] + forKey: @"bymonth"]; } } } @@ -2111,7 +2123,7 @@ RANGE(2); [component setComment: comment]; [component setAttach: attachUrl]; [component setAccessClass: privacy]; - [component setCategories: category]; + [component setCategories: categories]; [self _handleAttendeesEdition]; [self _handleOrganizer]; clientObject = [self clientObject]; @@ -2146,7 +2158,7 @@ RANGE(2); if ([aValue length]) { // Predefined alarm [anAlarm setAction: @"DISPLAY"]; - [aTrigger setValue: aValue]; + [aTrigger setSingleValue: aValue forKey: @""]; } else { // Custom alarm @@ -2177,7 +2189,7 @@ RANGE(2); aValue = [aValue stringByAppendingFormat: @"%i%@", [reminderQuantity intValue], [reminderUnit substringToIndex: 1]]; - [aTrigger setValue: aValue]; + [aTrigger setSingleValue: aValue forKey: @""]; [aTrigger setRelationType: reminderRelation]; } else