ECO III and Skype: Building a Model to Consume, Organize and Browse Skype Contacts (Part 1)
On the internet you can find a lot of examples that show how to use ECO in order to replace applications that would be considered typical “Database Applications”. This article will focus on a completely different aspect of how ECO can become very useful. Let us assume that you have a lot of data you want to organize and analyze in order to create reports, extract additional knowledge or convert the information into another format. If you have the data in a database you build queries and thus get the desired facts. You might even use a tool that allows you to assemble these queries using a graphical user interface, so that you do not have to construct the mostly very complex SQL queries from scratch. But where do you store the knowledge you retrieve? Another table in the database might be created in order to store the gained facts and the database as a whole becomes even more complex. Another issue is that the data to be analysed is not even persisted in a database you can access with SQL and thus most data mining tools will not help you at all. This article uses Skype (http://www.skype.com/) as an example.
Why Skype?
The reason for using Skype is that I hardly know any software developer who does not have Skype installed on his system. If you do not know Skype yet, go to http://www.skype.com and form an opinion about it. If you want to experiment with the example source of this article, you will need to install it on your system. You will also have to add people to your “contact list” in order to talk with them, which can be compared to an address book.
Why the need for an ECO model?
There is no information on where or how Skype stores this contact list. But there is a COM API, which can be downloaded from https://developer.skype.com/Docs/Skype4COM. Please download the library from that location and use the installer. That will take care of registering the necessary COM libraries in the registry and will make it possible for you to run the example.
If you look at the detailed documentation of this library, you will see that quite a lot of information can be requested for the people on your contact list. However, all the information for a user is stored in one instance of a class named “User”. Information could be extracted if you read all the users, imported them into a database and then thought about how you could present the data to the user so s/he could analyze it. This makes the Skype COM API a perfect use case for an ECO model. Let me make one thing very clear: the model we will build here is just “one option”. There are many other approaches possible if you want to analyze different things. Take this article as a means to be able to build your own models for similar cases.
The reason for using Skype is that I hardly know any software developer who does not have Skype installed on his system.
Finding interesting facts in the data
As said in the last paragraph, it might not make very much sense to take a Skype contact list as a data source for this, but it shows the techniques to “get the work done”. Studying the properties of the class “User” of the Skype COM API (table 1) the following questions and tasks may arise:
- which users can be included in video conferences?
- how many of my contacts are male/female?
- create a list of all birthdays
- group contacts by country
- […]
I am quite sure you could do this all using a database: import all the data, build queries and present it to the user. However, this requires quite a lot of work as you need not only write your code to import the data, but you also have to design tables in a database. In addition to all this, you need to make certain all of the information can be presented to the user. Why not build a class model that stores all the information and makes the structure of the data easily understandable, amendable and extensible? ECO gives you all that.
Building the model
Please refer to my last article in issue 90 of this magazine (“ASP.NET: Using ECO III to Build Maintainable Applications with Authentication and Authorization”) how to get started building models. That article will also explain ECO Packages and their advantages in detail. The model we will build in this example consists of two ECO Packages:
|
InteropPkg: |
contains the bridge between COM and ECO and the class “Controller” that manages our business logic, uses classes from ContactPkg (see igure 1) |
| ContactPkg: |
contains the two main classes “Contact” and “Group” (see figure 2) |
The application I created for the example is a “Delphi for .NET ECO Winforms Application” and I included the model in the application. I did not create a separate package DLL which contains the model to make the source code easier to deploy. As I already stated in my first article, it is better to build a separate package DLL, because it makes your models easier to re-use.
I will start by explaining the InteropPkg. It contains three classes. The classes “Contact” and “Group” have a little framed arrow in the left bottom corner, which means that another model contains them. In Together one refers to those as “shortcut classes”. This leaves the class “Controller”.
The Controller is the key…
The class “Controller” embodies only operations and has a one-to-many association to “Contact” and another one with the same multiplicity to “Group”. This means that every instance of “Contact” and “Group” refers to exactly one instance of “Controller”.
The operations of “Controller” are of particular interest. First, let us focus on “getController”. This operation returns an instance of “Controller” and has its “static” property set to “true”. This makes “Controller.getController” a static method in the ECO source code that will be generated from the model. “Static” methods or “class methods” are nothing new in object-oriented programming. However, this method makes sure that there will be only one instance of “Controller” in your EcoSpace.

Fig. : ECO Package “InteropPkg”. This model describes the class “Controller”. The Controller controls the creation of groups and contacts and also contains the implementation for the data extraction from Skype.
“There can only be one.”
Listing 1 will show you how to implement a Singleton Pattern with ECO. A Singleton Pattern implies that a class has only exactly one instance. Normally, you create objects in your EcoSpace using “myController := Controller.Create( EcoSpace )”, for example. You could still do that. However, you would end up with more than one instance of Controller this way. If you use “getController” the method will create a new instance if there is none in your EcoSpace, otherwise return the one that is already there. So instead of creating the instance of “Controller” manually, you simply call “Controller.getController”. The IExtentService of ECO contains a method called “allInstances” which yields a list of all objects of a certain class that you can specify as a String. So we use that in order to determine the number of instances. If it is equal to “0” we create a new instance, otherwise we return the first instance of the list.
All ExpressionHandles you hook up to “rhRoot” will have its root in “Controller”
In listing 2 you can see how this is being applied in a Winforms Application. Not only is an instance of “Controller” being created, it is also being set as the element for the ReferenceHandle named “rhRoot”. The consequence of that is that all ExpressionHandles you hook up to “rhRoot” will have its root in “Controller”. Thus to yield all the groups you do not type “Groups.allInstances” but “self.Groups”. This might seem unnecessary at first, however, “Controller” also has two more operations to get instances of the classes “Group” and “Contact” shown in listings 3 and 4. Both methods have the same structure, so I will only explain listing 3 in detail, which gets a group by its name. First, we get a reference to the IOclService. Then we evaluate the expression “self.Groups” and select only the instance that has the name “AName”, which is a parameter of the method. The expression used will return “nil” if no group matches the given criteria and thus a new instance needs to be created. Mind that the new instance has to be hooked up to the instance of “Controller” being used, otherwise you will end up creating every group over and over again as it will never be found by the OCL expression used as we specify the current controller as our context (=origin) for the expression.
class function Controller.getController(AService:
IEcoServiceProvider): Controller;
var
extent : IExtentService;
l : IObjectList;
begin
// implementation of Singleton Pattern using
//IExtentService
extent := EcoServiceHelper.
GetExtentService(AService);
// get all instances
l := extent.allInstances( 'Controller' )
as IObjectList;
// if there is none, create, otherwise
// return the only one
if l.Count=0 then
Result := Controller.Create(AService)
else
Result := l[0].AsObject as Controller;
end;
Listing 1: getController makes sure that there is only one instance of “Controller” using the IExtentService
constructor TWinForm.Create;
begin
inherited Create;
//
// Required for Windows Form Designer support
//
InitializeComponent;
FEcoSpace := TSkypeUserBrowserEcoSpace.Create;
rhRoot.EcoSpace := FEcoSpace;
FEcoSpace.Active := True;
// -- get instance of Controller
FController :=
Controller.getController(FEcoSpace);
// -- assign instance as element
rhRoot.SetElement(FController);
end;
Listing 2: Constructor of a Winform that uses the “Controller” class as its root element
function Controller.getGroup(AName: string): Group;
begin
Result := (EcoServiceHelper.GetOclService(self.AsIObject.
ServiceProvider).Evaluate( self.AsIObject,
'self.Groups->select(name='''+AName+''')->first' )
as IObject ).AsObject as Group;
if not Assigned(Result) then
begin
// create Group instead
Result := Group.Create(
self.AsIObject.ServiceProvider);
Result.name := AName;
Result.Controller := self;
end;
end;
Listing 3: getGroup gets a group by its name; if it is not found, a new instance will be created
function Controller.getContact(AHandle: string):
Contact;
var
ocl: IOclService;
begin
ocl := EcoServiceHelper.GetOclService(
self.AsIObject.ServiceProvider);
Result := ( ocl.Evaluate(self.AsIObject,
'self.Contacts->select(handle='''+
AHandle + ''')->first' ) as IObject ).AsObject
as Contact;
if not Assigned(Result) then
begin
Result := Contact.Create(
self.AsIObject.ServiceProvider );
Result.handle := AHandle;
Result.Technical := Technical.Create(
self.AsIObject.ServiceProvider);
self.Contacts.Add(Result);
end;
end;
Listing 4: getContacts gets an instance of Contact by its “handle”; if there is not instance in the EcoSpace with that “handle”, a new instance will be created.
A package for the actual “data”
InteropPkg has been a very technical approach to building an ECO model so far. I will be honest and say that I started with the package that will be looked at in detail now: ContactPkg. However, for the structure of this article I think it is much better to explain it this way. The complete model is shown in figure 2.

Fig. 2: ECO Package “ContactPkg”; this model describes the class “Contact” and its related classes
The central class in the model is named “Contact”. This class contains all the information that can be gathered from the Skype COM API. That being said you might wonder why there are five more classes in the model. “Technical” and “ContactNumber” simply embody information that I – as the designer of the model – wanted to separate from the class “Contact”. Furthermore, you can access technical configurations of your contacts much easier using OCL this way if it is encapsulated in its own class. I suggest you open the example application in BDS and try to form some OCL expressions that deal with “Technical”. This way, you will see the advantages of this approach pretty quick.
I just have to point out that this is not a required thing to do. It would have been just as fine modeling “Birthday”, “Gender”, “Group” and “Technical” as attributes of “Contact”. Just for our task at hand it is much better to do it this way. The class “ContactNumber” should always be modeled this way in my opinion. An instance of this class is designed to be created for every contact number, be it mobile, landline or other number you can store in Skype. If I had created an attribute for each of those numbers, the class “Contact” would have been rather unstructured and messy. This way there is also only going to be an instance of “ContactNumber” if the contact really has a value entered. If those were attributes in the class, they would be unused and simply make the class harder to understand when looking at the model. Furthermore, strictly speaking a phone number has nothing to do with a person. I was even a little bit in doubt about the attribute “homepage”. However, I decided to include it in “Contact” and not create a separate class named “Website”, for example, as there was only one attribute.
I advise you to use the example source and have a look at all the different classes, attributes and associations to get a feeling how everything is set up. I will not cover the aspects I explained in detail in my last article, which has been printed in the last issue.
More details on “Technical”, “Gender” and “Birthday”
Before starting to explain the data import from Skype, I want to focus on the roles of the classes “Technical”, “Gender” and “Birthday”. For every instance of “Contact” there has to be exactly one instance of “Technical”. To make things easy, I do this manually in the method that imports the contacts from Skype. “Birthday” and “Gender” are different. If you look into the documentation of the Skype API, there are exactly three genders: male, female and unknown. Thus, there will be only three instances of “Gender” and every contact refers to one of those. For an instance of “Gender” this means it can refer to many “Contact” instances. Thus, we have a “1-to-many”-association. Just like the class “Controller”, “Gender” also has a class method that can be called to get an instance for “Gender” supplied as a parameter. We also have to supply a reference to the EcoSpace, which has IEcoServiceProvider as data type. Listing 5 shows “Gender.getContact” and in listing 6 you can see it being used to assign a gender to an instance of “Contact”. Operations like these are really useful as you normally would have to check every time if the instance you want to refer to has already been created and create it eventually. This way your code will look much simpler and easier to comprehend as it simply says “here I need a reference to an instance of “Gender” with id x”. Furthermore, methods like “getGender” can easily be used to assign different “default-Values” to instances. If these “defaults” change, you will have one place only where you need to change them.
We will have a detailed look at the “Birthday” class in my next article as instances of that class are created with yet another exciting ECO technique that makes our life to work with objects much easier.
class function Gender.getGender(
AService: IEcoServiceProvider; AId: string): Gender;
var
ocl: IOclService;
g: Gender;
begin
ocl := EcoServiceHelper.GetOclService(AService);
g := ( ocl.Evaluate(
'Gender.allInstances->select(id=''' + AId +
''')->first' )
as IObject ).AsObject as Gender;
if not Assigned( g ) then
begin
g := Gender.Create(AService);
g.id := AId;
end;
Result := g;
end;
Listing 5: This class method gets an instance of gender
Connecting to Skype
After designing the model we have to add a reference to the Skype API. You can use “Project / Add Reference…”. The dialog that will appear is a little bit clumsy to use, so please look at figure 3 and then select “Skype4COM 1.0 Type Library” in the listview on the tab called “COM Imports” (1). Click “Add Reference” to add it to the list at the bottom of the dialog (2). Check that the library appears in the list and press “OK”. Delphi will create an interop-library for us automatically, so that we can use anything that is inside the library as if it were “just another unit” in our project. In order to do that you merely need to add “SKYPE4COMLib” to your uses-clauses.

Allowing access
If you are worried now as you know how easy it is to install the necessary libraries to access Skype, it still requires your intervention before the Skype COM API actually sends information from Skype to any application as you can see in figure 4. For a developer this is even too secure and might become tedious, because even if you choose “Allow this program to use Skype” you have to do that again once you recompiled your application. Skype notices that the contents of the file changed and thus asks your approval again.

Fig. 4: Skype prompts the user for confirmation whenever an application wants to retrieve data using the Skype COM API
Importing the data
The method to import the data seems almost like pseudo-code now as the model we designed previously takes care of all the technical aspects. The import process works as follows:
- connect to Skype
- get a list of all groups on the contact list
- enumerate and import all the groups in the list from item 2:
- get the list of contacts (“users”) in that group
- enumerate and import all the users
Step 1 can be done by creating an instance of “SkypeClass”. “SkypeClass” has a property named “Groups” which returns a list of all Skype groups. We are already up to step 2 that iterates over all the elements using a “for-in”-loop. At that point you see why we implemented all those operations in the model. To get an instance of (the ECO class) “Group”, we simply use “self.getGroup”. It will be created if it is a group that has not been created yet. Then we can assign the properties to the class. The COM API delivers a “Users” property for every group, which we can use in conjunction with “for-in” to access all the contacts in that group (step 3 a.). Yet again we can use one of the “get”-operations we implemented to get an instance of “Contact” and can assign the properties afterwards, which already is the final step (3 b.). I hope you see now that planning and working on the model before actually “using” it, is very important and can safe a lot of time in the end. Listing 6 shows the complete code for the method.
function Controller.collectData: string;
var
skype : SkypeClass; // Skype COM object
lgr : GroupCollection; // list of groups (ECO)
gr: Group; // instance of group (ECO)
u : SKYPE4COMLib.User; // instance of user (Skype)
skypegroup:
SKYPE4COMLib.Group; // instance of
// group (Skype)
c : Contact;
begin
skype := SkypeClass.Create;
lgr := skype.Groups;
// get all the groups with contacts
for skypegroup in lgr do
begin
gr := self.getGroup( TObject(
skypegroup.&Type).ToString()
.Substring(3) );
gr.description :=
skypegroup.DisplayName;
// now enumerate contacts
for u in skypegroup.Users do
begin
c := self.getContact(u.Handle);
// add to group
c.Groups.Add(gr);
// set properties according to Skype
c.about := u.About;
c.displayname := u.DisplayName;
c.language := u.Language;
c.fullname := u.Fullname;
c.lastonline := u.LastOnline;
c.homepage := u.Homepage;
c.country := u.Country;
c.province := u.Province;
c.city := u.City;
c.DateOfBirth := u.Birthday;
c.Technical.hasCallEquipment :=
u.HasCallEquipment;
c.Technical.hasCallForward :=
u.IsCallForwardActive;
c.Technical.hasSkypeOut :=
u.IsSkypeOutContact;
c.Technical.hasVoicemail :=
u.CanLeaveVoicemail;
c.Technical.hasVideo :=
u.IsVideoCapable;
// get and link instance of gender
c.Gender := Gender.getGender(
self.AsIObject.ServiceProvider,
TObject(u.Sex).ToString);
// Contact numbers left out for
// further examination and
// todo by readers...
end;
end;
end;
Listing 6: This method imports the data from the Skype contact list into the ECO model; please mind that the variable names have been kept short, so that there is a minimum of line breaks; they should be renamed properly
Where is the database?
It is a common misconception that for ECO applications a database connection is mandatory. I think this example shows very well a use-case for which a database would make no sense so far at all. Thus, all the classes in the model have been marked as “transient”. So even if you assigned a database connection and set up an ECO Persistence Mapper, nothing would be stored in the database.
It still requires your intervention before the Skype COM API actually sends information from Skype to any application
Conclusion
I agree, I described a lot of things, giving you a lot of details on the model and not even explaining the graphical user interface to you. I will do so in part 2 of this article.
In this part I have explained how to use class methods with ECO, how to implement the Singleton Pattern using IExtentService, how to connect to Skype and how to import the information from the contact list into the model. You may simply start the example application that already is being made available with this part of the article and use the ECO debugger to browse the EcoSpace.
What will be next?
Part 2 of this article will explain to you how you can generate the birthday list with the help of attributes in the model that have user code. Furthermore, we will build a Winforms Application with Master-Detail-functionality and I will also give some information on how to use VCL.NET to display a nice birthday calendar. ECO III is designed to be used with Winforms mainly, but using it with VCL.NET is possible as well.