2022 April Release

Domain-Specific Language FundamentalsPermanent link for this heading

This chapter introduces the concept of domain-specific languages (DSLs) as well as a concise summary of important recommendations to keep in mind when working with Fabasoft app.ducx.

What is a DSL?Permanent link for this heading

Developing use case-oriented software solutions requires managing different aspects and elements such as data structures, user interface design, the implementation of methods, and business rules.

In order to account for this concept in an optimal manner, Fabasoft app.ducx is comprised of several declarative modeling languages, each designed for covering a particular aspect of solution development. For example, Fabasoft app.ducx includes a modeling language that has been designed explicitly for the definition of an object model. In addition to this, Fabasoft app.ducx includes languages for defining resources, a user interface model, an implementation model, a process model, and an organizational structure model.

These modeling languages are referred to as domain-specific languages (DSLs), where each DSL was designed for addressing a certain aspect of use case-oriented software development. The modular concept makes Fabasoft app.ducx easily extensible as new DSLs can be added on demand for addressing additional aspects without affecting existing projects.

Currently, Fabasoft app.ducx is comprised of eight distinct DSLs:

  • app.ducx Object Model Language
  • app.ducx Resource Language
  • app.ducx Use Case Language
  • app.ducx Business Process Language
  • app.ducx User Interface Language
  • app.ducx Organizational Structure Language
  • app.ducx Customization Language
  • app.ducx Expression Language

Each of these DSLs is covered in detail in its own chapter.

Common Characteristics of DSLsPermanent link for this heading

Each DSL is an independent declarative modeling language having its own set of language constructs and keywords designed with a particular purpose in mind. However, all DSLs also share certain similarities. These common characteristics and language elements that are the same across all DSLs of Fabasoft app.ducx are presented in this section.

Keywords, Expressions, and BlocksPermanent link for this heading

A DSL consists of a unique set of keywords for addressing a particular aspect of solution development. However, there are shared keywords that are common to all DSLs.

The Fabasoft app.ducx compiler is case-sensitive. All keywords must be in lower case.

Each expression must be terminated by a semicolon or be enclosed in a block. A block consists of one or more expressions enclosed in curly braces.

Operators and PunctuatorsPermanent link for this heading

There are several kinds of operators and punctuators. Operators are used in expressions to describe operations involving one or more operands.

Examples for operators and punctuators are { } [ ] ( ) , . : ; + - * / % & | ^ ! ~ = < > ? ?? ++ -- && || == != <= >= <=> += -= *= /= ??= ->.

CommentsPermanent link for this heading

Two forms of comments are supported: single-line comments and delimited comments. Single-line comments start with the characters // and extend to the end of the source line. Delimited comments start with the characters /* and end with the characters */. Delimited comments may span multiple lines. Comments do not nest and are not processed within character and string literals.

Multilingual StringsPermanent link for this heading

Multilingual strings are not defined in the source code but in separate resource files in order to keep the source code free of any hard-coded strings. By default, the resource files holding multilingual strings are named mlnames.lang. For each language you want to support, a separate resource file must be created.

The main benefit of this approach is that all language-specific string resources are maintained in a single resource file for the particular language, which allows for a quick and hassle-free localization of your application.

Resource files are automatically updated whenever you add a new element requiring a multilingual name (e.g. object classes, properties, or use cases) or another form of multilingual string, such as the label text for an input field on a form page or the text displayed for enumeration items in a drop-down list.

The language resource editor (see figure) is invoked when opening the mlnames.lang file. It allows you to enter a language-specific string for each multilingual string resource in your app.ducx project.

To edit the multilingual strings for all elements that are part of your software component open the mlnames.lang file in the Eclipse Project Explorer.

If a language string is not yet translated, the background color of this line is changed and after a successful build of this project, a warning is generated.

Beside mlnames.lang files, explanations.userdoc files are provided for defining explanation texts for properties. Simple XHTML tags like <b></b>, <i></i> or <br/> may be used to format the strings. These explanation texts can be displayed as context-sensitive help in the Fabasoft Web Client.

Note: The format of these files is by default CSV, existing projects are not migrated automatically.

Multilingual strings and explanation texts are not removed automatically when the corresponding reference is removed from the source code. Thus, you do not lose strings when temporarily commenting out some source code. To clean up multilingual names and explanation texts use the “Clean up Language Files” context menu command.

Using Fully-Qualified References for Referring to Component ObjectsPermanent link for this heading

In all DSLs, component objects can be addressed by their fully qualified reference.

Each component object has a unique programming name, which is described by its fully qualified reference that indicates a logical hierarchy. For example, COOSYSTEM@1.1:objsubject is the fully qualified reference of a component object with the reference objsubject that is part of software component COOSYSTEM@1.1.

