DejaVu -- a component-based approach to hypermedia

Jacco van Ossenbruggen & Anton Eliëns
Vrije Universiteit, Department of Mathematics and Computer Science,
De Boelelaan 1081, 1081 HV Amsterdam, The Netherlands
email: jrvosse@cs.vu.nl eliens@cs.vu.nl, fax: +31.20.4447653
www.cs.vu.nl/~jrvosse/ www.cs.vu.nl/~eliens/

abstract

Object-technology offers promising means to cope with the problems that occur when developing complex programs. The DejaVu project employs an object-oriented approach to formulate a framework for the development of distributed, intelligent hypermedia systems. The result of our efforts is a collection of libraries and tools providing the functionality needed for developing hypermedia applications and multimedia user interfaces. In this paper an overview is given of the components constituting the DejaVu framework, that is the hush (hyper utility shell) library and its extensions, offering a variety of GUI widgets, basic hypertext facilities, support for discrete event simulation, facilities for sound synthesis and high level music description languages, digital video and a connection to the World Wide Web. As an example, we will show how to extend our web browser to allow for embedding a discrete event simulation program in a collection of web pages. Also, we will discuss some issues of the design and implementation of our framework. Finally, we will briefly summarize the lessons learned and indicate the direction of our future research.

Introduction


introduction, concepts, components, examples, patterns, experience, conclusions, references
The DejaVu project at Software Engineering department of the Vrije Universiteit (Amsterdam) originated from the wish to offer graduate and undergraduate students the opportunity for projects that fit within a longstanding research effort concerning OOP and hypermedia.

The DejaVu project aims may be characterized by a number of buzz-words, familiar no doubt to all those who have had the privilege to write project proposals themselves: The DejaVu framework is intended to be open (in the sense of extendible by a variety of other software), heterogeneous (that is allowing for heterogeneous components, possibly developed within different software paradigms), distributed (which means operating on both LAN and WAN computer configurations), object-oriented (which is intended to characterize our approach at software development, including modeling and programming), and intelligent (which may, lacking a better definition, be characterized as allowing for a declarative approach to the knowledge-level aspects of the application). Wide in scope, and certainly ambitious, but on the other hand (as argued in  [Eliens94]) focussing on a number of interesting research issues in OOP and as such clearly of interest to students, in particular when taking into account the emphasis on multimedia and hypermedia systems.

The DejaVu project has, in a period of about three years, resulted in a collection of software components that allow for building quite complex systems in a relatively easy way. To substantiate this claim, we may remark that our software has succesfully been used over the last years in CS2 project assignments for Software Engineering, ranging over the realization of routeplanners, product/components control systems, business simulation models and (simple) geographical information systems. It has also been used, at a more advanced level, in OOP assignments, including the development of gambling machines (mimicking existing 'one armed' bandits), musical score editors (with soundsynthesis facilities) and interactive games (such as those you may find in a video game hall). Ease of use, that is a natural and simple class interface, is enforced (to the extent possible) as an explicit design goal for all components that constitute the DejaVu framework, now and in the future.

In this paper we will discuss the organization and modeling principles underlying the hush library and its extensions. The acronym hush stands for hyper utility shell and reflects our intention to provide the means for incorporating 'hypertechnology' in a variety of applications. The acronym hush is also intended to reflect our wish to allow each program developed with hush to act as a shell that interprets scripts written in a general purpose script language that is extended with functionality defined by the application itself.

A central theme in the DejaVu project has been to support a multi-lingual software development platform, allowing in particular for a close coupling between C++, the distributed logic programming language DLP  [Eliens92] and a variety of script languages (including Tcl).

The extensions of hush include support for discrete event simulation (sms), facilities for realtime soundsynthesis and music (hymne), support for digital video (xanim) and facilities to connect to the World Wide Web (web), also allowing to extend the web browsing facilities with client-side computing.

With respect to the area of hypermedia systems, we may remark that our results compete with developments undertaken by the big players in the field (such as the Java project by Sun Systems), yet our approach is fundamentally different to the extent that we are not interested in one particular application but rather in an approach to software development that allows for employing a variety of components, in a multiparadigm fashion.

