DEVELOPER'S DOCUMENTATION
=========================


This document is meant to provide a good insight into the architecture
choices made in Elisa. It should not have a too steep learning curve
and the explanations cannot refer to concepts not previously defined. A
reasonable use of common design patterns has been made to facilitate its
understanding. If something is unclear, you are more than welcome to notify
the team.

General goal: what the architecture strives to achieve
======================================================

Elisa's architecture aims to make adding new features *very easy*. To
achieve that goal, it is designed to be as **modular** as possible:
the developer can concentrate on one piece of code at a time without wasting
precious time in having to understand too many concepts and parts of Elisa
at once. Moreover, all the modules are meant to be **extendable**, that is,
new functionalities/components can be added and distributed without being
included in the core thus favoring participation external to the core
developers and designers. With these two concepts in mind, **modularity
and extendability**, two distinct people can produce two separate new
components that work together without knowing about each other.

Elisa contains the following modules:

- `Plugins module`_
- `Metadata module`_
- `Media module`_
- `Input module`_
- `User interface module`_
- `Player module`_

For each module, three things are provided later on in the documentation:

- its rationale, objectives and responsibilities including various use cases
  (UML activity diagrams can help explaining further a particular use case)
- UML class diagrams defining all the classes and their relationships
- UML collaboration diagrams describing the interaction between the objects

When relevant UML sequence diagrams can be used to answer the use cases in more
details.

UML notations are quickly summarised in this `reference card from Laurent Grégoire`_.

.. _reference card from Laurent Grégoire: http://tnerual.eriogerg.free.fr/umlqrc.pdf

General architecture
====================

In order to achieve a good modularisation, it has been decided to control
accesses to many components using the **Proxy design pattern**. This
rule is enforced in the `Metadata module`_, the `Media module`_ and the
`Input module`_.  In order to provide extendability, a system of pluggable
components has been designed using the **Factory design pattern**
and is called the `Plugins module`_.

Core architecture
-----------------

Application is the entry point of Elisa.
It groups all the necessary elements needed for Elisa to run.
It is in charge of instanciating a Config and a PluginRegistry.
A Config instance can parse configuration
files and provide their structured content to any object that asks.
The PluginRegistry is responsible for finding all the plugins and providing an
access to the classes defined in them.
Application also provides access to input events and data, and
holds the user interfaces. It creates various managers (InputManager,
MediaManager...) which are proxies for their Providers, an InterfaceController
explained in `User interface module`_ and a DBBackend. All data that have to
be stored are saved by a DBBackend instance to a database. DBBackend is
responsible for controlling what objects access this database.

|Application classes|

# FIXME: add message bus (MessageBus class) creation

.. |Application classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Application.png


Threading rules
---------------

An essential point of Elisa is to provide a reactive user interface which
never blocks the user. In order to achieve that, all method calls for which
the return might not be immediate (e.g. thumbnail generation...) has to be
made non-blocking. Elisa uses Twisted's deferreds to handle delayed returns.
Use of threads should be avoided in Elisa and whenever it is used, they must
be created using Twisted facilities (method deferToThread). The main thread
should be used for all the operations: all function calls should be done from
the main thread. In the case of calls from a separate thread Twisted allows
execution of function calls from the main thread (method callFromThread).


Communication within Elisa
--------------------------

Apart from direct method calls, two messaging systems are used in Elisa:
a bus (MessageBus class) and signals (Signal class).

Communication to the core components should use method calls as much
as possible when it's for 1-1 communications. In the other cases, the
following rules can help choosing between a bus message and a signal:

- The MessageBus should be used for 1-n asynchronous communications. 
- Signals should be used for 1-n synchronous communications. The
  signal.emit() call is blocking and should be used with care.
- Signals should reside at least in the module from where they are
  emitted. Best is to store them directly as instance attributes of the
  emitter, when the instance is easily accessible by others.

It is important to remember that the messages on the bus are dispatched
in the main thread whereas signal emissions are done in the calling thread.


Modules: their purpose and their design principles
==================================================

FIXME: add a diagram describing the interaction between the modules is
needed somewhere (diagram with packages).


Plugins module
--------------

FIXME:

- lacks of rationale/use cases and collaboration diagram

|Plugins classes|

The PluginRegistry is responsible for finding all the Plugins and providing an
access to the Components defined in them. A Component is an object that
can be instantiated and reused anywhere in Elisa. A Plugin is a factory of
Components. Developers can develop their own Plugins to provide their own
Components. PluginRegistry implements the **Factory design pattern**
to achieve it.
Base Components are defined to standardise common functionalities that are
likely to be reused. It is then possible for others to retrieve a list of
Components of a certain type and choose at runtime the one to use.
This approach may lead to a big number of different base Components.
A Plugin can contain several Components of the same type.

