PicLens for Firefox

As time passes, I find myself doing all the browsing with Firefox.

Today I installed version 3 because a colleague pointed me to the PicLens add-on.

This add-on is simple amazing! Another reason to forget about IE. :)

Click to see full size image

Posted 14 August 08 03:19 by hgr | 0 Comments   
Filed under ,
DSL Tools - Reference of Articles

Since I've been doing a number of posts on the DSL Tools, I thought it would be helpful to have a list with all of them:

Posted 15 July 08 06:43 by hgr | 0 Comments   
Filed under ,
DSL Tools #12 - Cascade Deleting Domain Classes

NOTE: The procedure described here is based on the domain model described in this previous post.

---

Now for something completely different.

As I said before instances of Entity, NormalAttribute, and Calculated attribute are created at runtime when the user "imports" them from another model. When this happens, the entities are also associated with a ModuleReference domain class which is basically a pointer to the external model used to import those classes (see this post for the domain model part that defines that relation).

Basically, this ModuleReference/Entity relation allows the user to know that he's using model X (ModuleReference X) and that Entity Y is defined on that model. Visually this is shown by a simple connector:

Click to see full size image

I allow the user to:

  • Delete module references.
  • Delete entities (meaning that they will not be used in the model).

I don't allow the user to:

  • Remove connections between module references and entities. That connection is important to know from where comes a specific entity.

What happens when the user deletes a module reference? The answer is that I need to delete all entities that came from that model (the entities that are connected with the module reference).

This can be achieved when the user "is deleting" the module reference. For that I just need to override OnDeleting in the GraphHasModuleReferences (the embedding relationship that allows the user to place module references in the model) and do the "cascade delete":

   1: using System;
   2:  
   3: namespace MyDSL
   4: {
   5:     /// <summary>
   6:     /// Provides custom behavior for the GraphHasModuleReferences domain class.
   7:     /// </summary>
   8:     public partial class GraphHasModuleReferences
   9:     {
  10:         #region Public Methods
  11:  
  12:         /// <summary>
  13:         /// Called when an instance of this domain class is being deleted.
  14:         /// </summary>
  15:         protected override void OnDeleting()
  16:         {
  17:             // Default behavior
  18:  
  19:             base.OnDeleting();
  20:  
  21:             // If you delete a module reference then all associated entities need to be removed.
  22:  
  23:             if ((this.Graph != null) && (!this.Graph.IsDeleting))
  24:             {
  25:                 // Ask the user
  26:  
  27:                 string question = string.Format(CommonHelper.Culture, Properties.Resources.RES_ConfirmModuleReferenceDelete, this.ModuleReference.Name);
  28:                 if (CommonHelper.ShowQuestion(question))
  29:                 {
  30:                     // Delete all entities
  31:  
  32:                     for (int i = this.ModuleReference.Entities.Count - 1; i >= 0; i--)
  33:                     {
  34:                         Entity entity = this.ModuleReference.Entities[i];
  35:                         entity.Delete();
  36:                     }
  37:                 }
  38:                 else
  39:                 {
  40:                     throw new InvalidOperationException(Properties.Resources.RES_UserCanceledOperation);
  41:                 }
  42:             }
  43:         }
  44:  
  45:         #endregion
  46:     }
  47: }

Simpler that I thought in the first place.

---

NOTE: The procedure described here is based on the domain model described in this previous post.

Posted 15 July 08 06:36 by hgr | 1 Comments   
Filed under ,
DSL Tools #11 - Preventing the User from Deleting Instances of a Domain Class

NOTE: The procedure described here is based on the domain model described in this previous post.

---

The next thing that I wanted to achieve in my DSL was to prevent the user from deleting manually the instances of certain domain classes.

On my example, since all the attributes are created automatically (by code) when each entity is added (also automatically), I don't want the user to be able to delete these attributes. That would create inconsistencies between this model and the model from where the entities were previously read.

Searching the Web for a solution, I came across 2 different articles about this:

Article #1

Article #2

The two articles define different approaches to the problem. The difference is basically the user experience you'll achieve depending when the solution you select.

Personally, I like to give clear feedback about why he can't do something very obvious. Also I tend to prefer the solution that requires less coding. So I went with the second.

1. Preventing the User from Deleting Embedding Relationships