Basic concepts -- requirements


introduction, concepts, components, examples, patterns, experience, conclusions, references
The problem addressed in the DejaVu project is essentially to provide support for the software engineering of (moderately complex) applications that require, apart from other functionality, multi-media interfaces and associative online help. Such support is primarily offered in the form of class libraries, most of which are written in C++. The language C++ was chosen for pragmatic reasons, since it allows for easily wrapping public domain software written in C in classes with a more user-friendly interface. (Another reason was that C++ seemed to be the right vehicle for programming assignments, with regard to its ever more widespread use.)

Application development generally encompasses a variety of programming tasks, including system-level software development (for example for networking or multimedia functionality), programming the user interface (including the definition of screen layout and the responsivity of the interface widgets to user actions), and the definition of (high-level) application-specific functionality. Each of these kinds of tasks may require a different approach and possibly a different application programming language. For example, the development of the user interface is often more conveniently done using a scripting language, to avoid the waiting times involved in compiling and linking. Similarly, defining knowledge-level application-specific functionality may benefit from the use of a declarative or logic programming language.

In our project, we decided from the start to support a multiparadigm approach to software development and consequently we had to define the mutual interaction between the various language paradigms, as for example the interaction between C++ and a scripting language, such as Tcl. Current scripting languages, including Python and Tcl, provide facilities for being embedded in C and C++, but extending these languages with functionality defined in C or C++ and employing the language from within C/C++ is rather cumbersome. What we need is a simple and generic mechanism to associate script commands with actions defined by the application and (inversely) a generic way to employ scripting facilities in the application.

Embedding script interpreters

The hush library provides a solution to the latter problem by defining a generic kit that may be used as the interface to any embedded interpreter. (Currently, however, Tcl is fully supported and Python only partially.) The public interface of the kit class looks as follows:
  interface kit {

	void eval(char* cmd);
	char* result();

	void action(char* name, handler* h);
   };
The function eval is used for evaluating (script) commands, and result may be used to communicate data back. The limitation of this approach, obviously, is that it is purely string based. In practice, however, this proves to be flexible and sufficiently powerful. The action function may be used to define new commands and associate it with functionality defined in handler objects, that will be introduced soforth.

Handler objects

The former problem, of extending the script language with functionality defined by the application, is (as already indicated above) addressed by defining a generic handler object class. Handler objects may be regarded as a generalization of callback functions, in the sense that they are activated whenever the corresponding script command is evaluated. The advantage of using objects for callbacks instead of functions, obviously, is that we no longer need type insecure casts, or static or global variables to pass information around. The public interface of the handler class looks as follows:
     interface handler : client {
  	int dispatch( event* e );   
to dispatch event

int operator()(); };
The dispatch function is called by the underlying system. (In effect, a standard callback function is used with the handler object as client data, which explains why the handler object inherits from the class client.) The dispatch function receives a pointer to a sytsem-defined event which encodes the information relevant for that particular callback. In its turn dispatch calls the application operator. Classes derived from handler need only redefine the operator() function. Information needed when activating a handler object must be provided when creating the object, or obtained from the event for which the handler is activated.

The use of handler objects is closely connected to the paradigm of event-driven computation. An event, conceptually speaking is an entity that is characterized by two significant moments, the moment of its creation and the moment of its activation, its occurrence Naturally, an event may be activated multiple times and even record a history of its activation, but the basic principle underlying the use of events is that all the information that is needed is stored at creation time and, subsequently, activation may proceed blindly.

User actions

Another use of handler objects (in hush) is for defining the actions that must be taken in response to user events, resulting from actions such as moving the mouse, or pressing a button, or selecting an entry from a menu. This is illustrated by the public interface of the generic widget class:
   interface widget : handler {
	...
	void bind( handler* h );
	void bind( char* user, handler* h );
	...
   };
The first member function bind may be used for installing a handler for the default bindings of the widget, whereas the second bind function is to be used for overriding any specific bindings. (Notice that the class widget is derived from handler class to allow the widget to be its own handler. In this way inheritance or the delegation to a separate handler object may be used to define the functionality of a widget.)