|Components|

References to Component instances must not be kept by the PluginRegistry. They
are returned by the PluginRegistry to the caller. The caller can be any
class. In certain cases, a Manager is provided to abstract various Components
providing the same functionality: Providers. Managers ask and keep references
to Providers instantiated by the PluginRegistry. Providers must not be called
directly: all the calls made are made to a Manager which relays them to the
right Providers. This is an implementation of the **Proxy design pattern**.

|Manager classes|


.. |Plugins classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Plugins.png
.. |Components| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Components.png
.. |Manager classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Managers.png


Media module
------------

FIXME:

- lacks of a collaboration diagram
- discuss data caching
- plugins might want to store data in the database -> ACL system might
  be needed

The media module provides access to media files in a protocol transparent
fashion. Location of media files are specified using special URIs. 
It also handles scanning and monitoring of media sources and keeps track
of what media are available in Elisa.

|Media classes|

.. |Media classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Media.png


The MediaManager is the central class to which all the calls are made. It
holds MediaProviders instances and relays file access requests to them
(implementation of the **Proxy design pattern**). The MediaProviders in turn
implement the various file I/O protocols. The requests are made using MediaURI
instances which are roughly based on URLs and the answers are either MediaURI
instances or Python file descriptors.
The MediaManager is portable by design. The MediaProviders can be
platform-dependent or require external applications running.

Media's data and media's metadata are stored in a specific table of Elisa's
database (DBBackend) (FIXME: decide which one) which is only accessible through
MediaDB. MediaDB has a set of methods to facilitate its access. There is no
direct access to the database outside of MediaDB: no SQL requests outside of
MediaDB.

