previous chapter chapter"> contents
page top
page next chapter chapter">

HasIndexing

January 21, 1994
last modified April 1, 1994

Defined in List.Def 
Mixes in with Object
Inherits from HasIterator
Inherits interface from HasCount

Class Description

The HasIndexing mixin provides a way for you to create a subclass for which the concept of an index has some meaning. The subclass might have an index field; the subclass might be a linked list (like Linkable) that has an implicit concept of indexing. You override the At method to provide a way to get at objects using the indexing concept. The At method might just read a field, or it might (like Linkable_At) step through some number of links. What the index means is up to youyour class could be a sorted list, or it could be an unsorted collection of objects related in some way.

The HasIndexing mixin differs from the HasCount mixin in that it provides an ordering for a collection, and not just a count.

Remember that if the documentation and the software (especially the definition files) disagree, always trust the software.

Using the HasIndexing mixin

Since class HasIndexing is a mixin, you'll never work directly with HasIndexing objects. Instead, you'll work with objects of classes that inherit from the HasIndexing mixin, such as ObjectList. Many of the list classes inherit from the HasIndexing mixin, and so will many of the list subclasses you create. In addition, class Linkable inherits from the HasIndexing mixin, which means that any descendent of class Viewable does as well.

Programming Information

Instantiate: never
Subclass: always
Call its methods: sometimes

Your subclass must implement the Count method inherited from the HasCount interface.

Your subclass must also implement the At method, a crucial method, since it provides callers a way to get at objects using the indexing concept. You might want to spend time tuning the performance of your At method, since the system calls it quite often.

Methods you might call

The HasIndexing class has the following methods you might Call:

Method Description
Getting elements
First Return the element with index 1
FirstNonNil Return the first object that isn't nilObject
Last Return the object with the highest index
Iterators
Each Call a function on each object
Searching
Search Return the index of the matching object
FindAfter Return the index of the first matching object with index higher than the given number
FindEqual Searches using IsEqual to determine equality instead of ==
String matching
FindClosestMatch Returns the index of item (if any) whose search name most closely matches the given string
FindExactMatch Returns the index of item (if any) whose search name exactly matches the given string
SearchForExpansion Search the collection of objects for a text object that appropriately expands an abbreviation

Methods you might override

Whenever you create a subclass of the HasIndexing class, you must override the following method:

Method Description
At Returns the element at the given index
Count Returns the number of elements in a collection

Description of fields

The HasIndexing mixin defines no fields.

Method Descriptions

Getting

At

operation At(index: Unsigned): Object, noFail, safe

Call: often
Override: always

Call At to get the object at position index.

Every time you create a class that inherits from the HasIndexing mixin, you must override At. At should return the object at position index. What the index means for your subclass is something you must determine. At could search for an object with index in a particular field. At could step through index links in a linked list, returning the last object.

The following code fragment shows how the Linkable class defines the At method:

self = First(self);
while (--index > 0)
   if ((self = Next(self)) == nilObject)
      break;
return self;

Apply this example with some care, since Linkable must also override the First method to avoid a nasty case of recursive definitions. Here is another example, this time from the ObjectList definition of At:

ObjectID   result;
long   *ptr = BeginReadFieldsOf(self, Object_);
long   count = *ptr;
EndRead(self);
if (index < 1 || index > count) return nilObject;
ptr = BeginReadExtra(self);
result = *(ptr + index - 1);
EndRead(self);
return MakeUsable(self, result);

First

attribute First: Object, readOnly;
// operation First(): Object

Call: sometimes
Override: rarely

Call First to get the object at index 1. First relies on At.

FirstNonNil

attribute FirstNonNil: Object, readOnly;
operation FirstNonNil(): Object

Call: sometimes
Override: rarely

Call FirstNonNil to get the first object in the group that is not the nil object. FirstNonNil uses the Each method to compare each object with nil.

Last

attribute Last: Object, readOnly;
operation Last(): Object

Call: sometimes
Override: always

