DSL Tools #3 - Dynamic Default Values for Domain Properties
The sample domain model I've been working with includes an abstract class called NamedElement.
This is basically a construction that guarantees that all other significant domain classes will include a domain property called Name.
I wanted each class the user adds to the model to have a different default value for this name property (reducing his effort to define valid names). But I also want to ensure that each class has a different name (rule enforced elsewhere).
So the objective here was to dynamically generate default names for each class depending on the classes already placed in the model. For example, if the user adds 3 activities and then 3 gateways, these new elements should have the following names: Activity, Activity1, Activity2, Gateway, Gateway1, and Gateway2.
It looks simpler that it is in reality. The problem was finding which "event" would do the job better.
I'm not really sure if this is the best solution for this requirement, but I ended up overriding the MergeRelate method of the ProcessGraph domain class.
1. Prepare the partial class for ProcessGraph
This previous post describes the steps required.
2. Override MergeRelate in that class and setup the properties accordingly
MergeRelate is called whenever a given element is related (merged) (or added in this case) with the ProcessGraph domain class.
This is called immediately after the user drops the new element on the designer.
So this a great place to initialize the new element's properties.
/// <summary>
/// Creates a relationship between this target element and the
/// specified source element.
/// </summary>
/// <param name="sourceElement">The element to be related to this
/// model element.</param>
/// <param name="elementGroup">The group of source elements that
/// have been added back into the target store.</param>
protected override void MergeRelate(ModelElement sourceElement,
ElementGroup elementGroup)
{
// Adding default values
Start startElement = (sourceElement as Start);
if (startElement != null)
{
startElement.Name = Properties.Resources.Start;
startElement.Description = Properties.Resources.StartDesc;
}
End endElement = (sourceElement as End);
if (endElement != null)
{
endElement.Name =
GeneralHelper.GenerateElementName<End>
(this, Properties.Resources.End);
endElement.Description = Properties.Resources.EndDesc;
}
ActivityElement activityElement =
(sourceElement as ActivityElement);
if (activityElement != null)
{
activityElement.Name =
GeneralHelper.GenerateElementName<ActivityElement>(this,
Properties.Resources.Activity);
}
GatewayElement gatewayElement = (sourceElement as GatewayElement);
if (gatewayElement != null)
{
gatewayElement.Name =
GeneralHelper.GenerateElementName<GatewayElement>(this,
Properties.Resources.Gateway);
}
Contract contractElement = (sourceElement as Contract);
if (contractElement != null)
{
contractElement.Name =
GeneralHelper.GenerateElementName<Contract>(this,
Properties.Resources.Contract);
}
DataObject dataObjectElement = (sourceElement as DataObject);
if (dataObjectElement != null)
{
dataObjectElement.Name =
GeneralHelper.GenerateElementName<DataObject>(this,
Properties.Resources.DataObject);
}
// Default behavior
base.MergeRelate(sourceElement, elementGroup);
}
GenerateElementName is a generic method that does all the magic of generating a valid name for each new element. This involves basically inspecting the current model elements, finding the last "valid name" (e.g. Activity3) and generating the new one (e.g. Activity4).