With the addition of Association Rules that introduce dependencies beyond simple Persistent Object type, I wanted to describe what these new dependancies are and how they are enforced.
Exclusion and Prerequisites
These concepts were introduced in Supporting Exclusion and Required Constraints in Attribute Resources. Basically an attribute (A) may not be associated to a Persistent Object if:
- The object has an attribute already associated with it that excludes A (note that this rule is symmetric : if A excludes B then B also excludes A)
- The object does not have an attribute associated with it that is a prerequisite for A to also be associated with the object (Note that this rule cannot be symmetric)
The Attribute::associate(…) and Attribute::disassociate(…) methods enforces the above rules so neither in code or in the GUI can these constraints be violated.
Association Rules with Property Constraints
The ability to include constraints based on Property values have recently been supported in Association Rules. For example you can now state attributes of type Foo can be associated to model faces that have a string property “Type” whose value is “Bar”.
The Attribute::associate(…) methods can enforce these constraints. Note that in this case the Attribute::disassociate(…) can not invalidate this constraint (mainly because the prerequisite mechanism does not depend on property constraints.
Property Modification
Everything works fine assuming that the properties associated with Persistent Objects are constant. This is not the case if an object’s properties are modified. Consider the above example where we have an model face F1 with a property Type=“Bar” and an Attribute A1 of type Foo. Based on Foo’s association requirement, F1 can be associated with A1. Now if sometime afterwards F1 loses its Type property (or perhaps it is changed to “Wall”), it should no longer be associated to A1, but A1 does not know of the property change.
Possible Solutions
Using Resource and Operation Management with Observers
In this case (theoretically), an observer could listen for property changes and if one occurs, it could look at all of the attribute resources and determine if any associations have become invalid and remove them . There are a couple pitfalls with this approach:
- Since the attribute resource may be changed as the result of an observer being fired, the operation that caused the property to change would not correctly report that both the persistent object and the attribute were modified. This would be cause issues with a GUI-based application like ModelBuilder
- It would be a pain to debug (most observer-based interactions can be hard to trace)
- Would be difficult to do in a multi-threaded/parallel environment - consider the how the setProperty operator works. It’s prerequisite is that the resource (in this case the model resource (MR) owning F1) needs to be modifiable. It doesn’t stipulate anything about the attribute resource (AR) owning A1. So consider the scenario where MR is not being used by any currently running operation but AR is. So the property operation begins to run and suddenly rules into a problem when the Observer tries to change AR. The only recourse would be to have the property operation launch an attribute validate operation that would execute sometime later.
Using Resource and Operation Management without Observers
In this case the setProperty operation would directly do the validation itself by examining the resources in the resource manager and looking for attribute resources that are associated with the current resource being modified (in this case MR).
The benefits of this method are
- Simpler to debug - no observations going on
- Ability for the operation to also return the modified attribute resources
- Ability to provide an option to not change a property if it would cause invalidations.
Pitfalls with this approach:
- Possible multi-threading issues as in the previous case though it might be possible for the operation to request all of the resources currently depending on the property to be modifiable.
Not Using Any Management
We could provide a mechanism that would associate a list of resources (via weak pointers) that has dependancies on a specific property and when that property changes those resources are then told to update.
The benefits of this method are
- Ability for the operation to also return the modified attribute resources
- Ability to provide an option to not change a property if it would cause invalidations.
- Does not depend on any management.
Pitfalls with this approach:
- Possible multi-threading issues as in the previous case though it might be possible for the operation to request all of the resources currently depending on the property to be modifiable.
- Would introduce yet another observation like system
External Validation Method/Operation
In this case the association are left invalid and there is a method that would reexamine the attribute resources. You could provide two options:
- One that would check the entire resource
- One that would take in the object whose property was changed and do a more focused validation.
The main setback with this approach are:
- There would be no possibility to test to see if removing a property would cause an invalidatey.
- Things are left in an invalid state and the user/developer has to remember to revalidate explicitly.
Reference Items
Currently reference items do not have exclusion or prerequisite constraints though I’m the process of implementing the ability to prevent 2 different reference items from being assigned to the same persistent object. We do have the property modification issue since you can use properties in the reference item’s assignment rule.
In addition, there are new “constraints” that have been introduced in the Qt widgets for reference items such as a reference item should only be assigned objects that are currently associated with the item’s owning attribute. Note that this “rule” is only available in the GUI system and would not be validated using anything in SMTK Core.