In Fabasoft app.ducx, a software component is somewhat similar to a namespace in an object-oriented programming language: all component objects belonging to a particular software component are nested in this namespace.

The following example shows part of an object model containing the definition of object class Product. This object class is derived from object class COOSYSTEM@1.1:BasicObject. Furthermore, the existing property COOSYSTEM@1.1:mlname is assigned to the object class. Please note that fully qualified references are used for referring to both of these component objects belonging to software component COOSYSTEM@1.1.

Example

objmodel APPDUCXSAMPLE@200.200
{
  class Product : COOSYSTEM@1.1:BasicObject {
    COOSYSTEM@1.1:mlname;
  }
}

Using the Import StatementPermanent link for this heading

Using the import statement, a software component can be made known to the compiler in order to avoid the need for using the fully qualified reference when referring to a component object that is part of the imported software component.

Once a software component has been imported using an import statement, you can refer to component objects belonging to this software component with their reference only, instead of having to use the fully qualified reference. This allows for the software component prefix to be left out when referring to its component objects once a software component has been imported.

A list of import statements is referred to as import declarations. All import declarations have to be placed inside objmodel, resources, userinterface, usecases, processes or orgmodel blocks. The different types of models are described in greater detail in the following chapters.

Note: Software components listed in the import declarations must be referenced in your Fabasoft app.ducx project’s list of software component references. For further information on software component references, refer to chapter “Adding a Software Component Reference”.

Example

objmodel APPDUCXSAMPLE@200.200
{
  // Importing COOSYSTEM@1.1 permits the use of reference shorthands when
  // referring to component objects that belong to COOSYSTEM@1.1, instead

  // of requiring the use of fully qualified references

  import COOSYSTEM@1.1;

  class Product : BasicObject {
    mlname;
  }
}

It should be noted that even when a software component is imported using an import statement it is still valid to refer to component objects of the imported software component using the fully qualified reference.

Resolving of Simple ReferencesPermanent link for this heading

A reference that is not fully qualified may be ambiguous, because component objects belonging to different software components might have the same reference. References of the own software component are favored over references of other software components. In all other cases the fully qualified reference must be used to refer to these component objects.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import FSCREC@1.1001;

  // "Record" is not sufficient for unique qualification, because it could
  // refer to either FSCFOLIO@1.1001:Record or FSCREC@1.1001:Record

  class DUCXRecord : FSCFOLIO@1.1001:Record {
    ...
  }

  class SpecialRecord : FSCREC@1.1001:Record {
    ...
  }

  class State : FSCFOLIO@1.1001:State {
    ...
  }

  // "State" refers to APPDUCXSAMPLE@200.200:State because the own
  // software component is favored

  class SpecialState : State {
    ...
  }
}

Resolving of Qualifiers in app.ducx ExpressionsPermanent link for this heading

Beyond the resolving strategy of not fully qualified references some more rules apply to app.ducx expressions.

As a general rule of thumb, whenever you use qualifiers provide a type for the qualifiers to avoid ambiguities.

In following cases special resolving rules apply:

Compound properties

  1. Attempt to resolve the qualifier to a kernel interface method.
  2. Attempt to resolve the qualifier to a property of the compound property.
  3. No further resolving attempt is carried out and an error is generated.

In the following example GetTypeDefinition is resolved to a kernel interface method and isbn to a property of the compound property publication.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase ResolveCompoundProperty() {
    variant Order {
      impl = expression {
        Publication @publication = {};
        @publication.GetTypeDefinition();
        @publication.isbn;
      }
    }
  }
}

Objects

  1. Attempt to resolve the qualifier to a kernel interface method.
  2. Attempt to resolve the qualifier to a property of the object.
  3. Attempt to resolve the qualifier to an action or use case of the object.
  4. No further resolving attempt is carried out and an error is generated.

In the following example GetClass is resolved to a kernel interface method, orderpositions to a property of the object order and ResolveCompoundProperty to a use case of the object order.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase ResolveObject() {
    variant Object {
      impl = expression {
        Order @order = #Order.ObjectCreate()[2];
        @order.GetClass();
        @order.orderpositions;
        @order.ResolveCompoundProperty();
      }
    }
  }
}

Item Scope

  1. Attempt to resolve the qualifier according to the rules for the type of the list.
  2. Attempt to resolve the qualifier according to the rules of the local scope.
  3. No further resolving attempt is carried out and an error is generated.

In the following example objname is resolved to COOSYSTEM@1.1:objname and name to a key in the local scope.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase ResolveObjectList(string name) {
    variant Object {
      impl = expression {
        Object[] objects =
          coort.SearchObjects(cootx, "SELECT * FROM
                 COOSYSTEM@1.1:Object WHERE objname LIKE 'Test%'"
);
        objects[objname == "Test"];
        objects[objname == name];
      }
    }
  }
}

