Connecting the application logic with the user interface in Uiml.net

Let me warn you first, this will be a long blog post.

Creating a cross-platform user interface with Uiml.net is one thing, actually getting it to do something useful by interacting with the application logic is another issue.

Currently, the logic element in UIML only specifies the class of object associated with a call element. This implies that the objects are stateless or static and that the UIML rendering engine will instantiate the object each time a call is made.

Here’s a simple example:

<call name="Console.println">
  <param>Uiml says hello!</param>
</call>
...
<logic>
  <d-component id="Console" maps-to="System.Console">
    <d-method id="println" returns-value="void" maps-to="WriteLine">
      <d-param id="message" type="System.String"/>
    </d-method>
  </d-component>
</logic>

Of course, in this example that’s fine, because the Console class only has static methods. It doesn’t have a state we wish to monitor in the user interface. When we want to interact with a database connection for example, things become more difficult.

One way to solve the problem would be to develop a mechanism to specify which instance of an object will be referenced by a specific call. This would allow multiple instances of the same external object to exist within the system and be referenced separately.

However, in our opinion this would only complicate the application logic binding mechanism and the UIML language itself.

Uiml.net provides an API that allows multiple object instances to be connected to the same user interface. When a call to the application logic is made, we first check if a call can be invoked on one of the connected object instances. When that fails, we’ll try to invoke it statically liked we did before.

If we call a certain method, all connected objects will invoke this method. There’s a catch though. When the call has to return a value, which return-value do we use? We suggest to use the return-value of the object that was connected last. Considering the fact that each invocation may have an effect on the other connected objects, this is the most sensible thing to do.

This API has been supported in Uiml.net for a while now (in fact, I completed the implementation during my internship in July 2004, building upon Kris ideas). Unfortunately we hadn’t communicated it very well. Hopefully this blog post is a start. I will probably also provide some example in the future.

Following is a simple example with a modification of the UIML calculator. When you hit the = button, the contents of the output entry will be sent to the Print method of an instance of the CalcInspector class. Depending on a member _times, this value is written out _times times:

<behavior>
  ...
  <rule>
    <condition>
      <event part-name="bsol" class="ButtonPressed"/>
    </condition>
    <action>
      <call name="CalcInspector.Print">
        <param>
          <property part-name="output" name="text"/>
        </param>
      </call>
    </action>
  </rule>
  ...
</behavior>
...
<logic>
  <d-component id="CalcInspector" maps-to="CalcInspector">
     <d-method id="Print" maps-to="Print">
      <d-param id="output" type="System.String"/>
    </d-method>
  </d-component>
</logic>
...
public class CalcInspector 
{
  int _times;
  public CalcInspector(int times)
  {
    _times = times;
  }
 
  /// <summary>
  /// Prints the output _times times.
  /// </summary>
  public void Print(string output)
  {
    for(int i = 0; i < _times; i++)
    {
      Console.WriteLine(output);
    }
    Console.WriteLine("...Done!");
  }
}

The relevant code when the interface is rendered:

CalcInspector small = new CalcInspector(2);
CalcInspector large = new CalcInspector(5);
 
uimlDoc.Connect(small);
uimlDoc.Connect(large);

As you can see, we created two CalcInspector instances: small and large. One will write the output two times while the other will print it five times. Following are two screenshots on a PDA, one just before pressing the = and one right after:

[img:113715576,medium]

[img:113715577,medium]

Since we connected small first, small‘s Write method will be executed first.

We also implemented a connection to other way around. It allows the application logic to subscribe to certain events from the UIML user interface. This was originally inspired by the connection mechanism of Glade. We wanted an easy and convenient way to connect with the rendered user interface. Uiml.net takes advantage of the .NET framework‘s basic support for aspect-oriented programming to realize this. Methods, classes, variables and the like can be augmented with so-called attributes. These attributes can then be queried using reflection. The instances are again connected in the same way as in the previous example.

The attribute UimlEventHandler specifies that a method can deal with user interface events. Parameters can be passed with the attribute that specify the information the method wants to receive together with the event.

[UimlEventHandler("ButtonPressed")]
public void OnButtonPressed(Part sender, UimlEventArgs a)
{
  Console.WriteLine("Received event from a part:");
  Console.WriteLine("\tApparantly it's the \"{0}\" part",
                    sender.Identifier);
  Console.WriteLine("\tIt's class is <{0}>", sender.Class);
  Console.WriteLine("\tThe UI object is of the type [{0}]",
                    sender.UiObject.GetType());
  }
}

In this example, the method would receive every ButtonPressed event. It then writes out from which part it got the event. It writes out its class, and the class of the actual underlying user interface object. So in fact the application logic queries a piece of the part tree, in which a lot of information is present.

But we could go further:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[UimlEventHandler("ButtonPressed", "fr", "l_recent")]
public void OnButtonPressed(Part sender, UimlEventArgs a)
{
  Part fr = a.GetPart("fr");
 
  Console.WriteLine("\nPart \"{0}\" of type <{1}> has" + 
                    "the following subparts:", fr.Identifier, fr.Class);
 
  System.Collections.IEnumerator e = fr.GetSubParts();
  while(e.MoveNext())
  {
    Part sub = (Part)e.Current;
    Console.WriteLine("\t\"{0}\" of type <{1}>;",
                      sub.Identifier, sub.Class);
  }
 
  Part l_recent = a.GetPart("l_recent");
  string propertyName = "text";
  Console.WriteLine("The [{0}] property of part \"{1}\"" +
                    " is: {2}", propertyName, l_recent.Identifier, 
                    l_recent.GetProperty(propertyName));
}