Call Last to get the last object in the group, that is, the object whose index number is the number of objects in the group. Last relies on a subclass's implementations of Count and At.

Iterators

Each

overrides Each(proc: EachFunction; VAR params: Parameters): Object

Call: sometimes
Override: sometimes

Call Each to call a function on each member of a group.

See the section on HasIterator_Each for a complete discussion of the basic behavior of the Each method. You will see various implementations of Each throughout the Magic Cap system.

HasIndexing_Each relies on your subclass's implementation of At.

Here is an example of how the HasIndexing mixin itself uses Each to define the FirstNonNil method:

Private ObjectID
ReturnNonNil(ObjectID item, Parameters *parameters) {
   #pragma unused (parameters)
   return item;
}
Method ObjectID
HasIndexing_FirstNonNil(ObjectID self) {
   ObjectID result = Each(self, ReturnNonNil, nil);
   if (result == nilObject)
      Warn(cannotFindIndex);
   return result;
}

Searching

Search

operation Search(target: Object): Unsigned, noFail;

Call: sometimes
Override: sometimes

Call Search to search your collection of objects for a given object. target is the desired object; Search returns the index of the first occurrence of this object in the collection of objects. If the requested object isn't in the collection, the result is zero.

The HasIndexing mixin definition of Search simply calls FindAfter with an afterIndex of 0. (See the description of FindAfter just below this paragraph.) Override Search if your subclass requires a different search algorithm.

FindAfter

operation FindAfter(target:Object; afterIndex: Unsigned): Unsigned, 
noFail;

Call: sometimes
Override: sometimes

Given an object ID number, FindAfter searches objects with indices greater than afterIndex. FindAfter returns the index of the first matching object. If no object matches, FindAfter returns 0. FindAfter relies upon the subclass's definition of At.

String Matching

FindClosestMatch

operation FindClosestMatch(matchString: String; afterIndex: Unsigned; 
userEntryOrder: Boolean): Unsigned, noFail

Call: rarely
Override: rarely

Call FindClosestMatch to search an object collection, beginning at a specified index position, for the object whose search name most nearly matches a given string. matchString is the string to match; afterIndex is the index position after which to begin the search.

FindClosestMatch searches the entire list, starting at the specified index and wrapping around from the end of the list to the beginning. (If afterIndex is less than zero or greater than the length of the list, the operation fails with the exception cannotFindIndex.) If it finds an object whose search name exactly matches the requested string, FindClosestMatch immediately returns the index of that object. Otherwise, it scans the entire list until it wraps back around to the original starting index, then returns the index of the object whose search name comes closest to the match string.

The string comparison is case-insensitive: that is, corresponding upper- and lowercase letters are considered equivalent. The standard of "closeness" is the difference in ascii value in the first character position in which the two strings differ. For example, aardvark and gnu are a closer match than aardvark and zebra (because a is closer to g than to z), and antelope and antenna are closer than antelope and anteater (because antel is closer to anten than to antea). See the descriptions of Object_GetSearchName and Card_GetSearchName for more information.

FindExactMatch

overrides FindExactMatch(matchString: String; afterIndex: Unsigned; 
userEntryOrder: Boolean):  Unsigned, noFail;

Call: sometimes
Override: rarely

FindExactMatch returns the index of the first item (if any) whose search name matches matchString exactly. See the description of FindClosestMatch above for more information.

SearchForExpansion

overrides SearchForExpansion(prefix: Text; VAR index: Unsigned): Text

Call: sometimes
Override: rarely

You are probably familiar with Magic Cap's word expansion trick. Word expansion (or completion) allows users to expand an abbreviation into a complete word or phrase by tapping on the "expand" button on the keyboard. The SearchForExpansion method is crucial to classes like WordGuessList and AbbreviationList, which implement word expansion.

Given a prefix and an index, SearchForExpansion looks through the collection of objects (starting with the object at index index) for an expansion match. SearchForExpansion returns a text object containing the expanded word (if it found a match), and puts the index of the matching object in *index.