Calendar:Architecture: Difference between revisions

Jump to navigation Jump to search
no edit summary
No edit summary
 
No edit summary
Line 1: Line 1:
This document describes a proposal for a new architecture of the calendar content model.
This document describes a proposal for a new architecture of the calendar content model.


== Current state ==
== Architectural review ==
Currently the calendar component is built around the calIItemBase interface, events and todos derive from it and add only their specific functionality. Different providers implement the calICalendar interface, which is basically the authoritive instance keeping the storage and the calIItemBase objects in sync.
Currently the calendar component is built around the calIItemBase interface, events and todos derive from it and add only their specific functionality. Different providers implement the calICalendar interface, which is basically the authoritive instance keeping the storage and the calIItemBase objects in sync. Anyone can register as an observer at a calICalendar to get notified of changes to the model.


instances of the calIItemBase interface currently have strong value semantics, which means that in order to modify an item one needs to clone() the object, modify the newly created object and notify the calICalendar object.
Instances of the calIItemBase interface currently have strong value semantics, which means that in order to modify an item one needs to clone() the object, modify the newly created object and notify the calICalendar object.
 
The following section will consist of a short review of the current interfaces to show the relationship among the main interfaces. I won't touch every aspect and it won't go into much detail since this is irrelevant to the proposal of the new architecture of the model.
 
Let's start with the calICalendar interface, where only the interesting methods are shown:
 
  void addObserver( in calIObserver observer );
  void removeObserver( in calIObserver observer );
 
  void addItem( in calIItemBase aItem, in calIOperationListener aListener );
  void adoptItem( in calIItemBase aItem, in calIOperationListener aListener );
  void modifyItem( in calIItemBase aNewItem, in calIItemBase aOldItem,
                  in calIOperationListener aListener );
  void deleteItem( in calIItemBase aItem, in calIOperationListener aListener );
 
  void getItem( in string aId, in calIOperationListener aListener );
  void getItems( in unsigned long aItemFilter, in unsigned long aCount,
                in calIDateTime aRangeStart, in calIDateTime aRangeEndEx,
                in calIOperationListener aListener );
 
As shown in the interface above, clients can add items and modify or delete existing items. This is the first group of methods which generally belongs to the item management. Clients can also register as an observer to get notified to changes (addObserver,removeObserver). The last important group is getItem() and getItems() which allows clients to query for items with specific attributes or belonging to specific ranges.
 
The next important interface is calIItemBase, again only the most relevant aspects are shown here.
 
  attribute AUTF8String id;
  attribute AUTF8String title;
 
  attribute calIRecurrenceInfo recurrenceInfo;
  attribute calIDateTime recurrenceStartDate;
 
  void getAttendees(out PRUint32 count,
                    [array,size_is(count),retval] out calIAttendee attendees);
  calIAttendee getAttendeeById(in AUTF8String id);
  void removeAttendee(in calIAttendee attendee);
  void addAttendee(in calIAttendee attendee);
  void removeAllAttendees();
 
  readonly attribute boolean isMutable;
  void makeImmutable();
  calIItemBase clone();
 
  attribute calICalendar calendar;
 
  calIItemBase createProxy();
  attribute calIItemBase parentItem;
  attribute calIDateTime recurrenceId;
 
  void getOccurrencesBetween (in calIDateTime aStartDate, in calIDateTime aEndDate,
                              out PRUint32 aCount,
                              [array,size_is(aCount),retval] out calIItemBase aOccurrences);
 
The first group of attributes and methods belong to the properties of the item (title,attendees,recurrences,etc.). The second part is made of the 'isMutable'-attribute and the clone()-method. An item is generally 'readonly', writing attributes of such an item will throw an exception. Clients need first to clone() the item and modify the newly created object. The last section is dedicated to recurrence handling. Items can have a recurrence-rule (contained in the calIRecurrenceInfo-object) which means that this event will occur several times during its lifetime. This aspect of the item handling is modeled with help of socalled 'proxy'-items. Those items contain the informations regarding some specific occurence of the main event and refer to it through their 'parentItem' reference. The parentItem does not know anything about living proxy items and proxyItems can be generated with help of the 'getOccurrencesBetween()'-method. Clients have a consistent interface for either parentItems or proxyItems, both implement the calIItemBase interface. In case clients need to know if an object refers to a parentItem or a proxyItem, they can check if item.parentItem == item, or in other words, if the parentItem refers back to 'this' we've a reference to a parentItem.
 
Since parentItems and proxyItems implement the same interface, proxyItems can also be passed to methods to calICalendar or any other method that expects a calIItemBase. The implementation of methods expecting a calIItemBase eventually need to figure out if they're given a parentItem or proxyItem.
 
The typical way of a call-chain can easily be sketched with a simple example. Let's assume some client (e.g. a view) exists and is registered as an observer with the calICalendar instance. It then could create a new event, fill its attributes and tell the calendar about the new instance. The clients will get notified through the appropriate onAddItem()-method since it registered as a listener. It is worth noting that the item passed as argument to onAddItem() is no longer the item which was originally created by the view, since it gets cloned() on its way. The client will probably call getOccurrencesBetween() on the item to receive a list of proxyItems that are all specific occurrences of the original object. If the client decides to modify the parentItem or any of the proxyItems, it needs to clone() the instance, modify the attributes and pass the new object to modifyItem() provided at the calendar implementation. The calendar will sync the incoming items with the underlying storage and send a notification via onModifyItem() on all registered listeners. This mechanism ensures the client receives the modifed item. Again please note that the item was cloned at least twice, once to make the original modification and at least one more time in the calendar during the sync process. After the client received its notification message, it needs to get rid of its now outdated references and again query for the now current occurrences through getOccurrencesBetween() to display the modifed state of the item.
 
The advantages of this approach are as follows:
 
* If you clone(), you know you have the sole reference to that object, if you don't, someone else might change it without your knowing
* If you just modify the events (without cloning), then other places that have a reference to the same item, suddenly have a reference to an item that isn't in sync with the store.
* This approach makes it easy to implement a generic undo/redo functionality.
* Failures during the sync with the storage are handled elegantly.
 
The disadvantages are as follows:
* clone()ing costs an enormous amount of resources, since we constantly create and delete object. We need to even always copy objects that don't need to be copied in case we just want to modify a single attribute.
* This approach opens the door for all sorts of 'dangling references'-issues, since it's easy to keep outdated version of items.
* It's tedious to compare items, since two instances of an item still could refer to the same semantically equal object.
 
== New proposal ==
...
40

edits

Navigation menu