20.5.
|
20.6. What's in a DictionaryDictionaries 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
In Tiger, the list on the left is an outline whose headings can be opened and closed; alternatively, you can use a
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
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
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
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
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
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
20.6.2. Enumerations
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
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
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. 20.6.3. Classes
A
class
is a datatype (see Chapter 13). Applications are free to define new datatypes in addition to those provided by AppleScript. These will
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
20.6.3.1. Plurals
For most classes, the dictionary will provide both a singular and a plural form for the English-like
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
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. 20.6.3.2. Class inheritance
At an early stage in the history of AppleScript, it was
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
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
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
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
20.6.4. Properties and ElementsA 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
In real life, things are not so simple, for various reasons. Objects can be
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. 20.6.5. RecordsWhen 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
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
20.6.6. EventsEvents 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
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
20.6.7. SuitesAt 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
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
Interestingly, much of the Apple event registry contents still exist, as part of AppleScripteven though, ironically, most of the suites have
You may recall that in Chapter 1 I said ("Is This Application Scriptable?") that the mere presence of a dictionary wasn't enough to
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
|