Donn Shull continues his series of articles on Matlab’s undocumented UDD mechanism. Today, Donn explains how to use and customize UDD properties.
Properties meta-data
The UDD system is a class system. UDD packages, classes, events, and properties are all classes. In this section we will take a closer look at property classes.
As we have already shown, properties are added to a UDD class by adding schema.prop
calls to the schema.m class definition file. What this really means is that each property of a UDD class is itself a class object (schema.prop
) with its own properties and methods. The methods of schema.prop
are loadobj() and saveobj(), which are used to serialize objects of this class (i.e., storing them in a file or sending them elsewhere).
It is schema.prop
‘s properties (so-called meta-properties) that interest us most:
Property | Data Type | Description |
---|---|---|
AccessFlags | Matlab structure | Controls which objects can access (read/modify) the property |
CaseSensitive | on/off | Determines if the exact case is required to access the property (i.e., can we use ‘casesensitive’ instead of ‘CaseSensitive’) |
DataType | string | The underlying object’s property data type, set by the constructor |
Description | string | This can hold a description of the property (normally empty) |
FactoryValue | As specified by DataType | This is used to provide an initial or default property value |
GetFunction | Function handle | A function handle that is called whenever the property value is read |
Name | string | The name of the property, also set by the constructor |
SetFunction | Function handle | A function handle that is called whenever the properties value is changed |
Visible | on/off | Determines if a property will be displayed by the get method for a UDD object |
We can manipulate the values of these meta-properties to control various aspects of our property:
% Create instance of simple.object >> a = simple.object('a'); % Find the Value property and list its meta-properties % We can manipulate these meta-properties within limits >> a.findprop('Value').get Name: 'Value' Description: '' DataType: 'single' FactoryValue: 7.3891 AccessFlags: [1x1 struct] Visible: 'on' GetFunction: [] SetFunction: [] >> prop.Visible = 'off'; % i.e. hidden property (see below) >> prop.AccessFlags.PublicSet = 'off'; % i.e. read-only >> prop.AccessFlags.PublicGet = 'on'; % Find the DataType meta-property of the Value property % This meta-property and all other schema.prop base class properties are fixed >> a.findprop('Value').findprop('DataType').get Name: 'DataType' Description: '' DataType: 'string' FactoryValue: '' ... |
Adding properties to existing objects in run-time
schema.prop
is a very useful tool – it can be used to add new properties to existing object handles, even after these objects have been created. For example, let’s add a new property (MyFavoriteBlog) to a standard figure handle:
>> p=schema.prop(handle(gcf), 'MyFavoriteBlog','string') p = schema.prop >> set(gcf,'MyFavoriteBlog','UndocumentedMatlab.com') >> get(gcf,'MyFavoriteBlog') ans = UndocumentedMatlab.com |
Using this simple mechanism, we can add meaningful typed user data to any handle object. A similar functionality can be achieved via the setappdata/getappdata functions. However, the property-based approach above is much “cleaner” and more powerful, since we have built-in type checks, property-change event listeners and other useful goodies.
Property data types
In the article on creating UDD objects we saw that the Name and DataType meta-properties are set by the schema.prop
constructor. Name must be a valid Matlab variable name (see isvarname).
DataType is more interesting: There are two equivalent universal data types, 'mxArray'
, and 'MATLAB array'
. With either of these two data types a property can be set to a any Matlab type. If we use a more specific data type (e.g., ‘string’, ‘double’ or ‘handle’), Matlab automatically ensures the type validity whenever the property value is modified. In our simple.object
we use ‘double’ and ‘string’. You can experiment with these and see that the Value property will only allow scalar numeric values and the Name property will only allow character values:
>> set(obj, 'Value', 'abcd') ??? Parameter must be scalar. >> obj.Value='abcd' ??? Parameter must be scalar. >> obj.Name=123 ??? Parameter must be a string. |
The following table lists the basic UDD data types:
Category | Data Type |
---|---|
Universal | MATLAB array, mxArray |
Numeric Scalars | bool, byte, short, int, long, float, double |
Numeric Vectors | Nints, NReals |
Specialized Numeric | color, point, real point, real point3, rect, real rect |
Enumeration | on/off |
Strings | char, string, NStrings, string vector |
Handle | handle, handle vector, MATLAB callback, GetFunction, SetFunction |
Java | Any java class recognized by Matlab |
User-defined data types
While this is an extensive list, there are some obvious types missing. For example there are no unsigned integer types. To handle this UDD provides two facilities for creating your own data types. One is the schema.EnumType
. As you can see, Matlab has had a form of enumerations for a really long time not just the last few releases. The other facility is schema.UserType
.
With these two classes you can create any specialized data type you need. One word of caution: once you have created a new UDD data type it exists for the duration of that Matlab session. There is no equivalent of the clear classes mechanism for removing a data type. In addition once a new data type has been defined it cannot be redefined until Matlab is restarted.
Let’s use a problem discussed in the CSSM forum as example. The essence of the problem is the need to flag a graphic line object as either editable or not. The proposed proposed is to add a new Editable property to an existing line handle. We will use schema.EnumType
to create a new type named 'yes/no'
so that the new property could accept only ‘yes’ and ‘no’ values:
function tline = taggedLine(varargin) %TAGGEDLINE create a line with Editable property % % TLINE = TAGGEDLINE(VARARGIN) create a new handle graphics line % and add 'Ediatable' property to line. Default property value is 'yes'. % % INPUTS: % VARARGIN : property value pairs to pass to line % % OUTPUTS: % TLINE : hg line object with Editable property % If undefined define yes/no datatype</font> if isempty(findtype('yes/no')) schema.EnumType('yes/no', {'yes', 'no'}); end tline = line(varargin{:}); schema.prop(tline, 'Editable', 'yes/no'); end |
It is necessary to test for the existence of a type before defining it, since trying to redefine a type will generate an error.
We can use this new taggedLine() function to create new line objects with the additional Editable property. Instead of adding a new property to the line class we could have defined a new class as a subclass of line:
function schema() %SCHEMA hg package definition function schema.package('hg'); end |
We create our class definition as a subclass of the handle graphics line class:
function schema() %SCHEMA hg.taggedline class definition function % package definition superPackage = findpackage('hg'); pkg = findpackage('hg'); % class definition c = schema.class(pkg, 'taggedline', findclass(superPackage, 'line')); if isempty(findtype('yes/no')) schema.EnumType('yes/no', {'yes', 'no'}); end % add properties to class schema.prop(c, 'Editable', 'yes/no'); end |
And our constructor is:
function self = taggedline %OBJECT constructor for the simple.object class self = hg.taggedline; end |
Here we have placed the schema.EnumType
definition in the class definition function. It is usually better to place type definition code in the package definition function, which is executed prior to any of the package classes and available in all classes. But in this particular case we are extending the built-in hg
package and because hg
is already defined internally, our package definition code is never actually executed.
The schema.UserType
has the following constructor syntax:
schema.UserType('newTypeName', 'baseTypeName', typeCheckFunctionHandle) |
For example, to create a user-defined type for unsigned eight-bit integers we might use the following code:
schema.UserType('uint8', 'short', @check_uint8) function check_uint8(value) %CHECK_UINT8 Check function for uint8 type definition if isempty(value) || (value < 0) || (value > 255) error('Value must be a scalar between 0 and 255'); end end |
Hidden properties
Visible is an 'on/off'
meta-property that controls whether or not a property is displayed when using the get function without specifying the property name. Using this mechanism we can easily detect hidden undocumented properties. For example:
>> for prop = get(classhandle(handle(gcf)),'Properties')' if strcmpi(prop.Visible,'off'), disp(prop.Name); end end BackingStore CurrentKey CurrentModifier Dithermap DithermapMode DoubleBuffer FixedColors HelpFcn HelpTopicMap MinColormap JavaFrame OuterPosition ActivePositionProperty PrintTemplate ExportTemplate WaitStatus UseHG2 PixelBounds HelpTopicKey Serializable ApplicationData Behavior XLimInclude YLimInclude ZLimInclude CLimInclude ALimInclude IncludeRenderer |
Note that hidden properties such as these are accessible via get/set just as any other property. It is simply that they are not displayed when you run get(gcf) or set(gcf) – we need to specifically refer to them by their name: get(gcf,’UseHG2′). Many other similar hidden properties are described in this website.
You may have noticed that the CaseSensitive meta-property did not show up above when we used get to show the meta-properties of our Value property. This is because CaseSensitive has its own Visible meta-property set to 'off'
(i.e., hidden).
Additional meta-properties
FactoryValue is used to set an initial value for the property whenever a new simple.object
instance is created.
GetFunction and SetFunction were described in last week’s article, Creating a UDD Hierarchy.
AccessFlags is a Matlab structure of 'on/off'
fields that control what happens when the property is accessed:
Fieldname | Description |
---|---|
PublicSet | Controls setting the property from code external to the class |
PublicGet | Controls reading the property value from code external to the class |
PrivateSet | Controls setting the property from internal class methods |
PrivateGet | Controls reading the property value from internal class methods |
Init | Controls initializing the property using FactoryValue in the class definition file |
Default | ??? (Undocumented, no examples exist) |
Reset | Controls initializing the property using FactoryValue when executing the built-in reset function |
Serialize | Controls whether this object can be serialized |
Copy | Controls whether to pass the property’s current value to a copy |
Listener | Controls whether property access events are generated or not |
AbortSet | Controls whether property set events are generated when a set operation will not change the property’s value |
The CaseSensitive meta-property has AccessFlag.Init = 'off'
. This means that properties added to a class definition file are always case insensitive.
Another interesting fact is that properties can be abbreviated as long as the abbreviation is unambiguous. Using our simple.object
as an example:
>> a = simple.object('a'); >> a.n % abbreviation of Name ans = a >> a.v % abbreviation of Value ans = 0.0000 |
It is considered poor programming practice to use either improperly cased, or abbreviated names when writing code. It is difficult to read, debug and maintain. But show me a Matlab programmer who has never abbreviated Position as ‘pos’…
Note: for completeness’ sake, read yesterday’s post on MCOS properties on Loren’s blog, written by Dave Foti, author of the original UDD code. Dave’s post describes the fully-documented MCOS mechanism, which is newer than the undocumented UDD mechanism described here. As mentioned earlier, whereas UDD existed (and still exists) in all Matlab 7 releases, MCOS is only available since R2008a. UDD and MCOS co-exist in Matlab since R2008a. MCOS has definite advantages over UDD, but cannot be used on pre-2008 Matlab releases. Different development and deployment requirements may dictate using either UDD or MCOS (or both). Another pre-R2008a alternative is to use Matlab’s obsolete yet documented class system.
In the next installment of this series we will take a look at UDD events and listeners.
Hi Donn, thanks for your very interesting revelations on UDD so far! I see one additional advantage of UDD classes compared to MCOS, and that is the support for the MATLAB property inspector called by obj.inspect. The inspector automatically recognizes the datatypes used in the schema.m definitions and adds corresponding property editors and renderers. Unfortunately, this seems impossible with standard MCOS classes so far.
In addition, the inspector allows us to group the properties in categories by implementing a class function info = getInspectorGrouping(h,arg). This function is very easy to define as well, since it should only return a cell array of categories and properties:
Donn, I am very glad to finally get some insight into UDD classes in Matlab. Your articles so far have been fantastic.
I may be jumping the gun just a little bit with my question, but I was attempting to create a class that subclasses hg.surface. Ideally, I would like to hide the XData property, however, if you attempt to modify the property in the schema.m file, it doesn’t allow changing the AccessFlags (since the superclass is already created). However, using your technique of overriding the property, I was able to generate a new XData property that is no longer visible. The question is, how does one go about accessing the XData property of a super class, since you still have to be able to manipulate the XData internally.
Maybe this will be covered when you discuss the java behind UDD, but I can’t come up with a good way to do this.
@Jonathan – To the best of my knowledge if you override a UDD property you will loose access to the SuperClass version (or if you prefer the superclass will loose access to the subclass version). If the valus of XData is fixed at the time of your subclass version you can pass it to the superclass in the constructor but you wont be able to change it afterwards.
Donn
Donn, thanks for all of the great info in these articles.
Regarding property AccessFlags, do you have any insight into using the ‘PublicSet’ and ‘PublicGet’ meta-properties to create private (internal) properties? From what I’ve tried so far, setting these to ‘off’ prevents public access, which is the intention. It does not affect access from any methods in the @object folder, except that the constructor method does not appear to have access. Unfortunately, it also seems to prevent access from any sub-function of any class method, or from any set/get functions defined in schema.m.
I’ve seen one way around this, in the scribe.colorbar class, by defining ‘getprivateprop’ and ‘setprivateprop’ methods and then using these throughout the rest of the code. This is clumsy to implement, and negates the whole idea of private access, since anyone can just call:
to get access to the supposedly private property.
Am I missing something? Is there a way to at least make the set/getprivateprop methods themselves private?
A quick followup: The constructor method does have access to class properties regardless of the ‘PublicSet’ and ‘PublicGet’ access flags. Sorry for the misinformation.
@John – Thanks for your kind words. You can place a private directory underneath your class directory. methods in that directory will not be available publicly but your regular methods will have access to them. I would guess that the method subfunction anomaly you have found was not intended and scribe.colorbar was a workaround. Try the private subfolder and les us know if it works for you.
Donn
[…] The properties do have a settable CaseSensitive meta-property that we can set to 'on/off' (default='off') on a property-by-property basis. […]
[…] In UDD classes, we can do this easily by setting the property’s DataType meta-property. An easy way to do this is by setting the second argument of the schema.prop function. A detailed explanation was provided here. […]