AppleScript. The Definitive Guide
Authors: Neuburg M
Published year: 2006
20.5. Multiple-Word Terms
Many terms, especially commands in scripting additions, consist of multiple words . An example frequently used in this book is display dialog . You might think that such a term would present extra challenges for resolution, but in actual fact just the opposite appears to be the case; multiple-word terms are a good thing:
Though I don't know the details, a natural explanation of AppleScript's success in resolving multiple-word terms would be that it tries the longest possible combinations of words first.
A multiple-word property name can be a little troublesome . The most commonly encountered example is text item delimiters (Chapter 16). Here's what happens when you use this term in a tell block targeting a scriptable application:
tell application "Finder" get text item delimiters -- error: Finder got an error: Can't get text item delimiters end tell
In that code, AppleScript successfully resolves text item delimiters as the 'txdl' property, but then it makes a mistake: it sends an Apple event to the Finder, asking for this property. The Finder has no 'txdl' property, so it returns an error. The usual workaround is to add my or AppleScript's :
tell application "Finder" get my text item delimiters -- fine end tell
But no Apple event is sent to the Finder in the case of a one-word global property:
tell application "Finder" get space end tell
I believe this is the same behavior discussed in "No Terminology Clash," earlier in this chapter: space is a name already in scope, and we don't say this is the Finder's space , so it is assumed to be our space (meaning AppleScript's space ). Evidently this rule breaks down with multiple-word properties. Fortunately, multiple-word properties that you might be tempted to use unqualified (without saying of something) are very rareindeed, text item delimiters is probably the only one.
20.6. What's in a Dictionary
Dictionaries are not presented to you as a verbal explanation with instructions for their use. They are structured in tabular form and typically displayed through a special dictionary viewer window within a script editor application. It takes time and practice to learn to use this dictionary viewer and to get the most out of its display . You need to be adept at reading and understanding a dictionary display if you are to communicate successfully with scriptable applications and scripting additions.
The good news is that, starting in Tiger, the Script Editor 's dictionary display has been improved (for the first time since the dawn of AppleScript). Back in the bad old days, the dictionary was shown in a simple, primitive way (Figure 20-1). Its table of contents was a simple scrolling list down the left side, organized by "suites" that were not alphabetically arranged. Because you didn't know what "suite" a particular item was in, you had to scroll through the list looking for it in each suite. Clicking an entry in the list displayed its information in the main pane; that information was static and terse.
In Tiger, the list on the left is an outline whose headings can be opened and closed; alternatively, you can use a columnar browser at the top of the window (Figure 20-2). A search field at the top lets you jump directly to a desired entry. The information for an entry is more copious : you learn not only what elements a class contains but also what classes it is an element of. And class names are hyperlinks , which you can click to jump to their entries and read their information.
In Script Debugger , the dictionary display is even better. (See Figure 20-3; the screenshot has been doctored to shorten the window by omitting some of the elements.)
The columnar browser at the top of Script Debugger's dictionary window has categories grouping commands and classes, letting you escape the tyranny of "suites." In the information displayed for an entry, the plural is given; properties and elements are shown in a clean, tabular layout; all datatypes (not just classes) are hyperlinked; and an extra section at the bottom of the display lists all classes of which this is an
Figure 20-1. The dictionary display in Script Editor 1.8.3
element or property and all verbs of which this is a parameter type. There is also an option, not shown here, to display the raw four-letter codes in addition to their English-like translations. (I'll talk about further features of the Script Debugger dictionary display later on.)
Not only the dictionary display has been improved; starting with Tiger, applications can take advantage of a new dictionary format . In the past, an application expressed its dictionary as an 'aete' resource; starting in Tiger, an application can express its dictionary as an sdef file (an XML text file), which AppleScript can read directly (see "Dictionary" in Chapter 3). The new sdef format will give dictionaries more flexibility and allow their display to be more expressive and helpful than in the past; for instance, an sdef dictionary can include extensive comments and examples. However, AppleScript on pre-Tiger systems can't read an sdef, so it may be some time before applications actually start relying on the new dictionary format. (In fact, I don't know any major application that uses it.) Thus, in the discussion that follows , I'll treat 'aete' format as the default case.
20.6.1. Value Types
There are many places where a dictionary specifies a value's type. Every property is of some type, and the dictionary tells you what it is. (Examples appear in Figure 20-2; a disk object's capacity is a double integer, its ejectable is a boolean, and so forth.) Each of a command's parameters is of some type, and the command's returned value, if any, is of some type (Figure 20-4).
Figure 20-2. The dictionary display in Script Editor 2.1
In the dictionary itself, such value types are expressed as four-letter codes. Such a four-letter code may designate a built-in AppleScript datatype (see Chapter 13) or a class defined by the application (see "Enumerations" and "Classes," later in this section). This allows, say, iTunes's dictionary to specify that a value should be a string or a TRack . However, this is not enough to express all that a dictionary might want to tell you, so the dictionary specification permits certain further value types and options:
Value type information is for the human reader only. The AppleScript compiler, for example, does not check to see whether the value you actually supply as a command parameter or to set a property is of the type specified by the dictionary; and the runtime engine doesn't care what type of value a scriptable application returns.
This being so, it is difficult to see why the dictionary specification requires that types be encoded as four-letter codes; four-letter codes are a machine-readable format, but no machine is going to read this information. The communicative worth of value types in the dictionary is thus limited unnecessarily.
Take, for example, iTunes's convert command. Its direct object is described in the dictionary as list of reference . This is both too restrictive and too general. It's too restrictive because the direct object need not be a list. It's too general because "reference" could mean anything, whereas in fact what iTunes wants is quite specific: a track or an alias. The dictionary specification provides no convenient way to say "a track or an alias, either singly or a list" by means of four-letter codes; so iTunes's developers have effectively left the truth to be discovered by experimentation.
An enumeration is a list of four-letter codes. It is treated as a class, it has a four-letter code identifying it, and it is used as a value type in the dictionary just like any other class (see "Value Types," earlier); a property value can be specified as an enumeration, as can a command parameter or result. The four-letter codes that constitute the list are called enumerators ; they are constants. When a value type is an enumeration, the actual value will be an enumerator .
For example, in the Finder's dictionary, the disk class's format property is specified as an enumeration called an 'edfm' . That enumeration is defined elsewhere as having enumerators including Mac OS Extended format , audio format , and so on. So the actual value will be Mac OS Extended format or some other member of the list. In BBEdit, the close command's saving parameter is a 'savo' enumeration. That enumeration is defined elsewhere as having enumerators yes , no , and ask . So you are expected to supply yes , or no , or ask as the value of this parameter.
In the Script Editor's display of a dictionary, enumerations are not listed along with classes. Rather, an enumeration is shown as a value type by listing its enumerators (separated by slashes ). In Figure 20-2, the format property is not said to be a certain kind of enumeration; instead, a list of that enumeration's enumerators is displayed. Similarly, in the Script Editor's display of BBEdit's dictionary, the close command's saving parameter is not said to be a certain kind of enumeration; its value is given as yes/no/ask , a list of that enumeration's enumerators. This way of displaying an enumeration rapidly becomes all but illegible as the number of enumerators increases (as Figure 20-2 demonstrates ). Also, enumerations and enumerators may have comments describing their meaning and usage; this information is completely stripped from the display.
Script Debugger's display is much more like what's actually in the dictionary. Enumerations are listed separately in tabular form, showing their enumerators and any comments. A property or parameter whose value is an enumeration gives the enumeration as a hyperlink; you click the hyperlink to see the listing of its enumerators.
In your code, when you supply a value that is stated in the dictionary to be an enumeration, the AppleScript compiler does not check to see whether that value is in fact an enumerator of the specified enumeration.
A class is a datatype (see Chapter 13). Applications are free to define new datatypes in addition to those provided by AppleScript. These will generally correspond to the various types of thing the application operates on. For example, the Finder is all about files and folders on disks, so it has a file class, a folder class, and a disk class.
Internally, the dictionary expresses a class as a four-letter code. Where a class is already defined by AppleScript, the dictionary uses (or should use) the same four-letter code that AppleScript uses. In the dictionary display, you will see the English-like name of the class. (See "Value Types," earlier in this chapter.)
For most classes, the dictionary will provide both a singular and a plural form for the English-like term . So, for example, the Finder defines both file and files , both folder and folders , both disk and disks . In the Script Editor's display of the dictionary, a class's plural appears when that class is listed as an element of another class (Figure 20-2). In Script Debugger, the plural is given explicitly along with the class's name (Figure 20-3).
The AppleScript compiler uses this information to treat singular and plural alternatives with some intelligence (see "Nonsensical Apple Events," earlier in this chapter). For example, a bare plural is often interchangeable with every plus the singular: you can tell the Finder equivalently to get disks or to get every disk . This is not because AppleScript has any natural-language intelligence; it's because the dictionary explicitly says that disks is the plural of disk .
Sometimes, a dictionary won't provide a plural form for a class name. Perhaps there is only one object of that class (it occurs only as a property, not an element). For example, the Finder provides only the singular for desktop-object , the class of the desktop. In a case like this, you can't use the plural or every with the singular.
It is also possible for a class to be declared as its own plural ( text does this, for example). In such a case, you can use every but not a separate plural term.
220.127.116.11. Class inheritance
At an early stage in the history of AppleScript, it was observed that dictionaries were becoming large and repetitious. Several classes might share some of the very same attributes, and it seemed silly, both in the dictionary resource and in its display, to repeat the information about these attributes in the entry for each of those classes. Also, in those early days there were limits on how large a dictionary could be, so there were practical reasons for wanting to prevent unnecessary repetition.
Thus a mechanism was instituted for separating out attributes common to multiple classes. This is done by having the dictionary specify that one class inherits from another class. For example, in the Finder, there is a class item ; both the file class and the container class inherit from item . This simply means that whatever properties the item class has, both the file class and the container class have alsothough of course the file class and the container class might have other properties that the item class does not. The item class has a name property; therefore, so does the file class, and so does the container class. Similarly, the folder class inherits from the container class. Therefore it also inherits from item , and so a folder has a name property too. Furthermore, the container class has an entire contents property; therefore, so does the folder class. We also say that item is the superclass of file and container , and that file and container are two of its subclasses .
Thus we have a hierarchy of inheritance. An application's dictionary can include more than one such hierarchy. For example, the Finder also has a class desktop window , which inherits from Finder window , which inherits from window .
It must be stressed that this inheritance is not a true object-oriented relationship among classes! It has nothing to do with object-orientation, and it doesn't reflect any reality about how these datatypes are implemented in the application. There is no sense, in AppleScript, in which a folder "is" a container because the folder class inherits from the container class. Inheritance here is merely a notational device for expressing commonality of attributes in the dictionary.
Because of class inheritance, it is possible for a class to be abstract that is, a class may exist only as a way of encapsulating a set of attributes so that other classes can inherit them; it is not the class of any actual object to which the programmer will ever refer. For example, BBEdit's dictionary defines an item class whose sole function is as an ultimate superclass, just so that every other class will have an ID property and a container property inherited from it; there's no property or element anywhere in the dictionary whose class is item .
The Finder, too, has an item class that acts primarily as a superclass. No Finder object is of the item class, so it is reasonable to describe this class as abstract. Nevertheless, it's a bit different from BBEdit's item class: in the Finder, some classes have an item element. So you can use the term item when scripting the Finder, in ways that you never would when scripting BBEdit. For example:
tell application "Finder" class of item 1 of desktop -- document file (not item ) end tell
We come now to the question of how inheritance is portrayed in a dictionary display. In the Script Editor, inheritance is simply stated as a fact. For example, in Figure 20-2, the disk class listing says: "inh. container > item." This means that disk inherits from container , which inherits from item . The implication is that when you look at the disk class listing in Script Editor, you're not being shown all the properties of disk . To see the rest of disk 's properties, you need to look at container and item as well. Thus inheritance may actually make it harder for you to see a class's properties; you have to keep shuttling from class to class, up the entire inheritance chain, finding what properties each superclass has, and trying to remember them all.
Script Debugger is more helpful. The listing for a class in Script Debugger can include its inherited attributes. Thus you are shown all of a class's properties in one place. Another nice feature of Script Debugger is that it graphs each of an application's inheritance hierarchies (Figure 20-5). (Script Editor can display classes organized by inheritance in its browser, but it shows only the single hierarchy starting with the item class, if there is one.)
Figure 20-5. Script Debugger's display of the Finder's class inheritance hierarchies
Elements (as well as properties) are inherited by subclasses, but most application developers do not bother to take advantage of this in constructing the dictionary. For example, in the Finder the elements of the folder class are exactly the same as the elements of the container class; yet the dictionary explicitly repeats them in both classesit does not, as it does for properties, include them only in the container class, even though this would be perfectly sufficient, as the folder class would inherit them.
20.6.4. Properties and Elements
A class can have two kinds of attribute: properties and elements (compare "Properties and Elements" in Chapter 11). A class's properties and elements are part of that class's listing in the dictionary.
A property in the dictionary involves two four-letter codesone for the property's name and one for its value type (see "Value Types," earlier in this chapter). If a property name is the same as the name of a property defined by AppleScript, they should have the same four-letter code. For example, in the Finder the item class's name property has the four-letter code 'pnam' and a value type 'utxt' (Unicode text); the code 'pnam' matches the code for the name property defined by AppleScript itself. A property can be specified as read-only ; this is displayed in Script Editor as "[r/o]", and in Script Debugger as "get" (as opposed to "get/set").
An element in the dictionary is the four-letter code of a class (see "Classes," earlier). Thus an element name in the dictionary display is a class name. The dictionary can also provide the forms of element specifier that may be used to access this element; in the Tiger Script Editor this information is stripped from the dictionary display, but Script Debugger includes it (contrast Figure 20-2 with Figure 20-3). An element can also be specified as read-only; this is much rarer than a read-only property. Script Debugger marks such an element as "get" (as opposed to "get/make/delete," the default).
The complete schema of a scriptable application's properties and elements constitutes its object model (see "Attributes" in Chapter 11). The object model is what makes it possible to refer to any actual object within the application. In theory, all actual objects should be connected as a kind of tree, which should be navigable in its entirety starting at a single pointnamely, the application itself, the only thing to which you have a reference when you start targeting that applicationand it should be possible to deduce this tree correctly from the dictionary. The tree should start at the application class. For this reason, the application class is usually the first thing you'll turn to when you begin your study of an application's dictionary.
In real life, things are not so simple, for various reasons. Objects can be mutually linked as elements of one another; class inheritance can muck up the tree; there can be more than one tree, with orphaned classes; there can be shortcuts, sometimes undocumented, that jump through levels of the tree implicitly; and the dictionary can simply lie ("Defects in the Object Model," later in this chapter). Nevertheless, the object model is one of your basic keys to successful scripting of an application, and much of the struggle of using a dictionary involves trying to deduce the object model so that you can refer to a desired object (see, always see, Appendix A).
The Script Editor helps you with a "containment" view of a dictionary's classes (Figure 20-2; to see the containment hierarchy, you would press the second segment of the View button in the toolbar). Script Debugger goes further, drawing a graph of the tree rooted at any class that has elements. Even more important, Script Debugger has an Explorer view that probes a running application's object model in real time, showing all elements and properties for every actual object in the hierarchy at that moment, telling you their values and showing you how to refer to them (see Figure 2-4).
The AppleScript compiler does not enforce the distinction between a property and an element, and does not enforce encapsulationfor example, it doesn't look in the dictionary to see whether the property you are ascribing to an object really is a property of that object. See "Nonsensical Apple Events," earlier in this chapter.
When the result of a command sent to a scriptable application is an object of a class defined by that application, it is usually a reference to an object in that application's world (Chapter 12). For example, when you ask BBEdit to get document 1 , what comes back is a reference to document 1you aren't handed the entire literal document. If you want to know more about document 1, you can explore its properties, and when you do, an Apple event is sent to BBEdit. That's because this object lives in BBEdit's world; all you've got is a reference:
tell application "BBEdit" set d to document 1 -- result is a reference get name of d -- sends Apple event to BBEdit end tell
But sometimes what happens is quite different. You receive from the application what appears to be an object. It has properties. It seems to be an object of a class defined by the target application. But examining its properties doesn't cause any Apple event to be sent. What you have isn't a reference: it's the entire object itself:
tell application "BBEdit" set d to find tag "style" start_offset 0 class of d -- tag result start_offset of tag of d -- 225 ; no Apple event is sent end_offset of tag of d -- 259 ; no Apple event is sent end tell
What you've got is actually a record (Chapter 13) disguised as a class. You can think of it as a pseudo-class . In the dictionary, it's listed as a class. You have no certain way of knowing, from the dictionary display, that this command yields a record rather than a reference to an object. And if you ask it what class it is, this thing says tag result , not record . But that's just trickery . It is a recorda record with an item named class ! Thus when you ask it for its class, you're given the value of that item, which is tag result .
The example illustrates why this device is useful. BBEdit wants to hand you a package of information consisting of a whole bunch of values at once. These values have names so that you can examine the ones you're interested in. A record is a perfect vehicle for conveying this, because that's precisely what a record isa package of name-value pairs. At the same time, this record needs to be described in the dictionary, because if it weren't, it would be useless to you. A record has no introspection! You can't ask a record: "So, what properties have you got?" You have to know in advance. The description of the record in the dictionary tells you what properties the record has and what they signify. To provide you with this description, the dictionary simply treats the record as a class; the record properties are treated as class properties (and the "class" has no elements).
Because the names of the properties of this record are defined in the target application's dictionary, you may not be able to extract them outside a tell block targeting that application. For example, this doesn't work:
tell application "BBEdit" set d to find tag "style" start_offset 0 end tell start_offset of tag of d -- error: Can't get tag of...
There's no formal marker in an 'aete' -format dictionary to distinguish a record from a class. Script Editor's dictionary display thus does nothing to let you know that a class might really be a record. Script Debugger, on the other hand, does some detective work. If a class has no elements and is not an element of any class, Script Debugger calls it a record. This results in some false positives (the Finder's Finder preferences property, for example, is not a record, though Script Debugger calls it one), but it's still a helpful heuristic. In an sdef-based dictionary, the distinction can be made formally , so we can expect dictionary displays to be positively useful in this regard as applications begin to adopt the sdef format.
Events are the verbs of the AppleScript language. An event is an Apple event (Chapter 3). An Apple event is specified using two four-letter codes. So, for example, the Finder's reveal command is the event 'misc\mvis' .
(In reality the two four-letter codes together are simply a 64-bit integer. The exact convention used to present them to a human reader is unimportant. I like to separate them with a backslash. Others use a forward slash, or put each half in single quotes, or whatever.)
An Apple event can take parameters . The default parameter is the "direct object "; its four-letter code is '----' , which has no English equivalent. Others parameters have names; the name is a four-letter code, like a property. (See "Event Handlers" in Chapter 9 and "Target" in Chapter 11.)
The dictionary entry for an event lists the following information:
The dictionary display expresses essentially the same information: it tells you a command's name, the names and value types of the parameters (and whether they are optional), and the value type of the result, if any. Figure 20-6 shows the Script Editor's display of iTunes's add command. The direct object is said to be a list of alias ; the to parameter is said to be a location reference , and optional (that's the meaning of the square brackets); and the result is said to be a track . Script Debugger's display (Figure 20-6) includes a template for actually using the command and a tabular display of its parameter information; if you press the Tell button in the toolbar, the command template is pasted into your script.
In an sdef-based dictionary, a distinction can be made formally between commands (Apple events that your script sends) and events (Apple events sent by an application to your script, as discussed in "Event Handlers" in Chapter 9). In the future, therefore, we can expect dictionary displays to reflect this distinction.
Figure 20-6. Script Debugger's display of a command
The AppleScript compiler enforces the rule that the only parameters following a command should be the direct object and the labeled parameters defined by that particular command. It knows nothing about required and optional parameters, and it does not enforce the value types expected for each parameter.
At the top level of a dictionary, events and classes are grouped into suites . You can see this in Figure 20-2, where the first column of the browser at the top of the Script Editor dictionary display consists of suites: the Standard suite , the Finder Basics suite, the Finder Items suite, and so forth.
A suite is in large part just a device for organizing the top level of a dictionary. Whether this is a good organizational device is a matter of opinion. Some people profess to like them; I think they're the work of the devil . In part my opinion goes back to the days (Figure 20-1) when the only way to find anything in a dictionary window was through a scrolling list that clumped things into suites. You didn't know what suite anything was in, and the suites weren't listed in alphabetical order, so you could never find anything. Things are better now. I still don't know what suite anything is in, but I don't need to know, because Script Editor's dictionary has good searching. Best of all is Script Debugger's approach, where you can ignore suites altogether: the first column of the browser (Figure 20-6) lets you view commands, classes, records, and so forth without regard to suites.
Suites are also a kind of historical fossil. Back in the early days of System 7, even before AppleScript was made public, Apple Computer realized that if developers started freely adding Apple events to their applications and assigning them four-letter codes, there would be massive conflict and duplication of effort. Imagine a hundred different versions of the delete command, with a hundred different names and four-letter codes and sets of parameters; multiply that by the number of common commands and classes that scriptable applications were likely to need, and you can see the nightmare vision that was starting to unfold. So Apple created some standardized sets of commonly needed Apple events and classes, not telling developers how to implement them, but requesting them, where possible, to use Apple's choice of four-letter codes and English-like terminology, Apple's choice of classes and commands and parameters. The result was a centralized databasethe Apple event registry . The suites resulted from this codification.
Interestingly, much of the Apple event registry contents still exist, as part of AppleScripteven though, ironically, most of the suites have fallen into disuse (see "The 'aeut' Resource," later in this chapter). Thus AppleScript itself contains, for example, a suite of Macintosh Connectivity Classes , with names like bus slot , which you can't see and which no application (as far as I know) has ever bothered to implement. On the other hand, certain suites present in AppleScript are quite commonly implemented, in whole or in part. The Standard Suite , sometimes referred to as the Core Suite , is the source of commands like exists , make , and select . The Text Suite, containing terms like word and text style info , is sometimes used as a starting point for applications that manipulate text. On the other hand, the Miscellaneous Suite , a frequent repository for commands like copy , cut , and undo , is a product of the Apple event registry but is not built into AppleScript itself.
You may recall that in Chapter 1 I said ("Is This Application Scriptable?") that the mere presence of a dictionary wasn't enough to prove that an application was scriptable. Apple's own Dictionary application, which I used as an example, essentially reflects in its dictionary the Standard Suite and the Text Suite from AppleScript itself. But this is really just a bug, a kind of automatic reflex of the framework used to build the application (basically, any Cocoa application that claims to be scriptable inherits this meaningless dictionary even if it isn't really scriptable).
A word needs to be said about the Required Suite . In the current implementation of AppleScript this is empty, but in the early days of System 7 these were the four Apple events to which every application, scriptable or not, had to respond in order to be System 7-native in the first place. That's because these were the fundamental messages sent by the Finder to do such basic things as launch and quit the application. The old Required Suite Apple events have been moved to the Standard Suite , and to this day, every application responds to them. They are shown in Table 20-1.
Table 20-1. The original Required Apple events
AppleScript. The Definitive Guide
Authors: Neuburg M
Published year: 2006
Apple Training Series: AppleScript 1-2-3
Apple Automator with AppleScript Bible
AppleScript (Developer Reference)
AppleScript: The Missing Manual
Apple Training Series: AppleScript 1-2-3
Apple Automator with AppleScript Bible
AppleScript (Developer Reference)
AppleScript: The Missing Manual