// // P4SpecDescription.mm // P4ObjectLayer // // Created by Michael Bishop on 9/19/10. // Copyright 2010 Numerical Garden, LLC. All rights reserved. // #import "P4SpecDescription.h" #import "P4TypeConversions.hpp" #import "NGAUtilities.h" #include "clientapi.h" #include "spec.h" static NSArray* StructuredFieldsFromFieldStrings(NSArray*fieldStrings); static NSDictionary* ParsedFieldDefinitionsFromRawFieldStrings(NSDictionary*rawFieldStrings); static Spec* CreateSpecFromSpecDefinition(NSDictionary*dict); class SpecElem; class StrPtr; class Error; class P4SpecDataObjC : public SpecData { public: explicit P4SpecDataObjC( NSDictionary * spec ); virtual ~P4SpecDataObjC(); virtual StrPtr *GetLine( SpecElem *sd, int x, const char **cmt ); virtual void SetLine( SpecElem *sd, int x, const StrPtr *val, Error *e ); private: NSMutableDictionary * mSpec; StrBuf mSavedValue; }; @interface P4SpecDescription () -(NSArray*)valuesArrayForFieldTag:(NSString*)fieldTag; -(NSArray*)valuesArrayForFieldCode:(int)fieldCode; @property (readonly, nonatomic) Spec * spec; @end @interface P4SpecFieldDescription () -(id)initWithCode:(int)code specDescription:(P4SpecDescription*)description; @property (readonly, nonatomic) SpecElem * specElem; @end @implementation P4SpecDescription +(P4SpecDescription*)specDescriptionWithEncodedDefinition:(NSString*)specdef { return [[[self alloc] initWithSpecDefString:specdef] autorelease]; } +(P4SpecDescription*)specDescriptionWithSpecDefProperties:(NSDictionary*)specdefData { return [[[self alloc] initWithSpecDefProperties:specdefData] autorelease]; } -(id)initWithSpecDefString:(NSString*)specdef { if ( (self = [super init]) == nil ) return nil; Error e; _specCPP = new Spec( [specdef UTF8String], "", &e ); _parsedValues = [[NSMutableDictionary dictionary] retain]; return self; } -(id)initWithSpecDefProperties:(NSDictionary*)properties { if ( (self = [super init]) == nil ) return nil; Error e; _specCPP = CreateSpecFromSpecDefinition( properties ); _parsedValues = [[NSMutableDictionary dictionary] retain]; return self; } -(Spec*)spec { return (Spec*)_specCPP; } -(void)dealloc { delete ((Spec*)_specCPP); [_parsedValues release]; [super dealloc]; } -(int)identifierCode { return self.spec->Get(0)->code; } -(int)fieldCount { return self.spec->Count(); } -(void)enumerateSpecElemsUsingBlock:(void (^)(SpecElem *, NSUInteger, BOOL *))block { BOOL stop = NO; for (int i = 0; !stop && i < [self fieldCount]; i++) { SpecElem * elem = self.spec->Get(i); block( elem, i, &stop ); } } -(NSSet*)allCodes { __block NSMutableSet * set = [NSMutableSet setWithCapacity:[self fieldCount] ]; [self enumerateSpecElemsUsingBlock:^( SpecElem * elem, NSUInteger idx, BOOL * stop) { [set addObject:[NSNumber numberWithInt:elem->code]]; }]; return set; } -(NSSet*)allTags { __block NSMutableSet * set = [NSMutableSet setWithCapacity:[self fieldCount] ]; [self enumerateSpecElemsUsingBlock:^( SpecElem * elem, NSUInteger idx, BOOL * stop) { [set addObject:[NSString stringWithStrPtr:elem->tag]]; }]; return set; } -(SpecElem*)specElemForCode:(int)code { Error e; return self.spec->Find(code, &e); } -(SpecElem*)specElemForTag:(NSString*)name { Error e; return self.spec->Find([name strRef], &e); } -(P4SpecFieldDescription*)fieldForCode:(int)code { SpecElem * elem = [self specElemForCode:code]; if ( !elem ) return nil; return [[[P4SpecFieldDescription alloc] initWithCode:code specDescription:self] autorelease]; } -(P4SpecFieldDescription*)fieldForTag:(NSString*)name { SpecElem * elem = [self specElemForTag:name]; if ( !elem ) return nil; return [[[P4SpecFieldDescription alloc] initWithCode:elem->code specDescription:self] autorelease]; } -(NSString*)encodedDefinition { StrBuf encoded; self.spec->Encode(&encoded); return [NSString stringWithStrPtr:encoded]; } -(NSString*)formFromSpecProperties:(NSDictionary*)properties { P4SpecDataObjC specData( properties ); StrBuf * formBuf = self.spec->Format( &specData ); NSString * form = [NSString stringWithStrPtr:*formBuf]; delete formBuf; return form; } -(NSArray*)valuesArrayForSpecElem:(SpecElem*)specElem { if ( !specElem ) return nil; NSNumber * codeNumber = [NSNumber numberWithInt:specElem->code]; NSArray * array = [_parsedValues objectForKey:codeNumber]; if ( array ) return array; // Parse the values and set them NSString * encodedValuesString = [NSString stringWithStrPtr:specElem->values]; NSArray * valueTypes = [encodedValuesString componentsSeparatedByString:@","]; NSMutableArray * valuesArray = [NSMutableArray arrayWithCapacity:[valueTypes count]]; for (NSString * valueTypeString in valueTypes) [valuesArray addObject:[valueTypeString componentsSeparatedByString:@"/"]]; [_parsedValues setObject:valuesArray forKey:codeNumber]; return valuesArray; } -(NSArray*)valuesArrayForFieldTag:(NSString*)fieldTag { return [self valuesArrayForSpecElem:[self specElemForTag:fieldTag]]; } -(NSArray*)valuesArrayForFieldCode:(int)code { return [self valuesArrayForSpecElem:[self specElemForCode:code]]; } @end @implementation P4SpecFieldDescription @synthesize code = _code; -(id)initWithCode:(int)code specDescription:(P4SpecDescription*)description { if ( (self = [super init]) == nil ) return nil; _code = code; _specDescription = description; return self; } -(SpecElem*)specElem { return _specDescription.spec->Find( _code ); } -(BOOL)isDate { return self.specElem->IsDate(); } -(BOOL)isList { return self.specElem->IsList(); } -(BOOL)isReadOnly { return self.specElem->IsReadOnly(); } -(NSArray*)values { return [_specDescription valuesArrayForFieldTag:self.tag]; } -(BOOL)isAlwaysSet { return self.specElem->opt == SDO_ALWAYS; } -(BOOL)isIdentifier { return self.specElem->code == [_specDescription identifierCode]; } -(NSString*)tag { return [NSString stringWithStrPtr:self.specElem->tag]; } @end #pragma mark Statics NSArray* StructuredFieldsFromFieldStrings(NSArray*fieldStrings) { NSMutableArray * structuredFields = [NSMutableArray arrayWithCapacity:[fieldStrings count]]; for (NSString * fieldString in fieldStrings) [structuredFields addObject:[fieldString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; return structuredFields; } //This changes: //{ // "Fields" : [ // "651 User word 32 key", // "652 Email word 32 required", // "309 Options line 64 optional" // ] , // "Formats" : [ // "User 1 normal", // "Email 3 R" // ] , // "Values" : [ // "Options noallwrite/allwrite,noclobber/clobber,nocompress/compress,unlocked/locked,nomodtime/modtime,normdir/rmdir" // ] , // "Comment" : // "# A Perforce User Specification." //} // //to: //{ // "Fields" : [ // [ "651", "User", "word", "32", "key" ], // [ "652", "Email", "word", "32", "required" ] // ] , // "Formats" : [ // [ "User", "1", "normal" ], // [ "Email", "3", "R" ] // ] , // "Values" : [ // "Options", "noallwrite/allwrite,noclobber/clobber,nocompress/compress,unlocked/locked,nomodtime/modtime,normdir/rmdir" // ] , // "Comment" : // "# A Perforce User Specification." //} // //to: //{ // "Fields" : { // "User" : { // "code" : "651", // "dataType" : "word", // "length" : "32", // "fieldType" : "key", // "sequence" : "1", // "placementHint": "R", // } , // "Email" : { // "code" : "652", // "dataType" : "word", // "length" : "32", // "fieldType" : "required", // "sequence" : "3", // "placementHint": "R", // } , // "Options" : { // "code" : "309", // "dataType" : "line", // "length" : "64", // "fieldType" : "optional", // "values" : [ // [ "noallwrite", "allwrite" ] , // [ "noclobber", "clobber" ] , // [ "nocompress", "compress" ] , // [ "unlocked", "locked" ] , // [ "nomodtime", "modtime" ] , // [ "normdir", "rmdir" ] // ] // } // } , // "Comment" : // "# A Perforce User Specification." //} // NSDictionary* ParsedFieldDefinitionsFromRawFieldStrings(NSDictionary*rawFieldStrings) { NSMutableDictionary * fieldDefinitions = [NSMutableDictionary dictionaryWithCapacity:[rawFieldStrings count]]; for (NSString * key in rawFieldStrings) { id value = [rawFieldStrings objectForKey:key]; if ( [value isKindOfClass:[NSArray class]] ) [fieldDefinitions setObject:StructuredFieldsFromFieldStrings(value) forKey:key]; else [fieldDefinitions setObject:value forKey:key]; } return fieldDefinitions; } Spec* CreateSpecFromSpecDefinition(NSDictionary*dict) { enum FieldColumns { FieldCode = 0, FieldTag, FieldType, FieldLength, FieldOpt }; enum FormatsColumns { FormatTag = 0, FormatSequence, FormatPlacement }; enum WordsColumns { WordTag = 0, WordMaximum }; NSDictionary * fieldDefinitions = ParsedFieldDefinitionsFromRawFieldStrings(dict); NSArray * fieldFields = [fieldDefinitions objectForKey:@"Fields"]; NSDictionary * wordFields = [[fieldDefinitions objectForKey:@"Words"] NGA_dictionaryByKeyingOnIndex:WordTag]; NSDictionary * formatFields = [[fieldDefinitions objectForKey:@"Formats"] NGA_dictionaryByKeyingOnIndex:FormatTag]; NSDictionary * valueFields = [[fieldDefinitions objectForKey:@"Values"] NGA_dictionaryByKeyingOnIndex:0]; NSDictionary * presetFields = [[fieldDefinitions objectForKey:@"Presets"] NGA_dictionaryByKeyingOnIndex:0]; NSString * comment = [fieldDefinitions objectForKey:@"Comments"]; Spec * encoderSpec = new Spec; Error e; if ( comment ) encoderSpec->SetComment([comment strRef]); for (NSArray * specEntry in fieldFields) { NSString * tag = [specEntry objectAtIndex:FieldTag]; SpecElem * specElem = encoderSpec->Add([tag strBuf]); specElem->code = [[specEntry objectAtIndex:FieldCode] intValue]; specElem->maxLength = [[specEntry objectAtIndex:FieldLength] intValue]; specElem->SetOpt( [[specEntry objectAtIndex:FieldOpt] UTF8String], &e); specElem->SetType( [[specEntry objectAtIndex:FieldType] UTF8String], &e); NSArray * wordEntry = [wordFields objectForKey:tag]; if ( wordEntry ) specElem->nWords = (char)[[wordEntry objectAtIndex:WordMaximum] intValue]; NSArray * formatEntry = [formatFields objectForKey:tag]; if ( formatEntry ) { specElem->SetSeq( [[formatEntry objectAtIndex:FormatSequence] intValue] ); specElem->SetFmt( [[formatEntry objectAtIndex:FormatPlacement] UTF8String], &e ); } NSArray * valueEntry = [valueFields objectForKey:tag]; if ( valueEntry ) specElem->values = [[specEntry objectAtIndex:1] strBuf]; NSArray * presetEntry = [presetFields objectForKey:tag]; if ( presetEntry ) specElem->presets = [[specEntry objectAtIndex:1] strBuf]; } return encoderSpec; } #pragma mark P4SpecDataObjC P4SpecDataObjC::P4SpecDataObjC( NSDictionary * spec ) { mSpec = [[NSMutableDictionary alloc] initWithDictionary:spec]; } P4SpecDataObjC::~P4SpecDataObjC() { [mSpec release]; mSpec = NULL; } StrPtr * P4SpecDataObjC::GetLine( SpecElem *sd, int x, const char **cmt ) { *cmt = 0; NSString * key = [NSString stringWithStrPtr:sd->tag]; id val = [mSpec objectForKey:key]; if ( val == nil ) return 0; if( !sd->IsList() ) { mSavedValue = [val strRef]; return &mSavedValue; } // It's a list, which means we should have an array value here if ( ![val isKindOfClass:[NSArray class]]) { // LOG_DEBUG(@"%s should be an array element. Ignoring...", sd->tag.Text() ); return 0; } if ( (uint)x >= [val count] ) { return 0; } val = [val objectAtIndex:x]; if ( val == [NSNull null] ) return 0; mSavedValue = [val strRef]; return &mSavedValue; } void P4SpecDataObjC::SetLine( SpecElem *sd, int x, const StrPtr *v, Error *e ) { NSString * key = [NSString stringWithStrPtr:sd->tag]; NSString * val = [NSString stringWithStrPtr:*v]; if( sd->IsList() ) { id array = [mSpec objectForKey:key]; if ( array == nil || array == [NSNull null] ) { array = [NSMutableArray arrayWithCapacity:x+1]; [mSpec setObject:array forKey:key]; } [array replaceObjectAtIndex:x withObject:val]; } else { [mSpec setValue:val forKey:key]; } return; }