How the framework works!

  1. The Avalon framework
  2. Initialization of views
  3. Modifing and validating the data
  4. Changes in the data-model
  5. Creation of new wobzilla components

The Avalon framework

The Wobzilla component-architecture is based on the concepts of the Avalon component-framework. A central principle of Avalon is "inversion of control": The componnets are rather passive and get their instructions from the superiour container (which can be a component itself). The communication between container and the component-instances managed by the container takes place over well defined interfaces (explained below). If a component needs a specific instruction, it must implement the method specified in the appropriate interface. This method will be called by the container at the right time. According to the avalon documentation this works like in the military.

configuration

The configuration is analog to the component hierarchie a tree (more precisely a DOM-Tree). Each node of the tree is a valid "sub"-configuration itself. If a component implements the configure method, it will be assigned the proper sub-configuration of the configuration-tree.

The configuartion node of a container contains the configuration of its child-components but it doesn't have to evaluate this sub-configurations. The container just passes them to its childs. In that way a complex component-configuration can be described without the components beeing complex themselfs. This is a successful application of "divide and conquer".

context

If a component implements the contextualize method it signals that it needs information from its context. Typical context entries in wobzilla are the language or special variables provided by a container. A container has further the possibility to replace or extend its assigned context, and provide this new sub-context to its childs.
See Context API.

services

Each container can provide a set services to it childs through the Service Manager object. As with context objects the container can also extend, restrict or replace the services provided by its parent, to offer different services to its childs. The childs lookup these services during the service method.

logging

TODO Explain Logging. Avalon Logger.

dispose

TODO explain disposable.


Initialization of views

In addition to the avalon-methods (which are optional for each component) each view must implement the init method. In the init method, the container assignes the output-model and the data-model to the view. The following table explains the arguments of the init-method:

argumenttypedescription
modelNodenode data-node which should be modified and/or displayed by the view. The view is not bound to edit only the passed data-node. It can also edit another node in the data-model, e.g. a sub-node or a parent-node of modelNode.
parentOuputNodenode the node in the outputTree to which the view should append its output. In contrast to the data-nodes, here the view is only allowed to append to the parentOutputNode. Accordingly only output-nodes that were appended by the view itself can be deleted.
insertAfter
optional
node the node, after which the view should append its output

The interation between containers, components and the data- and output-tree is shown in the followning collaboration-diagram:

sequence-diagram of view-initialization

Modifing and validating the data

The validate method instructs the view to validate any changes made by the user in the user-interface and write back these changes to the data-model. The view must use the wobzilla.transaction service to do this.

validate is the only method that a view should call itself. valdiate could be called as a reaction to specific GUI-events, for instance when a text-field looses focus, or a button is pressed.

validate should return false, when the validation failed and true when there was no validation or if validation was successful. Containers should forward the validate calls to their childs. Before saving a document, the validate method of the root-view is called and WobzillaEngine will only save the document if that method returned true. See wobzilla.variableHandler for further information about specifying global and local constraints and displaying error-messages.


Changes in the data-model

To get notified when the data-model changes, the component should implement the requery-method. This method is called by the container every time a change in the data-model occurs. If the view is a container itself it should forward the requery notifications to their children to achieve a successive refresh.

Note: To avoid reentrance-problems and inconsistencies, no modifications of the data-model are allowed during the processing of the requery method.

Passed to the requery is a list with all elementary node-operations that occured on the data-model, in the order of their execution. This list is called wobzilla.changeLog.

Elementary node-operations are:

DOMNodeInserted

A node was inserted into the tree. Additional arguments:

argumenttypedescription
targetnodeThe node that was inserted
parentnodeThe parent-node of the node being inserted.
insertBeforenodeThe node, that follows the newly inserted node. This attribute is null if the node was inserted at the last position.

DOMNodeRemoved

A node was removed from the tree. Additional arguments:

argumenttypedescription
targetnodeThe node that was removed
parentnodeThe parent-node of the node being removed
removedBeforenodeThe node that had followed the removed node before it was removed. This attribute is null, if the removed node was the last child.

DOMAttributeChanged

The attribute of an element-node has been changed. Additional arguments:

argumenttypedescription
targetnodeThe element-node to which the attribute belongs.
attrNamenodeThe name of the attribute
newValuenodeThe new attribute-value
oldValuenodeThe old attribute-value

Creation of new wobzilla components

The most important goal of Wobzilla is an easy extensibility. Therefore the interfaces are kept simple, as the following example of integrating a new component shows.

The new component MyCheckBox should allow the manipulatation of boolean values. The configuration of MyCheckBox only specifies the data-node in the select attribute.

<wbz:container select="diplomarbeit">
  <wbz:view class="MyCheckBox" select="@abgegeben"/>
  .. 

The code for the MyCheckBox-component is short:

function MyCheckBox() {
    var _xpath; // xpath expression matching the model-node
    var _outputNode; // html-checkbox element
    var _model; // node in the data-model
    var _resolver; // wobzilla.xpath processor
    var _transaction; // wobzilla.transaction handler
    var _log;
    var _context;

    this.enableLogging = function(logger) {
        _log = logger;
    }
    this.contextualize = function(context) {
        _context = context;
    }
    this.service = function(serviceManager) {
        _resolver = serviceManager.lookup("wobzilla.xpath");
        _transaction  = serviceManager.lookup("wobzilla.transaction");
    }
    this.configure = function(config) {
        _xpath = config.getAttribute("select");
    }
    this.init = function(parentOutputNode,parentModelNode,insertAfter) {
        // create checkbox and append to output tree
        _outputNode = document.createElementNS(HTML_NS,"input");
        _outputNode.type = "checkbox";
        WBZ_insertAfter(parentOutputNode, _outputNode, insertAfter);

        // get model-node and set checkbox state to model-value.
        // This approach only works for attributes. See
        // code of WbzTextField for the handling of textnodes
        _model = _resolver.getNodeSafe(parentModelNode, _xpath, _context);
        _outputNode.checked = (_model.nodeValue == "true");

        // register for checkbox-click events
        _outputNode.addEventListener("click",this.validate,true);
    }
    this.requery = function(changeLog) {
        // update checkbox state on model change
        _outputNode.checked = (_model.nodeValue == "true");
        if (_log.debugging)
           _log.debug("new checkbox state: " + _model.nodeValue);
    }
    this.validate = function() {
        var newValue = ( _outputNode.checked)  ? "true" : "false";
        if (newValue != _model.nodeValue) {
            _transaction.start();
            WBZ_setNodeValue( _model, newValue);
            _transaction.commit();
        }
        return true;
    }
}

The integration in the Wobzilla redo/undo- and update-mechanisms takes place through the requery-method. If the data-node changes (caused by an user interaction or an undo) the requery method gets called and the CheckBox-state will be adjusted. To use MyCheckBox copy the file to js/views/MyCheckBox.js and add the following line in wobzilla.xul.

<script type="application/x-javascript" src="js/views/MyCheckBox.js"/>

Testing and debugging of components can be done with Venkman (the Mozilla-Debugger). Vekman also offers a very good Profiler.

Additional assistance is provided through the Wobzilla logging mechansim. There is a debug-menu, and you can use id-attributes in the configuration-files for context-sensitiv logging. This enables you, to differentiate the log output from a TextField containing the first-name from a TextField containing the last-name.


Christopher Kohlhaas Email: kohlhaas at users.sourceforge.net
Last modified: Wed Oct 15 20:01:01 CEST 2003