Since this is a lot of code, we’ll go over it gradually.

1
[UimlEventHandler("ButtonPressed", "fr", "l_recent")]

The first line specifies that we are interested in ButtonPressed events. We also request the parts fr and l_recent from the user interface, so we can examine them. These parts will be collected in the UimlEventArgs parameter.

4
Part fr = a.GetPart("fr");

This line gets the fr part from the event arguments.

The next few lines go through all of fr‘s children, and writes them to the console:

9
10
11
12
13
14
15
System.Collections.IEnumerator e = fr.GetSubParts();
while(e.MoveNext())
{
  Part sub = (Part)e.Current;
  Console.WriteLine("\t\"{0}\" of type <{1}>;",
                    sub.Identifier, sub.Class);
}

In a similar fashion, we request the l_recent part:

17
18
19
20
21
Part l_recent = a.GetPart("l_recent");
string propertyName = "text";
Console.WriteLine("The [{0}] property of part \"{1}\"" +
                  " is: {2}", propertyName, l_recent.Identifier, 
                  l_recent.GetProperty(propertyName));

Here we write out l_recent‘s text property. So it is also possible to request properties from a part, and query their value. In fact, the whole Uiml.net API is available through this mechanism.

One might wonder, what if the application logic is only interested in certain events from a specific branch of the part tree? This is possible by adding an additional parameter to the Connect method, specifying which part and its subchildren it should connect to:

uimlDoc.Connect(this, "l_recent");

Bzr versus git

I have been exploring distributed revision control systems for almost a year now.

While I am impressed by the speed and features of git, I still prefer user-friendly interfaces such as the ones provided by darcs and bzr.

So I decide to compare the performance of git and bzr on a well-known example of a large source tree, the Linux kernel. I downloaded linux-2.6.0, and the latest version, linux-2.6.15.4.

These are the versions of git and bzr that I used:

$ git --version
git version 0.99.9c

$ bzr --version
bzr (bazaar-ng) 0.7pre

First I did an init in the 2.6.0 directory:

$ time bzr init

real    0m1.593s
user    0m0.140s
sys     0m0.047s

$ time git-init-db

real    0m0.161s
user    0m0.000s
sys     0m0.006s

Then I added all files:

$ time bzr add > /tmp/bzr-add

real    0m31.870s
user    0m31.072s
sys     0m0.520s

$ time git-add > /tmp/git-add

real    0m42.121s
user    0m32.428s
sys     0m3.208s

To my surprise bzr was quite a bit faster than git here.

Then I did a cp -r ../linux-2.6.15.4/* . inside the linux-2.6.0 directory. Now, how about doing a diff?

$ time bzr diff > /tmp/bzr-diff

real    1m13.869s
user    0m26.168s
sys     0m2.860s

$ time git-diff > /tmp/git-diff

real    2m26.982s
user    1m48.952s
sys     0m39.048s

Again, bzr is faster than git.

Let’s commit the initial revision:

$ time bzr commit -m "" > /tmp/bzr-commit

real    2m4.757s
user    1m16.578s
sys     0m6.195s

$ time git-commit -a -m "dummy." > /tmp/git-commit

real    0m54.964s
user    0m49.719s
sys     0m3.297s

As you can see, committing a large tree is where git really shines.

Next, I did a stupid test, I wanted a diff of all changes after the commit. Since I didn’t change anything, the diff would be empty:

$ time bzr diff > /tmp/bzr-diff-after-commit

real    3m51.918s
user    0m7.216s
sys     0m1.970s

$ time git-diff > /tmp/git-diff-after-commit

real    0m0.057s
user    0m0.009s
sys     0m0.047s

Git knows there have been no changes, so it immediately returns. Bzr on the other hand searches the whole tree again to see if there are changes, which is of course very slow. Git is a clear winner here.

Let’s give bzr another chance, what about a status report? This is something developers do often, they want to know what files were modified, added, deleted, and so on.

$ time bzr status > /tmp/bzr-status-after-commit

real    0m19.711s
user    0m15.180s
sys     0m1.178s

$ time git-status > /tmp/git-status-after-commit

real    0m0.442s
user    0m0.256s
sys     0m0.202s

It’s not as bad as the diff, but bzr is still way to slow here. Git, as expected, can easily check if there have been changes, so it immediately returns.

Now, let’s actually do a change. I added my name to the MAINTAINERS file, and tried to commit it.

$ time bzr commit -m "bla" > /tmp/bzr-commit-after-change

real    2m6.685s
user    0m31.734s
sys     0m3.458s

$ time git-commit -a -m "bla" > /tmp/git-commit-after-change

real    0m7.364s
user    0m6.936s
sys     0m0.430s

It takes git only 7 seconds to update its datastructure, in order to easily check if there has been a change at a later time. Bzr however seems to be traversing the whole tree again.

As a conclusion, we can say that until the initial commit, bzr is very fast (mostly even faster as git). However, after committing, git can easily check against the committed version, while it takes bzr very long to do that. Performing a diff, getting the status or committing again is very slow compared to git.

Without an intial commit, bzr diff is two times as fast as git-diff. Adding is also quite a bit faster.

Of course this was not a fair comparison, since the bzr developers have not been optimizing for speed at the moment. Speed improvements are planned for bzr 2.0. And I can’t blame them, wasn’t it Tony Hoare who stated: premature optimization is the root of all evil?

I think bzr will certainly do for my projects, and can only get better in terms of performance. Its user experience is excellent as opposed to git’s. It also has good support for Windows, which is an important factor for general adoption in my opinion.