
- Home
- Magazine
- Conference & Seminars
- News
- Archives
- Forums
- Store
- Directory
- Editorial
- Advertising
- User/Login
- Contact



Camelot is an object oriented environment which has been built "from the ground up" by developers for developers. Camelot is designed from first principles rather than being based on other products, and it does more than making it easier to write graphical user interfaces.
The environment incorporates some significant new capabilities for the management of the complexity introduced by large system developments. One of these is an additional level of modularity which permits individual components to be created independently, then combined with other components to create a complete system (see Figure 1). The use of object repositories to store both objects and instance specific methods provides an additional degree of encapsulation, and its open architecture guarantees full interoperability with existing systems.
Camelot is supported by a comprehensive visual development environment. Nevertheless, as some of the concepts which we are going to discuss are best expressed syntactially, we begin with a background description of Camelot's Fire language.
Variables are accessed either normally (one := two), hierarchically (one += two.three.four) or indirectly (one.two -= <<"th"+"ree">>.four.<<"fi"+"ve">>). In the indirect form, any expression which results in a string value can be placed between the <<Ę>> bracketing. Fire implements an hierarchical variable name resolution scheme which searches for a variable first in the method, then in the object, etc. until it searches the global name space. These levels are method -> object -> connected object -> module -> global, and are discussed in greater detail below.
The code fragment below illustrates a several Fire principles.
01 #include "DCCDefinitions.D"
02 #ifndef DCC
03 # define DCC 5
04 #endif
05 method open( arg integer ) returning string
06 {
07 name string : initial value "DNX";
08 element object.dcc(ne);
09 if ( arg:assigned )
10 {
11 name := elements[ DCC, arg ].name;
12 }
13 if ( name:length >= 17 )
14 {
15 [ New(element) ConnectionAddFirstTo( self ) ];
16 }
17 return ( name );
18 }
Lines 01 to 04 show that Fire incorporates a full preprocessor which allows the usual #include, #define, #if(n)def etc. directives.
Line 05 is the standard Fire method definition line, in this case for a method called open with an argument called arg of type integer. The method returns a string value. All Fire methods run in the context of an object of a minimum class, i.e. the class in which the method is defined (the "method class") or one which inherits from the method class.
Lines 07, 09 and 13 show that Fire variables have attributes -in this case, an initial value, an assignment status and a string length. Assignment status (:assigned) can be both read and set, and is particularly useful because it prevents the use of a variable before it has been assigned.
Line 08 shows that Fire variables of type object can have a minimum class as defined above which further qualifies their data type. In this case, the minimum class of the element variable is dcc in module ne.
Line 11 illustrates access to a two dimensional array called elements. Any Fire variable can be defined to be an array of any number of dimensions.
Fire has standard procedural flow of control constructs, as illustrated in the code fragment below: if-else, while, for each, for ( ), break, continue and return.
if ( boolean )
{
while ( x < y ) {
for each element in collection {
for ( index := 0; : index < 10 : index += 1; ) {
if ( condition( index ) ) {
break;
}
else {
if ( !otherCondition ) {
continue;
} } } } }
return ( x >= y );
}
Fire also has an expanded form of the case statement and a full set of both logical and arithmetic binary and unary operators, as well as the tertiary ?: construct found in C.
case ( complexCalculation() )
{
= valueOne: {
x *= ( y + 3 ) % 4;
}
!= valueTwo: {
x = ( y & z ) | w;
}
~^ valueThree: {
x /= ( w < 1 ) ? -w : w;
}
> valueFour| x > y: {
}
default:
{ }
}
Methods in Fire are always executed in the context of an object, which is called the method object. "Sending a message to an object" is synonymous with "executing a method in the context of an object." Some examples of method invocation are shown below:
01 close( x.y, 142 + 857, subvalue() + 3.89 ); 02 close() : super; 03 [ otherObject close() ]; 04 [ findObject( name ) close() : if defined ]; 05 [ otherObject update(), display(), close() ];
Line 01 invokes the close method in the current object context, passing arguments x.y, 142 + 857, etc. Line 02 also invokes a close method in the current context, but the method it invokes is at a higher level of inheritance-the inherited close method.
Line 03 invokes the close method in the context of otherObject. In fact, it invokes the close method defined by otherObject or by its progenitors. Line 04 shows that the object context can itself be any expression which returns an object reference, and the if defined clause means that, if the close method is not defined, processing continues without error.
The construct shown in line 05 is a convenience; the update, display and close methods are all invoked in the context of otherObject. All return values except that of the last method invoked, in this case close, are discarded.
In addition to these syntactic constructs, the entire Camelot environment has been designed to support the object oriented paradigm, especially the principle of encapsulation.
startUpdate( delay integer, repeat integer, arg float ) {
[ window()
update( arg ) : asynchronous
, after delay seconds 0 ticks
, every 0 seconds repeat ticks ]
}
To understand instance specific methods we must start with an explanation of Fire's normal method dispatch mechanism (see Figure 3). Although this mechanism is optimized, it operates completely in accordance with the theory illustrated in the diagram below. All Fire objects reference a dispatch table which contains a list of methods in the order in which they were defined. The diagram illustrates an object of class C which inherits from class B which inherits from class A. The object in question has six methods defined: from the bottom up, Z, Y, super Y, X, super X and W. Let us assume that the object in this example receives a message Y. Its dispatch table is searched from the bottom up and the method defined by class C is executed.
Figure 4 shows instance specific methods Y, Z and Ø which have been added to the object in question. References to these methods are appended at the bottom of the dispatch table for the object, and when it receives a message Y it invokes the instance specific method, which is the first one found when searching the table from the bottom up. The first super invocation resolves to the method Y which was defined by class C, and the next to the method Y which was defined by class B.
Instance specific methods are a convenient way of implementing specific functionality which is only required by a single instance of an object. A good example of their use is in specifying the action of a button object on a panel, which in all probability is an action unique to that object. Of course, if the object is duplicated (e.g., to create a new copy of the window) so is the reference to its instance specific methods.
Camelot's object repositories are stored in both binary and machine independent forms, which means that any repository can be copied to any machine which Camelot supports. Camelot recognizes that the repository comes from a different machine and recreates its binary form, providing immediate portability.
This combination of object repositories and instance specific methods makes it possible to introduce new functionality without increasing the size of the environment as a whole. It also extends the benefits of encapsulation to a higher level by allowing the developer to ignore encapsulated functionality which does not pertain to the problem at hand. In addition, it makes distribution of new functionality a straightforward matter of copying a file from one system to another.
To circumvent this difficulty Camelot implements modules, which are higher level encapsulations of functionality. Camelot module definitions organize separate classes and methods into a self-contained unit with a well defined interface and a single objective, in much the same way that classes themselves organize data definitions and their associated methods. This means that a portion of a system can be developed and modified with a greater degree of independence.
We should note here that Camelot's philosophy is to use native mode controls (buttons, check boxes, etc.) whenever possible, although the user always has the option of using a control defined by Camelot. This means that, at the user's discretion, all buttons can look the same-or that a button can look like a Macintosh button on Macintosh computers, a Windows™ button under Windows, etc. This is an option settable both as a user preference and on a control by control basis.