If you look at the EntityHasNormalAttributes this is an embedding relationship that will be presented to the user has a compartment in the entity shape, containing all its normal attributes.

You prevent the user from deleting normal attributes if you prevent him from deleting this kind of relationship.

For that, you create a partial class for the EntityHasNormalAttributes and override the OnDeleting method. When you want to prevent the delete from happening, you need to throw an exception (InvalidOperationException is recommended). Like this:

   1: using System;
   2: using Microsoft.VisualStudio.Modeling;
   3:  
   4: namespace MyDSL
   5: {
   6:     /// <summary>
   7:     /// Provides custom behavior for the EntityHasNormalAttributes domain class.
   8:     /// </summary>
   9:     public partial class EntityHasNormalAttributes
  10:     {
  11:         #region Public Methods
  12:  
  13:         /// <summary>
  14:         /// Called when an instance of this domain class is being deleted.
  15:         /// </summary>
  16:         protected override void OnDeleting()
  17:         {
  18:             // Default behavior
  19:  
  20:             base.OnDeleting();
  21:  
  22:             // You can't delete normal attributes from entities
  23:  
  24:             if ((this.Entity != null) && (!this.Entity.IsDeleting))
  25:             {
  26:                 throw new InvalidOperationException(Properties.Resources.RES_Error_CannotDeleteAttribute);
  27:             }
  28:         }
  29:  
  30:         #endregion
  31:     }
  32: }

Notice that the exception is only thrown if Entity.IsDeleting is false. This means that you only want to prevent the relationship from being removed if you're not deleting the entity itself.

2. Preventing the User from Deleting Reference Relationships

My domain model also includes a reference relationship that I want to create at runtime (when the user "imports" the model) and prevent from being removed latter by him (manually).

Click to see full size image

In this case I want to prevent the connector from being removed. So the implementation is very similar: override IsDeleting in ModuleReferenceUsesEntities:

   1: using System;
   2:  
   3: namespace MyDSL
   4: {
   5:     /// <summary>
   6:     /// Provides custom behavior for the ModuleReferenceUsesEntities domain class.
   7:     /// </summary>
   8:     public partial class ModuleReferenceUsesEntities
   9:     {
  10:         #region Public Methods
  11:  
  12:         /// <summary>
  13:         /// Called when an instance of this domain class is being deleted.
  14:         /// </summary>
  15:         protected override void OnDeleting()
  16:         {
  17:             // Default behavior
  18:  
  19:             base.OnDeleting();
  20:  
  21:             // You cannot delete connections between module references and entities
  22:             // If we're deleting the module reference, then it's ok
  23:             // If we're deleting the entity, then it's ok
  24:  
  25:             if (this.ModuleReference == null || this.ModuleReference.IsDeleting)
  26:             {
  27:                 return;
  28:             }
  29:             if (this.Entity == null || this.Entity.IsDeleting)
  30:             {
  31:                 return;
  32:             }
  33:  
  34:             throw new InvalidOperationException(Properties.Resources.RES_Error_CannotDeleteModuleReferenceEntityConnection);
  35:         }
  36:  
  37:         #endregion
  38:     }
  39: }

Notice now that the exception is only throw when neither the module reference or the entity is being removed. That is the case when the user tries to delete the connector itself between these two domain classes.

---

NOTE: The procedure described here is based on the domain model described in this previous post.

Posted 15 July 08 06:25 by hgr | 2 Comments   
Filed under ,
DSL Tools #10 - Preventing the User from Adding Instances of a Domain Class

In the next few posts about the DSL tools, I'll be using the following domain model:

Click to see full size image

This model is relatively simple:

  • One model can have multiple entities.
  • One entity can have zero or more normal attributes.
  • One entity can have zero or more calculated attributes.

The interesting thing in this model is that the user is not supposed to create entities or attributes (either normal or calculated).

These domain classes are created at runtime (by code) through a menu operation where the user selects a different model (designed with a different DSL) and the domain classes are read from that model and put in the model the user is working on (so he can change some properties relevant to this model).

Basically this a major requirement of the DSL I've been working on, and that made me find solutions for the problems I'll present in this and in the next few posts.

So, first, if the domain classes are created at runtime, how do I go and prevent the user from adding instances of these classes to the domain model (for example, through the model explorer)?

