Assigning a Custom Access Method (CAM) to an Attribute

Introduction

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:
  • You should never call a CAM directly; instead, rely on the _getAttributeValue or _setAttributeValue methods to call it automatically.
  • CAMs in SAS classes are protected methods to help enforce indirect execution via the _getAttributeValue and _setAttributeValue methods. Component developers are encouraged to create all CAMs as Scope=Protected methods to encourage the use of dot notation and to inhibit the direct calling of CAMs by developers who use the component.
  • A CAM always has a single signature and should not be overloaded. The CAM signature contains a single argument that is the same type as its associated attribute. A CAM always returns a numeric value as a return code that indicates success or failure. For example, if a CAM is specified for a character variable, its signature is (C)N.
  • The CAM must be defined for the object that contains the attribute that calls it. In other words, if object.myAttribute calls a CAM named setcamMyAttribute, then setcamMyAttribute must be a method on object.
  • CAMs may be added only to New or Overridden attributes.
  • Default names for custom access methods in SAS classes follow this format:
    • _setcamAttributeName
    • _getcamAttributeName
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 Source window and the New Method dialog box, and 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:
  • CAM names can be up to 256 characters long. However, keep in mind that the USECLASS command can only accommodate method names up to 32 characters long. For easy maintenance, the method name is used as the SCL label in the METHOD statement.
  • CAM names may contain only alphanumeric characters and the underscore character. The Class Editor does not support modifications to methods whose names contain special characters. Be sure to remove special characters from method names when converting legacy classes.
  • In SAS classes, CAMs and other methods are named with a leading underscore. Users should not use leading underscores in names of methods or CAMs.
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:
  • setcamAttributeName
  • getcamAttributeName
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.