space
Previous Page | Next Page

Managing Attributes

Assigning a Custom Access Method (CAM) to an Attribute

A custom access method (CAM) is a SAS/AF software method that is associated with an attribute. The CAM is automatically executed to perform additional processing when the attribute's value is queried (with either dot notation or a direct _getAttributeValue call) or set (with either dot notation or a direct _setAttributeValue call). You can use the Class Editor to assign a CAM to a class attribute.

CAMs operate just like any other method, with a few special considerations:

See Attribute Values and Dot Notation for more information about CAMs and about the flow of control for the _setAttributeValue and _getAttributeValue methods.

For example, consider an object that has an attribute whose value is set to a SAS catalog. You can add a CAM to that attribute to determine whether the value is an existing SAS catalog as follows:

  1. Create a new class whose parent is sashelp.fsp.object.class and whose description is Document Object.

  2. Save the class as sasuser.test.document.class.

  3. In the Attributes node of the Class Editor, select New Attribute from the pop-up menu and add a character attribute named catalogToRead.

  4. In the Set CAM cell for the catalogToRead attribute, select the setcamCatalogToRead method from the drop-down list. When you are prompted to add the method, click Yes.

  5. In the New Method dialog box, click Source and add the following code to the sasuser.test.document.scl entry:

    USECLASS sasuser.test.document.class;
    /* ...other methods can be defined here... */
    
    setcamCatalogToRead: Protected method
            name:input:char(83)
            return=num;
       if name eq " then return(0);
    
       if cexist(name) eq 0 then
          do;
             /* set the errorMessage attribute */
             errorMessage = 'ERROR: Catalog does not exist.';
             put errorMessage;
             return(1);
          end;
       else return(0);
    endmethod;
    enduseclass;

  6. Compile and save the SCL, close the SCL source window and the New Method dialog box, then close the Class Editor.

When the attribute is set via SCL (for example, document.catalogToRead = 'sasuser.myclasses'; ), the setCAM is called, verifying that the catalog is a valid SAS catalog. If the catalog that is specified is an invalid SAS catalog name or does not exist, an error message is generated and the setCAM program halts.

The same CAM could be expanded to do more than simple validity checking. For example, you could add processing to the CAM to read information from the selected catalog and to store that information in a list attribute named contents when the catalogToRead attribute is set:

USECLASS sasuser.test.document.class;
/* ...other methods can be defined here... */

setcamCatalogToRead: Protected method
        name:input:char(83)
        return=num;

   dcl num rc;
   if name eq " then return(0);

   if cexist(name) eq 0 then
      do;
         /* set the errorMessage attribute */
         errorMessage = 'ERROR: Catalog does not exist.';
         put errorMessage;
         return(1);
      end;
   else do;
      /* use the catalog entry list model to read the catalog */
      /* and return the four-level name of each entry in it   */
      dcl sashelp.classes.catalogentrylist_c.class catobj;
      catobj = _new_ sashelp.classes.catalogentrylist_c();
      catobj.catalog=name;
      rc=clearlist(contents);
      contents = copylist(catobj.items);
      catobj._term();
      return(0);
   end;
endmethod;
enduseclass;


CAM Naming Conventions

CAM naming follows method-naming rules and conventions in several ways:

This format makes it easy to differentiate between CAMs and regular methods. For example, the method _setBorderStyle is easily distinguished from the associated CAM _setcamBorderStyle. Developers should follow the same format, without the leading underscore:

When you add a CAM to an attribute, the Class Editor automatically provides the CAM name in the format of setcamAttributeName or getcamAttributeName.

You should adhere to these naming formats to eliminate confusion between CAMs and regular methods. Doing so will ensure that CAMs are not accessed directly.


Avoiding Unnecessary CAM Execution

The ease with which dot notation enables you to get and set attribute values may lead you to write code similar to the following:

obj.color = obj2.text;
/* Assuming obj2.text contains a valid color value. */

This is perfectly valid code. However, setCAMs and getCAMs may be executing in the background each time code like this is run. Of particular concern is a getCAM that is associated with the right-hand side of an assignment.

Use caution when repeatedly evaluating a right-hand value, thus repeatedly running _getAttributeValue and any associated getCAM. The following code illustrates the potential inefficiency of repeatedly evaluating the right-hand side of an assignment:

do x = 1 to listlen(someList);
  /* Assigns the values and runs obj2._getcamAttribute2 each time. */
  obj.attribute[x] = obj2.attribute2
end;

Even if no getCAM is executing each time the value of attribute2 is queried, a better way to implement such code would be to assign the value to a third variable and to use the variable in the loop, avoiding any getCAM code that runs for each iteration of the loop:

/* Assigns the value and runs _getcamAttribute2 once. */
dcl newVariable = obj2.attribute2;
do x = 1 to listlen(someList);
  /* Assigns the values without running _getcamAttribute2. */
  obj.attribute[x] = newVariable;
end;


Avoiding CAM Recursion

Do not set other attribute values from within getCAM code. It is possible to inadvertently create an infinite loop when setting the value of another attribute while inside the execution of a getCAM method. For example:

  1. The getCAMX method is called when the value of the X attribute is queried.

  2. In the course of its code, getCAMX sets the value of the Y attribute.

  3. The Y attribute has a setCAM that queries the value of the X attribute, which starts the process again at step 1.

space
Previous Page | Next Page | Top of Page