In addition to the widget class, the hush library also provides the class item, representing graphical items. Graphical items, however, are to be placed within a canvas widget, and may be tagged to allow for the groupwise manipulation of a collection of items, as for example moving them in response to dragging the mouse pointer.

User-defined events

User interface events occur in response to actions by the user. They are scheduled by the underlying window system, which invokes the handler whenever it is convenient or necessary. When getting used to event-driven computation, system designers and programmers may feel the need to have events at their disposal that may be scheduled at will, under programmer's control. It will come as no surprise that another use of handler objects is to allow for user-defined events. The public interface of the class event looks as follows:
   interface event : handler {
	operator()();
   };
Actual event classes are derived from the generic class event, and a scheduler is provided to activate events at the appropriate time. (In effect, we provide a fully functional discrete event simulation library, including facilities for generating random distributions and analysing the outcome of experiments.) Note that there is an important difference between user-defined events and system-defined events. System-defined events are delivered to the user by activating a handler callback. In contrast, user-defined events are (directly) activated by a scheduler. They contain, so to speak, their own handler.

Discussion

What benefits do we derive from employing handler objects. One advantage is that we have a uniform way to define the functionality of script commands, callbacks to user actions and programmer controlled events. Another, less apparent advantage, is that it allows us to incorporate a variety of functionality (including sound synthesis facilities, digital video and active documents) in a relatively straightforward fashion, of which we will give an illustration later.
introduction, concepts, components, examples, patterns, experience, conclusions, references

Components -- an overview


introduction, concepts, components, examples, patterns, experience, conclusions, references
Having delineated the basic concepts around which our libraries have been constructed, we will present an overview of the individual component libraries constituting the DejaVu framework. In the next section, we will then give an example illustrating how the various components interact when used in combination.

Components



slide: Components

The hush API


introduction, concepts, components, examples, patterns, experience, conclusions, references
As explained in the previous section, the hush library contains the classes providing the interface to the embedded script interpreter(s) and the classes allowing for a smooth interaction with the underlying window system. (Currently, only X-windows is supported. An MS-windows implementation is on its way.)

The hush library contains the classes kit, event, handler, widget and item. In addition to these, hush provides the class session. Application programmers are minimally required to derive a class, say application, from session and redefine the function main. When the program is to be used as a script interpreter, the function prelude may be redefined to allow for initializing external packages or other components of the DejaVu framework.

As a comment, the hush library originated as a collection of classes giving the C++ programmer convenient access to the Tcl/Tk window programming toolkit. Its design has been inspired by the Interviews library. However, although less powerful wit respect to device-independent graphics, its class structure is less complex and, as experience shows, much easier to employ by undergraduate students. In addition, it offers a rich collection of user interface widgets.


introduction, concepts, components, examples, patterns, experience, conclusions, references

User interface widgets


introduction, concepts, components, examples, patterns, experience, conclusions, references
The (hush) widget library contains classes for a variety of user interface widgets, including the classes button, menu, text, canvas, filechooser and many more. As explained before, a widget allows for binding an independently defined handler to user actions. This may result in a code fragment like:
  button* b = new button(".b");
  handler* h = new help_handler(b);
  b->text("Help");
  b->bind(h);
We assume that help_handler does something meaningful, like displaying a hypertext help file.

Alternatively, a widget may declare itself to be its own handler. In that case we derive a new class from button and define the functionality (previously defined externally in the help_handler) in the new class itself, as illustrated below:

  class help_button : public button {
  public:
  help_button( char* path ) : button( path ) {
	this->text("help");
	this->bind(this);
	}
  int operator()(); // does what help_handler did
  };
Dependent on circumstances, either one of the approaches may be the most convenient. Allowing for delegating to an external handler helps to avoid cluttering the class name space. On the other hand, employing inheritance may result in a significant reuse of initialization code.
introduction, concepts, components, examples, patterns, experience, conclusions, references

Basic hypertext functionality