It's quite simple:

1. Prevent the User from Adding New Entities

Having all the extensibility points in place (see previous posts in this series), all I need to do is to create a partial class for the "container domain class" of Entity. That's ReportingModelRoot (see the GraphHasEntities relation).

On that class, I override the CanMerge method and check if I'm merging with an Entity domain class. If so, the method just has to return false.

   1: using System;
   2: using Microsoft.VisualStudio.Modeling;
   3:  
   4: namespace MyDSL
   5: {
   6:     /// <summary>
   7:     /// Provides custom behavior for the ModuleReferenceUsesEntities domain class.
   8:     /// </summary>
   9:     public partial class ReportingModelRoot
  10:     {
  11:         #region Public Methods
  12:  
  13:         /// <summary>
  14:         /// Returns a value indicating whether the source element represented by the
  15:         /// specified root ProtoElement can be added to this element.
  16:         /// </summary>
  17:         /// <param name="rootElement">The root ProtoElement representing a source element.  This can be null,
  18:         /// in which case the ElementGroupPrototype does not contain an ProtoElements
  19:         /// and the code should inspect the ElementGroupPrototype context information.</param>
  20:         /// <param name="elementGroupPrototype">The ElementGroupPrototype that contains the root ProtoElement.</param>
  21:         /// <returns>
  22:         /// true if the source element represented by the ProtoElement can be added to this target element.
  23:         /// </returns>
  24:         protected override bool CanMerge(ProtoElementBase rootElement, ElementGroupPrototype elementGroupPrototype)
  25:         {
  26:             // The user cannot add entities (they're added by code)
  27:  
  28:             if (rootElement.DomainClassId.Equals(Entity.DomainClassId))
  29:             {
  30:                 return false;
  31:             }
  32:  
  33:             // Default behavior
  34:  
  35:             return base.CanMerge(rootElement, elementGroupPrototype);
  36:         }
  37:  
  38:         #endregion
  39:     }
  40: }

2. Prevent the User from Adding Attributes to an Entity

This is basically the same as in the previous case. The difference is just that the "container class" is now Entity:

   1: using System;
   2: using Microsoft.VisualStudio.Modeling;
   3:  
   4: namespace MyDSL
   5: {
   6:     /// <summary>
   7:     /// Provides custom behavior for the Entity domain class.
   8:     /// </summary>
   9:     public partial class Entity
  10:     {
  11:         #region Public Methods
  12:  
  13:         /// <summary>
  14:         /// Determines whether this instance can merge the specified root element.
  15:         /// </summary>
  16:         /// <param name="rootElement">The root element.</param>
  17:         /// <param name="elementGroupPrototype">The element group prototype.</param>
  18:         /// <returns>
  19:         ///     <c>True</c> if this instance can merge the specified root element; otherwise, <c>false</c>.
  20:         /// </returns>
  21:         protected override bool CanMerge(ProtoElementBase rootElement, ElementGroupPrototype elementGroupPrototype)
  22:         {
  23:             // The user cannot add normal attributes (they're added by code)
  24:             // The user cannot add calculated attributes (they're added by code)
  25:  
  26:             if (rootElement.DomainClassId.Equals(NormalAttribute.DomainClassId) || rootElement.DomainClassId.Equals(CalculatedAttribute.DomainClassId))
  27:             {
  28:                 return false;
  29:             }
  30:  
  31:             // Default behavior
  32:  
  33:             return base.CanMerge(rootElement, elementGroupPrototype);
  34:         }
  35:  
  36:         #endregion
  37:     }
  38: }

One important thing to notice here is that this behavior is just UI related. It disables are commands in the designer (and the model explorer) that would allow the user to add instances of these domain classes. It doesn't prevent code from adding instances of these same classes. But that was just what I needed (since I have a procedure that does exactly that).

Posted 15 July 08 06:08 by hgr | 3 Comments   
Filed under ,
DSL Tools #9 - Adding Context Menus to a Designer

Again working on a new DSL, I was looking for a simple way of adding menus to the my DSL's main designer context menu.

In the end I found this great article by Sebastian Talamoni that explains it very well.

Here it is:

Adding a Menu to a DSL

Share this post: email it! | bookmark it! | digg it! | reddit! |