A MediaScanner (held by the MediaManager) handles a list of media sources
declared in the Application's config and is able to scan them (via the
MediaManager's MediaProviders), retrieve metadata of the various media
found (using the `Metadata module`_'s facilities) and store everything in
using MediaDB. MediaScanner maintains the database up-to-date by periodically
rescanning media sources. Media sources can also be monitored if related
MediaProviders support monitoring. Managing media sources (adding them,
removing them...) can be done through the MediaManager which relays orders to
the MediaScanner.

ElisaMediaProvider is a special MediaProvider defining a tree that does not
correspond to a filesystem but rather to the media's table in Elisa's
database. It allows requesting data from there using a MediaURI. FIXME: a
document describing the syntax and explaining the mechanism is being written
by Lionel and will be merged later on.


Metadata module
---------------

FIXME:

- lacks of use cases
- lacks of a collaboration diagram

The metadata module provides access to media's metadata. It has the ability to
cache the results in Elisa's database (DBBackend) for faster results.

|Metadata classes|

.. |Metadata classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Metadata.png

The MetadataManager answers other objects' requests for metadata. It
holds MetadataProviders instances and uses them to retrieve metadata from
files. It caches the requested metadata to the database through MediaDB in
order to improve the speed of future accesses.

Service module
--------------

FIXME:

- lacks of everything: rationale, responsibilities, use cases, collaboration diagram

|Service classes|

.. |Service classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Service.png



Input module
------------

FIXME: lacks of rationale/use cases

|Input classes|

The InputManager class is the link between the InputProvider and all the
objects that need to listen to user inputs. Objects can register to it
to receive InputEvents from given InputProviders.
Again, InputProviders instances are held by the
InputManager and all the calls are relayed to the providers via the Manager
(implementation of the **Proxy design pattern**). Every InputProvider
implements a specific input protocol (e.g.: keyboard input via Pigment,
remote control via LIRC...). They return InputEvents which encapsulates
the type of event and device from which the events are from (along with other
useful information detailed in the InputEvent class).

|Input collaboration|

.. |Input classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Input.png
.. |Input collaboration| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/InputFlow.png


User interface module
---------------------

The user interface design has the following set of objectives:

 - make it easy to change the appearance (being the layout, the icons,
   the animations or the graphical toolkit) without modifying the way the
   data is generated/retrieved
 - make it easy to add new features without the burden of writing drawing
   code
 - increase the visual consistency of the whole application
 - be able to display several times the same content at once on one or
   more Elisa
 - have a natural synchronisation of several user interfaces between
   themselves and with the underlying data

In order to achieve these goals, data and data rendering are separated
using a variant of the original **MVC design pattern**.
A network transparent **Observer design pattern** is also involved.

FIXME: A COMPLETE UPDATE OF THE FOLLOWING IS REQUIRED, PLEASE BE PATIENT.

|MMV classes|

FIXME:

- separate in actors logic -> contribution can be a new Theme,
  a new View for an existing Controller or a new couple View + Controller.
- once Observer/Observable implementation is complete, add a detailed explanation

**Model**

A Model describes the logical organisation of data that dictates the way
they will be rendered. A Model is a container for data that have been
retrieved from the data layer (MediaManager, MetadataManager, DBBackend):
Activity instances ask for the data to the data layer, fill Models with
them and send it for rendering.

The data layer must notify Models holding data whenever a modification
of the same piece of data happened so that the user interface stays in
synchronisation with the actual data source.

**Controller**

A Controller encapsulate a Model adding information such as rendering
parameters. It is meant to allow variations in the rendering of the same
Model instance. A Model has to notify any of its changes to the Controllers
that are encapsulating it.

A Controller instance can be shared over a network with other Elisa
processes. This implies that the Model it encapsulates and the data
contained by the Model have to be serialisable.

Let's use an example to explain further Controllers purpose: a clock plugin.

For a clock plugin to display the time, it first needs a Model containing the
current time: ClockModel. Let's assume that ClockModel stores it as the number
of seconds since the 1/1/1970. To be able to display this ClockModel, a View
is necessary: ClockView. However, in order to change the way the ClockModel is
render by the ClockView, some display parameters, such as the formatting of the
time, have been introduced. For that purpose, an extra class, the
ClockController is needed. A reasonable question could be: why do we need an
extra object to achieve that ? The answer is the following:

 - the actors' logic: two different people potentially implement
   ClockModel and ClockView separately (without knowing about each other):
   therefore the display parameters cannot be inside the ClockModel
 - we want to be able to synchronise two ClockViews: therefore the display
   parameters cannot be inside the ClockView

**View**

FIXME: add compatibility View/Controller notion (may go into Skin)

A View is a visual representation of a Controller. It uses a Controller to
retrieve the data and parameters necessary to its rendering.
Multiple Views can render the same Controller. A Controller has to notify any
of its changes to the Views that are rendering it.

A View instance cannot be shared over a network with other Elisa
processes. It is always a local object tight to the graphical toolkit used.


Models, Controllers and Views are (inherit from) Component:

- a Component inside a Plugin should be able to use a Model contained in
  another plugin
- FIXME: add your reason here (reasons mainly come from the actors logic)


**Composition**

The same rule of composition applies for Model, Controller and View: one
can define trees of Models, Controllers or Views.

Necessity of a root Model, a root Controller and a root View.

Views/Models/Controllers *only* communicate with:

- their parents
- their children
- their corresponding Views/Models/Controllers

Views/Models/Controllers *never* communicate with:

- their brothers
- the parents/children of their corresponding Views/Models/Controllers
- their grandparents, grandchildren, greatgrandparents and so forth:
  no generation jumps allowed

**Inheritance**

The goal of the design is to minimise as much as possible the amount of work
needed by developers. Two types of developers are involved: Models'
developers (likely to be developing new functionalities)
and Views' developers (rendering code). In order to help them in their tasks,
the two following objectives have to be completed:

- reusability of displaying/rendering code (Views): whenever possible, a
  View's developer should not have to copy/paste or rewrite any code but
  instead reuse the existing code
- extensibility of a given Model: already defined data structure should be
  not only reusable as is but also it should be possible to extend them for
  more specific purposes


To achieve these, a set of general purpose Models is proposed that can be used
by Models' developers directly or be extended using inheritance.
Correspondence between Models and Views is defined by the Skin. If a Skin does
not know about a particular Model, the inheritance tree is looked up until a
Model known by the Skin is found. To make this process easier, multiple
inheritance is forbidden.

**Theme**

FIXME

**Activity**

There are very few assumptions at the moment concerning an Activity.
It inherits from Component and its main goal is to encompass all the
functionalities not defined in all the other standards Components.

Examples:

- downloading code for the weather plugin would be in its Activity class
- FIXME: add your example here

**InterfaceController**

The InterfaceController has the responsibility to retrieve from the
PluginRegistry all the Skin and Activity components that are declared
in the configuration file. It
has the knowledge of which Skins are independent and which are clones of
other Skins. Therefore, it has to control the flow of Models between the
Activity and the Skin components.


|Simple collaboration|


.. |MMV classes| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Model-Mediator-View.png
.. |Simple collaboration| image:: https://code.fluendo.com/elisa/svn/trunk/docs/new_architecture/pictures/Separation.png


Player module
-------------

It has been decided that a simple version of the Player module would be used
first and that a more advanced one will be designed later on in order to allow
the more advanced features that are planned:

- video playback synchronisation
- pipeline distribution across the network
