The Aspect Definition Language (ADL) is a language for defining the classification, attributes, relationships and values of arbitrary data objects.
Every object in ADL is derived from another object, except the builtin Object which is the root of the derivation tree. ADL differs from most data definition languages in making no distinction between object instances and object classes; any object may be derived from any other object, thus any object may be treated equally as a class or an instance. Derivation is single; an object may have only a single base object (superclass in conventional terminology) - this restriction may be removed in later revisions of the language.
Any object may contain (be a parent of) any other object, almost without restriction on the types (classes) of the child objects. Containment has no depth restrictions. An object inherits (is regarded as containing) all the child objects of its base object recursively as well as its direct children. All objects have a parent, except the predefined Origin object.
A predefined Object called Variable has a set of predefined subclasses, each of which has an associated value notation. In future revisions of this language, syntax methods will specify notations for new Variable base types. Any object may contain Variables (children derived from Variable). All objects inherit some Variables from the predefined base Object class. Some types of Variables themselves contain Variables; they are parameterised.
Each Variable belonging to an object (including by inheritance) allows the object to also contain a single Assignment object for that variable. If no Assignment exists nor is inherited for a Variable belonging to a given object, the value of that Variable is undefined for the object. If an Assignment is inherited, a local assignment overrides it.
An object may contain References, which are derived from the Reference object. References associate the objects derived from the parent object with the derived instances of another object. Two syntaxes are used, declaring associations as one-or-one or one-to-many of the referent object. Depending on usage, a Reference may be regarded as an index or a conventional association - these distinctions are elaborated below. Future revisions of the language will define additional characteristics of References, including support for Methods.
Finally, an object (including Variable, Assignment or Reference) may be defined as extending any other object. That is, it may declare that from its point of view (aspect or context), an extended object has additional children. The added children are hidden except when viewed from an aspect which includes (is or inherits) the aspect that defined the extensions. Assignments to hidden variables are hidden.
When viewed from the aspect where it is declared, a contextual assignment overrides the value of a variable outside that aspect. For example, an object may defined a textual message in the English language. When the object is queried from a German aspect, a local assignment may override the message with one written in German.
Any object may be extended. For example, an application may require that a documentation string be allowed for any Object. The application can declare a documentation Variable within (pertaining to) a Documentation aspect, so that normal queries of Objects disregard the documentation values.
In addition to the above, an Object contains a Name variable which may optionally be assigned. All the above listed features of ADL Objects are then supported by the following Variables of Object:
Both C and C++ style comments may be used.
A new object is declared by saying its name and its superclass separated by a colon, and followed by a semicolon or a list of children:
Customer : BusinessPartner;
The name BusinessPartner must resolve to an object that already exists in the local or an enclosing scope. The base class may be omitted, and defaults to Object:
BusinessPartner:; // BusinessPartner is an Object
If an object will never need to have a name, there's no reason it should. Objects that are primarily values are often in this class:
: Customer
{
ContactName = "Richie Rich";
Discount = "35";
}
A list of children is enclosed in braces { }:
Customer : BusinessPartner
{
ContactName : String;
Id : Integer;
}
An individual child may be referenced or created by the dot notation:
Customer.ContactAddress : String;
The syntax used here re-opens the scope of the Customer object, and adds ContactAddress within that object, not as an extension. An alternate syntax allows adding variables as extensions instead, see below.
A child may have a name the same as an identifier in another scope, including its type, for example:
GUID : String;
RouterNode : Object
{
GUID : GUID;
}
This looks a bit like cheating; which GUID is which? It works because the base class search will disregard the object being declared (it hasn't been created yet), so will find the nearest definition, but it's preferable in this case (and sometimes necessary in others) to qualify the name:
GUID : String;
RouterNode : Object
{
GUID : .GUID;
}
The point . declares that the variable GUID derives from another GUID which is visible from global scope (probably because its parent is the predefined Origin object). Similarly:
Definitions :
{
GUID : String;
}
RouterNode : Object
{
GUID : Definitions.GUID;
}
The name Definitions is here found by looking through the class and base class contexts of each lexically enclosing context in the obvious way, and then the definition of GUID is found within the Definitions object.
A final special case exists for a new variable of the same name as its base object, the colon and base object name may be omitted if no children are declared (eponymous naming):
DNSName : String { MaxLength = 64; }
Host : Computer {
DNSName; // Host has a variable called DNSName of type DNSName
}
A variable belonging to an object may be assigned a value using either the weak assignment operator ~ or the final assignment operator =. The assignment may be included in the declaration of the variable, or it may stand alone:
BusinessPartner:
{
Id : Integer;
}
Customer : BusinessPartner
{
ContactName : String;
Discount : Integer ~ 0; // Discount defaults to 0
}
TradeCustomer : Customer
{
Discount = 15; // trade customers always get 15% off
}
An assignment is an object declaration; it defines an Assignment object. The statement above which defines the Discount variable also defines an initial assignment (two objects created by the one statement). No object may have more than one assignment to a variable, nor to a variable for which it inherits a final assignment. The Assignment object has the following definition:
Assignment : Object
{
Variable -> Variable; // Reference to the variable assigned, see below
Value : String; // String representing the value assigned, in appropriate notation
Final : Boolean; // Assignment may not be overridden
}
| Variable : Object; | // The base class for all variables |
| Character : Variable; | // UCS4 character (32 bit) |
| String : Variable; | // Array of UCS4 characters |
| Number : Variable; | // The base class for all numeric variables |
| Short : Number; | // 16-bit integer |
| Integer : Number; | // 32-bit integer |
| Long : Number; | // 64-bit integer |
| Real : Number; | // 64-bit IEEE floating point |
| Decimal : Number | // Exact decimal |
| { | |
| Size : Short; | // Number of significant digits |
| Precision : Short; | // Number of those digits after the decimal point |
| } | |
| DateTime : Variable; | // Date and Time or Time only, after ISO8601 |
| Enum : Variable; | // Enumeration, value is the name of a child variable |
Boolean : Enum
{
False : Integer = 0;
True : Integer = 1;
}
It is anticipated that a special syntax for defining enumerations will be defined in future.
Any object's context may be re-entered to add new variables and sub-objects, for example the definition:
Customer : BusinessPartner
{
ContactName : String;
Discount : Integer ~ 0;
}
has an identical meaning to both the following:
Customer : BusinessPartner;
Customer // Re-open Customer
{
ContactName : String;
Discount : Integer;
}
Customer
{
Discount ~ 0;
}
and
Customer : BusinessPartner; Customer.ContactName : String; Customer.Discount : Integer; Customer.Discount ~ 0;
This is useful to provide forward declaration. Note that only the first definition of Customer may provide a base object.
It's also possible to extend system objects in this way:
Object
{
Comment : String;
}
or the shorter way of saying the same thing:
Object.Comment : String;
provides a new Comment variable, available for assignment on any Object.
Customer
{
Order.Status : String;
}
This states that from the point of view of Customers (or anyone using the Customer's point of view), any Order has a Status, but these Status assignments are not relevant to (or visible from) any other context.
Values may be assigned contextually as well, so for example, a German context object may be defined which overrides value assignments containing English language strings:
OperationNames:
{
Insert : String ~ "Insert";
Delete : String ~ "Delete";
}
German
{
OperationNames {
Insert ~ "Einfügen";
Delete ~ "Löschen";
}
}
Note that the OperationNames object has been re-opened outside its original context, so the added objects form a contextual extension.
When the object OperationNames is interpreted in the German context, the German values will apply, overriding the default values. This demonstrates why Assignments are treated as objects; they also are inherited and their values may be overridden. If no Assignment is visible for a given Variable, the variable has no value.
Objects may refer to each other by either 1-to-1 or 1-to-many references, which are declared using the operators -> and => respectively. To the right of the operator is the name of the target object or referent, of which an instance or instances may be reached using this reference. To the left of the operator is the reference name if any.
The target object name may be followed by a list of variables enclosed in square brackets and separated by semi-colons. The variables must exist in the referent. In the case of a 1-to-1 reference, the instances are expected to have a distinct set of values for the variables. References thus serve the purpose of both indices and foreign keys in conventional systems.
PartyId : String;
BusinessPartner:
{
PartyId; // Define a new PartyId variable of type PartyId
}
->BusinessPartner[PartyId]; // All BusinessPartners have distinct PartyIDs
Customer : BusinessPartner
{ // definition left blank for this example
}
SalesRepresentative:
{ // definition left blank for this example
}
Order:
{
OrderNumber : Integer;
->Customer; // Each order is for one customer
->SalesRepresentative; // and was credited to one Sales Rep.
}
OrderItem:
{
OrderNumber : Integer;
->Order;
->Product;
}
Order
{
=>OrderItem[OrderNumber]; // All items for this order.
}
SalesRepresentative
{
Accounts => Customer; // Each Rep handles a number of accounts
}
References may also contain child objects, which are particularly used to control the behaviour of underlying implementations. Just follow the reference with an open brace and declare the children - or do it in an implementation context:
Microsoft.SQL.Server { // Add extensions for this context
->BusinessPartner { // Re-open the reference for contextual extension
PrimaryKey = True; // Use the Primary Key attribute for this index
}
}
Note that the variable list should not be re-specified when re-opening a reference.
To recap and (slightly) extend, here are the builtin objects in ADL:
Obviously, it wouldn't be possible to process this specification; these
are the objects needed before you can process others. In particular, the object
Object // Classification tree root
{
Super -> Object; // Superclass object
Parent -> Object; // Parent object
Context -> Object; // Context object
Name : String; // Object's name
}
->Object[Parent; Name]; // names, where assigned, are unique to a parent
Origin:; // Parent tree root
Reference:
{
Referent -> Object;
IsMany : Boolean; // 1:1 or 1:N reference?
=> Variable; // The distinguishing variables
}
Variable: // All support equality tests
{
Ordered : Boolean = True; // Most variables have some ordering
Minimum : String; // Minimum value (only ordered)
Maximum : String; // Maximum value (only ordered)
}
Assignment:
{
Variable -> Variable; // Variable assigned-to
Final : Boolean = False; // Value may not be overridden
Value : String; // Value assigned
}
Character : Variable; // UCS4 character
String : Variable // Unicode String
{
MaxLength : Integer;
Collation : String; // Collating rule
}
Number : Variable // All numbers
{
Unsigned : Boolean; // Is the number unsigned?
}
Short : Number; // 16-bit number
Integer : Number; // 32-bit number
Long : Number; // 64-bit number
Real : Number; // 64-bit IEEE floating point
Decimal : Number // Exact decimal value
{
Digits : Short; // Digits in decimal
Precision : Short; // Digits after point
}
DateTime : Variable; // DateTime or Time per ISO8601
Enum : Variable; // Enumeration
Boolean : Enum // The Boolean Enumeration
{
False : Integer = 0;
True : Integer = 1;
}
Notes: