This is somewhat trickier than an SDEF-defined record, but it is also more flexible.
script UserFieldCommand
property parent : class "NSScriptCommand"
property aedClass : class "NSAppleEventDescriptor"
property nssClass : class "NSString"
property nsnClass : class "NSNumber"
on performDefaultImplementation()
-- For demo purposes, the data is just hard-coded here
set theAuthor to "Mark Twain"
set birthYear to 1835
set isFavorite to true
-- return an NSDictionary whose keys are NOT defined in an sdef record-type (aka, user fields)
-- in this example, Spanish words are used just for fun
set aDict to current application's NSDictionary's ¬
dictionaryWithDictionary:{autor:theAuthor, nacio:birthYear, favorito:isFavorite}
return makeUserRecordDescriptor(aDict)
end
# ASOC implementation of - (NSAppleEventDescriptor *)scriptingRecordDescriptor for an NSDictionary
# Creates an empty record descriptor and an empty list descriptor, then
# Iterates over the dictionary and inserts descriptors for each key and each value into the list descriptor
# Finally, populates the record descriptor with the type 'usrf' and the list descriptor
on makeUserRecordDescriptor(aDict)
log aDict
set recordDescriptor to aedClass's recordDescriptor()
set listDescriptor to aedClass's listDescriptor()
set typeUserField to 1970500198 -- 'usrf'
set itemIndex to 1 -- AS records are 1-based
repeat with aKey in aDict's allKeys()
set aVal to aDict's valueForKey_(aKey)
-- The values can be several different types. This code DOES NOT handle them all.
set isStringValue to aVal's isKindOfClass_(nssClass's |class|) = 1
set isNumericValue to aVal's isKindOfClass_(nsnClass's |class|) = 1
set isBooleanValue to aVal's className()'s containsString_("Boolean") = 1
-- Insert a descriptor for the key into the list descriptor
set anItem to aedClass's descriptorWithString_(aKey)
listDescriptor's insertDescriptor_atIndex_(anItem, itemIndex)
set itemIndex to itemIndex + 1
-- Insert a descriptor (of the correct type for the value) into the list descriptor
if isStringValue
set anItem to aedClass's descriptorWithString_(aVal)
else if isBooleanValue
set anItem to aedClass's descriptorWithBoolean_(aVal's boolValue())
else if isNumericValue
set intValue to aVal's intValue()
set fpValue to aVal's doubleValue()
if intValue = fpValue
set anItem to aedClass's descriptorWithInt32_(aVal's intValue())
else
set anItem to aedClass's descriptorWithString_(aVal's stringValue) # TODO: 'doub'
end
else
set anItem to aedClass's descriptorWithString_("Unhandled Data Type")
end
listDescriptor's insertDescriptor_atIndex_(anItem, itemIndex)
set itemIndex to itemIndex + 1
end
recordDescriptor's setDescriptor_forKeyword_(listDescriptor, typeUserField)
return recordDescriptor
end
end
And finally the test script:
tell application "TestRecordReturn"
retrieve usrf info --> {autor:"Mark Twain", nacio:1835, favorito:true}
end tell
The command handler is very similar to the SDEF-based command; an NSDictionary is again used to create the data for the record.
However, since the ASOC engine has no SDEF info to go on, we have to implement our own method to convert the NSDictionary into
the correct NSAppleEventDescriptor to represent the record. This is what -scriptingRecordDescriptor would do in a Objective-C implementation.
The returned descriptior must be a record with a single key/value pair.
The key is 'usrf', in the form of an integer.
The value is a list descriptor containing a key descriptor followed by its value descriptor, which together encode the key/value of each dictionary pair.
While the dictionary keys are always strings here, the associated values can be almost anything. The sample implements string, boolean, and integer types.
Other value types are more difficult, and perhaps impossible in a script-only based implementation.
For instance, to fix the implementation of a double value above, you could add an Objective-C class with this method:
+ (NSAppleEventDescriptor *)descriptorWithDouble:(double)aDouble
{
double val = aDouble;
return [NSAppleEventDescriptor descriptorWithDescriptorType:'doub' bytes:&val length:sizeof(val)];
}
How much work you have to do is based on how complex your returned record is. Keep it simple if you can!
— Ron