Dictionaries

  1. Attempt to resolve the qualifier to a kernel interface method.
  2. Attempt to resolve the qualifier to a dictionary key.
  3. No further resolving attempt is carried out and an error is generated.

In the following example key is resolved as dictionary key.

Example

usecases APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  usecase ResolveDictionary() {
    variant Object {
      impl = expression {
        dictionary @dict = {};
        @dict.key = "Test";
        // alternative
        dictionary @dict = { key: "Test" };
        // alternative
        dictionary @dict;
        @dict = coort.CreateDictionary();
        @dict.key = "Test";
      }
    }
  }
}

Other data types

  1. Attempt to resolve the qualifier to a kernel interface method.
  2. No further resolving attempt is carried out and an error is generated.

Using Generic Assignment StatementsPermanent link for this heading

Fabasoft app.ducx allows you to include so-called generic assignment statements in your source code in order to assign values to properties. You may only reference properties that actually belong to the object class of the component object denoted by the Fabasoft app.ducx language element the assignment statement is nested in.

Initialization of Properties With Scalar ValuesPermanent link for this heading

Syntax

property = value;

For initializing a property with a scalar value, the reference of the property must be denoted followed by the equality character, and the value. The assignment statement must be terminated by a semicolon.

In the following example, the COOSYSTEM@1.1:attrextension property of a content property should be initialized with the value “txt”. There is no language element provided by any of the Fabasoft app.ducx DSLs for defining the value for this property, thus, a generic assignment statement must be used for defining the value of this property as illustrated by the following example.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;

  class OrderRecord : Record {
    content orderspecification {
      attrextension = "txt";
    }
  }
}

Initialization of Scalar Compound PropertiesPermanent link for this heading

Syntax

property = {
Assignment;
  ...
}

When setting a scalar compound property, the commonly used syntax can be used.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COODESK@1.1;

  class OrderRecord : Record {
    classdocstateicons = {
     dsirecorded  = true,
      dsidocstate = DS_EDIT,
      classmicon = MiniIconRecord_Edit
    }
  }
}

Initialization of Array ValuesPermanent link for this heading

Syntax

property = [value, value, value]

When setting array properties, the familiar expression syntax can be used, even with compound properties.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COODESK@1.1;

  class OrderRecord : Record {
    classdocstateicons = [
      {
        dsirecorded  = true,
        dsidocstate = DS_EDIT,
        classmicon = MiniIconRecord_Edit
      },
      {
        dsirecorded  = true,
        dsidocstate = DS_SUSPENDED,
        classmicon = MiniIconRecord_Suspended
      }
    ];
  }
}

Initialization of Expression ValuesPermanent link for this heading

Syntax

property = expression {
}

When setting expression properties, the keyword expression is used to describe an expression block.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COODESK@1.1;

  instance Expression MyExpression {
    exprtext = expression {
    }
  }
}

If the expression property does not specify parameters or a scope type, the keyword as and an expression property definition can be used to specify this information.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COODESK@1.1;

  instance FilterExpression MyFilterExpression {
    exprtext = expression as attrfilterexpr {
      return objowner == coouser
    }
  }
}

Initialization of Compound Properties, Compact ModePermanent link for this heading

Syntax

property<member, ...> = {
  { value, ... }
}

When assigning a list of values to a compound property, a special syntax can be used to create a very compact, table like initialization form. The reference of the compound property to be initialized must be followed by angle brackets. In the angle brackets, the references of the properties belonging to the compound property’s type that should be initialized with values must be specified. The initialization values provided must be enclosed in curly braces and separated by commas. Null values can be defined using the null keyword. Additionally, multiple lines must be separated by commas as well.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COODESK@1.1;

  class OrderRecord : Record {
    classdocstateicons<dsirecorded, dsidocstate, classmicon> = {
      { true, DS_EDIT, MiniIconRecord_Edit },
      { true, DS_SUSPENDED, MiniIconRecord_Suspended },
      { true, DS_CLOSED, MiniIconRecord_Closed },
      { true, DS_CANCELLED, MiniIconRecord_CancelledRec }
    }
  }
}

Compound properties can also be nested within other compound properties. The following example demonstrates the initialization of nested compound properties.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COOWF@1.1;
  import COODESK@1.1;

  extend class WorkList {
    dispviews<dispviewattr, dispviewcomp, dispcolumns<dispattribute>> = {
      { worklistitems, APPDUCXSAMPLE@200.200,
        {
          { objname },
          { actinstwork },
          { { actinstobject, bocontact } },
          { { actinstobject, boassignedto } },
          { actinstremark },
          { actinstreceivedat },
          { actinstenddeadline }
        }
      }
    }
  }
}