introduction, concepts, components, examples, patterns, experience, conclusions, references
One of the components extending the hush widget library is a hypertext widget offering basic hypertext functionality by allowing the programmer to embed (Tcl) script code in plain text. A hypertext widget, as any other widget of the hush widget library, can be created from an absolute path name (as in Tk) or, relative to a parent widget, with a path extending the path of the parent widget. The hypertext widget allows for a number of configuration commands, for setting font size, background, foreground and the like.
introduction, concepts, components, examples, patterns, experience, conclusions, references

Software sound facilities


introduction, concepts, components, examples, patterns, experience, conclusions, references
Sound facilities are an essential ingredient of a framework supporting multimedia. Sound may be produced from a stored audio file or, provided that real-time sound synthesis facilities are available, from some high level representation in a sound or music description language. Apart from a reduction in the storage capacity needed (and correspondingly, the network traffic load in a distributed environment), employing a high level music/sound description language has as an additional advantage that improved synchronization and linking facilities may be offered.

The DejaVu framework supports both MIDI and the sound synthesis facilities offered by Csound [XX]. When creating an instance of the icsound class a process is created which is capable of receiving sound events, as defined by the Csound numerical score language, and converting these events to data for the audio device. (The icsound class inherits from a wrapper class providing the functionality for spawning of a new process that communicates with the original process via pipes.) Care is taken that the audio device is connected only once. However, multiple instances of the icsound class may exist.

In addition to the icsound class, which accepts only low level sound events, instances of player may be used which accept musical events defined in the high level music description language Scot, that comes with Csound. The player class is also an event. This allows instances of player to be embedded in scripts, as well as being activated as user-defined events.


introduction, concepts, components, examples, patterns, experience, conclusions, references

Digital video


introduction, concepts, components, examples, patterns, experience, conclusions, references
Digital video may be considered as another standard ingredient of hypermedia and multimedia framworks. To employ digital video, our framework offers the class video. Apart from the usual widget creation commands, the class video has functions for reading in a video file, to play (forward) and to rewind. In addition, it offers a function to bind a framenumber to an event. Binding framenumbers to events allows for both synchronization and hyperlinking, albeit in a low level fashion.

Our video extension is based on the public domain xanim package and includes support for MPEG, AVI and Quicktime.


introduction, concepts, components, examples, patterns, experience, conclusions, references

Connecting to the WWW


