Sunday, November 19, 2017

#1 2011-04-18 10:14:58 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4421

User record property labels as text

regulus633 has demonstrated a short method of getting text representations of user record property labels. (If you need to do this, you're not using AppleScript properly; but the question keeps coming up.)

Applescript:

set theRecord to {firstName:"John", lastName:"Smith"}

tell application "Automator Runner"
   set keysList to call method "allKeys" of theRecord
end tell

If the record contains any reserved labels, the script renders them in the returned list as the integer values of their four-byte compiled tokens.

It's rather irksome having to script an application just for this purpose, especially if you don't normally have the application open. The time taken to launch it and quit it can make the script quite slow. So here's a script which uses vanilla methods to the same effect. It uses the File Read/Write commands to write the record to a temporary file and to parse the file's contents:

Applescript:

on allKeys(rec)
   script o
       property keyList : {}
       
       -- Parse the temporary file for top-level record labels.
       on parseAllLabels(fRef) -- Enter here having skipped "reco" at the beginning of the file.
           -- The next four bytes give the number of reserved-label properties in the record. Properties with user labels are grouped togther as a list value with the reserved label "usrf".
           repeat (read fRef for 4 as integer) times
               set labelKey to (read fRef for 4 as integer) -- Get each reserved label as a 4-byte integer value.
               if (labelKey is 1.970500198E+9) then -- If it's "usrf", extract the user label(s).
                   parseUserLabels(fRef)
               else -- Otherwise store the integer and wind on to the next label.
                   set end of my keyList to labelKey
                   skipValue(fRef)
               end if
           end repeat
       end parseAllLabels
       
       -- Extract user labels from the "usrf" list.
       on parseUserLabels(fRef) -- Enter here having just read "usrf".
           read fRef for 4 -- Skip the next four bytes ("list").
           -- The four bytes after that give the number of items in the list. The odd-numbered items are the labels and the even-numbered ones the values.
           repeat (read fRef for 4 as integer) div 2 times
               set labelTextClass to (read fRef for 4 as string)
               set labelByteLength to (read fRef for 4 as integer)
               if (labelTextClass is "utxt") then -- Unicode text in Snow Leopard (and Leopard?).
                   set end of my keyList to (read fRef for labelByteLength as Unicode text)
               else -- "TEXT" in Tiger.
                   set end of my keyList to (read fRef for labelByteLength as string)
                   -- Skip any padding of the label in the file to an even number of bytes.
                   read fRef for (labelByteLength mod 2)
               end if
               skipValue(fRef)
           end repeat
       end parseUserLabels
       
       -- Recursively coax the file mark past the value of a property in the file,
       on skipValue(fRef)
           set valueClass to (read fRef for 4 as string) -- Get the value class code.
           if ((valueClass is "reco") or (valueClass is "list")) then
               repeat (read fRef for 4 as integer) times -- Skip each entry in a subordinate record or list.
                   if (valueClass is "reco") then read fRef for 4 -- Skip the reserved label if in a record.
                   skipValue(fRef)
               end repeat
           else
               set valueByteLength to (read fRef for 4 as integer)
               read fRef for (valueByteLength + valueByteLength mod 2) -- Skip the value and any padding.
           end if
       end skipValue
   end script
   
   -- Open a temporary file to which to write the record and from which to parse it.
   set fRef to (open for access file ((path to temporary items as Unicode text) & "Rec.dat") with write permission)
   try
       set eof fRef to 0
       write rec to fRef
       read fRef from 5 for 0 -- Set the file mark to 5 to skip the opening "reco".
       o's parseAllLabels(fRef) -- Parse the labels.
   on error msg
       display dialog msg
   end try
   close access fRef
   
   return o's keyList
end allKeys

set theRecord to {firstName:"John", otherNames:{"Aardvark", "Lazenby"}, lastName:"Smith"}
allKeys(theRecord)

The script advances the file mark past information it doesn't need by simply reading the information from the file (and then ignoring it). It might be more efficient to maintain an ongoing calculation of the file mark and just read what's actually needed with the help of 'read's 'from' parameter.

Last edited by Nigel Garvey (2011-04-18 10:40:05 am)


NG

Offline

 

#2 2011-04-18 02:31:52 pm

regulus6633
Member
From:: Taulov, Denmark
Registered: 2006-11-02
Posts: 1695
Website

Re: User record property labels as text

That's pretty amazing Nigel. It really shows a lot of knowledge. I'm impressed.

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)