Initialization With “null”Permanent link for this heading

Initializations with null can be specified by using the keyword null and will result in an appropriate assign statement. The keyword null can be used to prevent default initializations or, using the keyword update, to clear already set properties.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COODESK@1.1;

  update instance MyFilterExpression {
    exprtext = null;
  }
}

Public, Private, Secured, ObsoletePermanent link for this heading

Component objects may be marked as public, internal, private, secured, or obsolete using the keywords public, internal, private, secured, securedreadonly, and obsolete.

Depending on the project settings component objects are by default public internal or private.

The following rules apply:

  • Component objects are public by default except in following cases:
    • Actions are private by default but can be marked as public.
    • Activities that are defined within the scope of a process are private by default but can be marked as public.
  • Implicitly generated component objects not defined in the source code (like menus or applications for a use case) are public, internal, private or obsolete depending on the definition of the component object itself that triggers the implicit generation of component objects.

PrivatePermanent link for this heading

Private component objects can only be used within the software component the component object is created in and in friend components. For further information on how to create a friend relation, please consult chapter “Defining a Friend Component”.

The type of the parameters of a public action or a public attribute must be also public. The private constraint is enforced by Fabasoft app.ducx the following way:

  • When using external private component objects of a non-friend component an error is generated.
  • External private component objects in non-friend components are excluded from IntelliSense.

InternalPermanent link for this heading

Internal component objects can only be used within the base product.

SecuredPermanent link for this heading

Secured component objects expressions can be executed with the EF_SECURED flag. In secure expressions it is possible to read and write properties marked with secured.

SecuredReadonlyPermanent link for this heading

In secure expressions, it is possible to read properties marked with securedreadonly.

ObsoletePermanent link for this heading

Obsolete component objects should not be used anymore and are likely no longer available in prospective versions. To handle obsolete component objects Fabasoft app.ducx provides following support:

  • When using obsolete component objects a warning is generated.
  • Obsolete component objects are excluded from IntelliSense.

Referencing ResourcesPermanent link for this heading

The file keyword is used to reference a resource such as an image or another type of binary or text file. It must be followed by parentheses holding the relative path to the resource file enclosed in double quotes. The path must be specified relative to your Fabasoft app.ducx project.

The file keyword can also be used to reference app.ducx expressions that have been defined in separate app.ducx expression language files.

Example

// Referencing a symbol imported to the "resources/symbols" folder of the
// app.ducx project

symbol SymbolApprove {
  images<symbolimageformat, content> = {
    {
      SF_PNG16,
      file("resources/symbols/Approve16.png")
    }
  }
}

// Referencing an expression in an app.ducx expression language file
impl = file("resources/expressions/GetOrders.ducx-xp");

Reference Naming ConventionsPermanent link for this heading

The following table gives an overview of suggested naming conventions for component object references. These naming conventions are only recommendations, and you are free to define your own naming conventions.

Note: A valid reference must begin with a character and must not contain any special characters except the underscore character.

Element

Recommended reference

Software component

The reference of a software component should be composed of upper-case characters only.

Example: APPDUCXSAMPLE

Object class

The reference of an object class should be in singular form. Use mixed case notation, starting with a capital first letter.

Example: OrderRecord

Enumeration type,
compound type

The reference of an enumeration type or a compound type should describe the purpose of a property of this type. If possible, do not include the word “Type”. Use mixed case notation, starting with a capital first letter.

Example: OrderState

Enumeration item

The reference of an enumeration item should be composed of upper-case characters only. Individual words should be separated by an underscore character. Each enumeration item should share a prefix common to all enumeration items of the enumeration type containing them.

Example: OS_SHIPPED

Property

The reference of a property should describe the purpose of the property. You may include an abbreviation of the object class the property belongs to. Use lower case characters only for a property reference.

Example: orderdate

Use case

The reference of a use case should be comprised of a verb describing the action performed followed by the object on which this action is carried out. Use mixed case notation, starting with a capital first letter.

Example: PrintInvoice

ACL

The reference of an ACL should be in mixed case notation, starting with a capital first letter, and ending with the postfix “ACL”.

Example: OrderRecordACL

Form

The reference of a form should be in mixed case notation, starting with the prefix “Form”. If you define different forms for administrators and end-users, you should use the postfixes “User” and “Admin”.

Example: FormOrderRecord, FormOrderRecordUser, FormOrderRecordAdmin

Form page

The reference of a form page should be in mixed case notation, starting with the prefix “Page”.

Example: PageOrderRecord

Other component objects

The reference of all other component objects should be in mixed case notation, starting with a capital first letter. An abbreviation of the object class may be used as a prefix.

Example: ButtonBarOrderRecord