introduction, concepts, components, examples, patterns, experience, conclusions, references
In addition to the basic hypertext facility mentioned before, the DejaVu framework also provides support (in the form of the web widget) for hypertext files written in HTML. The web widget displays HTML files and allows for following links defined by URLs. In addition to the standard widget functions, it offers a function loadhome, to load a home page. In a similar way as the basic hypertext widget, the web widget allows for embedding (Tcl) script code, by using the special (non-HTML) tag hush, as illustrated below:
 <h1>Digital video for HushWWW</h1>
 <hush tcl=source [urlfile www.cs.vu.nl/~se/mpeg.tcl>;
 You need the hush browser to play this MPEG demo 
 </hush>
The actual script code itself may be obtained from some site by using the special command urlfile. The text inbetween the hush begin and end tag will be displayed by 'ordinary' browsers, such as Mosaic and Netscape. Evaluating the script code may, similar as for the hypertext widget, result in packing additional widgets to the browser (widget), as for example a video widget showing an mpeg movie.
introduction, concepts, components, examples, patterns, experience, conclusions, references

introduction, concepts, components, examples, patterns, experience, conclusions, references

Example -- simulation on the Web


introduction, concepts, components, examples, patterns, experience, conclusions, references
As an immediate benefit of our component-based approach, client-side extensions to the Web, such as inline MPEG, interactive games and music synthesis  [WWW95b] can be easily realized. See  [DoornEl95b] for more illustrations and a discussion of safety issues.

Embedding simulations

From the perspective of our simulation package, we can use client-side computing techniques to provide hypermedia interfaces for existing simulation applications. For instance, in figure Dining, a simulation of the "Dining Philosophers" problem has been embedded into an HTML page to demonstrate our multi-lingual approach.


A simulation embedded in two HTML pages

Figure Dining shows the corresponding HTML markup for the first page. We use two <app> tags to include inline applets written in Tcl/Tk. Note that text inside the open and close tag will be ignored by our browser and may be used to display warnings if the page is browsed by non-hush browsers as Mosaic or Netscape.

  <title>The Dining Philosophers<title>
  <h1>The Dining Philosophers<h1>
  <h2>The problem<h2>
    Five philosophers sit around a table, with five chopsticks in between.
    ...
    We are interested in <a href="results.html">the percentage of the time<a> a 
    philosopher actually thinks. 
  <app class="sim-setup">
    Error: Dining table deleted: use our hush browser!
  </app> <hr>
  Press this button: <app class="sim-button">
    Error: Run button deleted: use our hush browser!
  </app>

Since hush programmers can easily define Tcl interfaces for functionality provided by C functions or C++ classes, applications written in these languages can be embedded in Web pages as well.

The Tcl/Tk applets used in the example above are in fact only responsible for the graphical interface, the simulation itself is modeled in C++, by refining the entity class (modeling a philosopher) and the resource class (modeling a chopstick) of the simulation library.

The simulation is initialized by scheduling five philosopher entity objects in a "waiting" phase at time t=0.0. The "simulation init" command will be executed by first applet. The simulation can be started by executing the "simulation run" command, which is bound on a push button by the sim-button applet.

When a philosopher is due to be activated, the scheduler will call its application operator which will perform an explicit dispatch on the current phase:

  int philosopher::operator()() {
      switch (phase()){
        case EATING   : eat();   return OK;
        case THINKING : think(); return OK;
        case WAITING  : await(); return OK;
        default: return FALSE; // undefined phase
      } 
  }

Suppose the philosopher is still in a waiting phase. The await() member will be called and the philosopher will see whether his chopsticks are available. If so, she will acquire them and starts eating. If not, the philosopher will remain in a waiting state, but is put on the scheduler's list of conditional events.

  void philosopher::await() {
    if (chopsticks_available()) {
        acquirechopsticks();
  
        phase(EATING);
  
    } else if (!conditional()) {
        waitonchopsticks();
        sim->hold(this);
    }
  }

However, if the philosopher is invoked while she is in a eating phase, she will continue with eating for some (exponentially distributed) random time interval after which she will move to a "thinking" phase.

  void philosopher::eat() {
    double t = gen->exponential(EAT_TIME);
    phase(THINKING);
    sim->wait(t);
  }

Multimedia Scheduling

From the perspective of our hypermedia framework, we benefit from the simulation package by having an efficient scheduler at our disposal and hence a convenient way to employ application-defined events.

In addition, we extended the simulation package with a soft-real time option, that allows us to schedule clock-synchronized events. For example, we implemented a simple slide show, by the repetitive scheduling of HTML pages with a fixed time interval.

However, general hypermedia applications require support for far more complex synchronization relations and temporal dependencies between the components of a hypermedia document. Such high-level temporal alignment cannot be (easily) expressed using the basic simulation primitives, nor can the corresponding documents be expressed by (text oriented) markup languages as HTML.

Therefor, we are currently developing components supporting more complex scheduling primitives. One of our goals is to add a (subset of) the event scheduling mechanisms of HyTime [REFXXX] to our new, SGML-based Web widget.


introduction, concepts, components, examples, patterns, experience, conclusions, references

Implementation and design -- patterns


introduction, concepts, components, examples, patterns, experience, conclusions, references
In the design and realization of our framework, we have employed a variety of strategies and techniques that now have commonly become known as design patterns and idioms. With reference to the catalogue presented in  [GOF94], we may remark that we employed both the Factory and Singleton pattern in a number of cases, for example in realizing the kit class (to be able to select an appropriate interpreter in a flexible way, and to enforce that there will be only one instance of the interpreter selected). Although we do not wish to claim that we have developed any new patterns, our approach with respect to the realization of patterns differs from approach usually followed in its emphasis on abstract classes, its use of explicit delegation and the employment of what we may call 'virtual' self-reference.

Choosing an interpreter

The envelope/letter idiom (originally introduced in  [Coplien92] is increasingly adopted as a standard approach to separate the definition of a high level interface and its realization by an implementation class. Usually, this involves two classes, say an abstract class A and its implementation class AImpl. A disadvantage of this approach is that an additional (implementation class) is added to the class (name) space. Another disadvantage is that refining the behavior of a class using inheritance becomes more cumbersome.

To illustrate our approach, look at the definition of the class kit:

  class kit {
  public:
    kit( char* kind = "tcl" );

    virtual int eval(char* cmd) { return _kit?_kit->eval(cmd):0; }
    ...
  protected:
    kit( kit* );
    kit* _kit;
  };
The constructor of kit indicates as a default a Tcl interpreter which is encapsulated by the class:
  class tcl_kit : public kit {
  public:
    tcl_kit() : kit(0) { ... }
    ...
  };
The constructor of kit assigns the tcl_kit instance to its _kit variable. In contrast, the tcl_kit initializes its (parent) _kit variable to zero. For the kit instance created by the user, calls are delegated to the tcl_kit instance. However, the user may still inherit from kit and selectively override its functions.

Multiple perspectives on events

One of the problems encountered in developing our framework is that the same class name may be claimed by multiple components. A notable example is the (class) name event, that applies both to the events generated by the (X) window system and to user-defined events. Instead of definign two event classes (such as xevent and simevent) to distinguish between the different notions of event, we decided to apply a similar technique as for the kit class and offer a single class event that may be viewed from multiple perspectives. The class definition of event looks as follows:
  class event : public handler {
  public:

    event( event* x ); // expects x event
    event( );          // creates sim event

    // X event interface

    virtual int type() { return _event?_event->type():0; }

    virtual int x() { return _event?_event->x():0; }
    virtual int y() { return _event?_event->y():0; }

    // simulation interface

    virtual void timestamp();
    virtual double timespent() { return _event?_event->timespent():0; }

  protected:
    event* -event;
  };
The disadvantage of this approach, clearly, is that we need to offer a fat interface (which could in principle be factored using multiple inheritance, at the expense of additional classes).

The (public) constructor event::event(event*) is used to encapsulate events generated by the (X) window system. An advantage of this approach is instance may be accessed via both pointer and object references, without sacrificing dynamic binding. (To our taste, it is more natural to write e.x() than e->x() when accessing the x coordinate of the mouse pointer stored in an event instance.)

For events that are to be scheduled under programmer's control, a class must be specified inheriting from event (overriding the operator() activation function). In that case a simulation event is created containing the additional data structures needed for bookkeeping and scheduling.

Virtual self-reference


introduction, concepts, components, examples, patterns, experience, conclusions, references
A special feature of the hush widget class library is its support for the definition of new composite widgets, which provide to the application programmer the interface of a built-in (or possibly other composite) widget. To realize this flexibility, we introduced a self() function that adds another level of indirection to self-reference. For example, we may define a drawtool widget, which provides for both the C++ programmer and the (Tcl) script programmer the interface of an ordinary (built-in) canvas:
  class drawtool : public canvas {
  public:
    drawtool() : canvas() { }     // to declare drawtool
    drawtool(char* path) : canvas() {
	initialize( path );             // constructs the components
	redirect( _canvas );      // delegate to _canvas component
	}
    int operator()() {              // interface to Tcl
	if ( !strcmp("drawtool",argv[1]) return create(argc, argv);
	else return self()->eval( flatten(argc, argv) );
	}
  private:
    void initialize( char* path ); 
    int create( int argc, char* argv[] );
    canvas* _canvas;
  };
For completeness, the first constructor is only used to define the script command drawtool as in
  tk->action("drawtool", new drawtool() );
which may occur in main or prelude of the application class.

The call redirect(_canvas) in the regular constructor results in delegating both member function calls and script commands addressed at the drawtool widget to the canvas widget pointed to by _canvas, which is assumed to be created in initialize. The _canvas widget acts, so to speak, as the primary component of drawtool in that it receives all bindings and (configuration) commands. Actual redirection occurs by calling self(). The definition of widget::self() is simply:

  widget* self() { return _self?_self->self():this; }
In other words, it checks the private _self variable of the widget, which may be given a value by widget::_redirect, until _self is undefined (zero), in which case the this pointer is returned. In this way primary widget components can be embedded at arbitrary depth within a composite widget, without any need to adapt the interface of the embedding (composite) widget.

Those well-versed in design patterns will recognize the Decorator patterns (as applied in the Interviews MonoGlyph class  [Interviews89]).


introduction, concepts, components, examples, patterns, experience, conclusions, references

Practice and experience


introduction, concepts, components, examples, patterns, experience, conclusions, references
In developing and maintaining or framework we encountered a number of problems, having to do with platform/compiler dependencies, configuration management and documentation.

Platform/compiler dependencies

The software was originally developed under SunOS 4.1 on a Sun Sparc workstation with the AT&T 3.0.1 compiler. Our first problem was to develop a version for the GNU g++/gcc compiler. As an example, it appeared that the GNU compiler did not allow for a construct like:
    class handler { ... }
  
    class widget : public handler {
    public:
      ...
      void handler( class handler* h );  // to install a default handler
      ...
    };
  
Consequently, we had to rename the widget::handler function into widget::installhandler. However, we decided to overload the widget::bind function to allow for installing a default handler.

Much time was spent in finding ways to satisfy both the GNU compiler and the AT&T compiler (and later the Sun CC 4.0 compiler). A situation which is unfortunately still characteristic for working with C++. Much time was also spent in adapting to differences in include files and linking conventions when moving to Solaris. We deal with different platform and compilers now by providing configuration set up files, trying to avoid as much as possible to clutter our code with conditional compilation macro instructions.

Configuration management

Apart from compiler and platform dependencies, users/installers will generally differ in their preference of what libraries and tools the want to have installed. (Including the various public domain libraries that we use, such as for example the QvLib and MesaGL libraries for our most recent VRML extension, the DejaVu source code is wll over 50 megabytes!) To deal with this problem we organised our software in levels to allow users/installers the choice whether to include a group of libraries when installing the framework. In addition, for each level the installer may indicate which particular libraries she wants to have installed. The levels distinguished include: This approach allows for managing different versions of the software packages in a fairly easy way, by modifying the appropriate configuration file. We decided not to use the GNU autoconfigure program program since it proved to be less flexible for managing user preferences. However, many of the packages (including Tcl/Tk and its extensions) do provide their own configuration programs to deal with platform and compiler differences. In addition, we do provide the possiblity to run a configuration program giving advice on how to define the configuration setup file.

Documentation support

An important aspect of framework development is the development of (and maintainance of) appropriate documentation. Users/developers need documentation to know how to use the (framework) classes in their programs and contributors need to know the requirements and constraints their software must satisfy to fit within the framework. Apart from user guides and manuals, we decided to document the various packages by employing literate programming techniques and to allow for looking at annotated include files, examples and selected parts of the source code with a standard Web browser. We use a code to HTML filter from the tools level for this. In the end we wish to provide documentation of our framework that also provides animated examples and demos when viewed with an appropriate (read: our own) Web browser.
introduction, concepts, components, examples, patterns, experience, conclusions, references

Conclusions


introduction, concepts, components, examples, patterns, experience, conclusions, references

Another question that may arise is how the notions of handlers and events are related to a hypermedia document model. The answer must be, only in a very loose fashion, that is to the extent that we need (and support) embeddable anchors and (in the future) a linkbase facility to store and retrieve associative links.

For developing actual hypermedia systems we need to provide support in the form of actual widgets (including a hypertext widget) and, equally important, guidelines and patterns for developing applications. In addition, we have to provide adequate documentation support.


introduction, concepts, components, examples, patterns, experience, conclusions, references

introduction, concepts, components, examples, patterns, experience, conclusions, references
Sim -- a C++ simulation library, IR-367, Amsterdam, Nov 29- Dec. 1 ERCIM W4G, Paris Feb. 8-10 Technology, Tools and Applications, April 10-14, 1995, Darmstadt DLP - A language for distributed logic programming, Principles of Object-Oriented Software Development, Hush -- a C++ API for Tcl/Tk, Music in Time-based Hypermedia, pp. 224-227 Distributed Multi-Paradigm Objects, L'Alpe d'Huez, April 3-7, 1995
introduction, concepts, components, examples, patterns, experience, conclusions, references