2022 June Release

app.ducx Object Model LanguagePermanent link for this heading

The purpose of the app.ducx object model language is to define the persistent object model for a software component.

Using the app.ducx object model language, you can easily define the basic elements that make up the object model:

  • object classes
  • properties and fields
  • enumeration types
  • compound types
  • extensions of existing object classes and types

An object model block consists of import declarations and object model elements. The objmodel keyword denotes an object model block. It must be followed by the reference of your software component and curly braces.

Object model blocks can only be contained in files with a .ducx-om extension.

Syntax

objmodel softwarecomponent
{
  // Import declarations
  import softwarecomponent;

  // Object model elements (object classes, types, fields)
  ...
}

Defining an Object ClassPermanent link for this heading

An object class defines the abstract characteristics of an object, which include properties and the use cases that can be executed on the instances of the object class.

Syntax

class<metaclass> reference : baseclass {
  ...
}

Selecting the Meta ClassPermanent link for this heading

Each object class is an instance of a meta class that defines the characteristics of the object class itself.

The meta class is specified in angle brackets following the class keyword. The definition of the meta class is optional. COOSYSTEM@1.1:ObjectClass is the default meta class.

Usually, you will use one of the following meta classes for defining a new object class:

  • The COOSYSTEM@1.1:ObjectClass meta class should be used for object classes that do not contain object lists or content properties. This class is used by default.
  • The COOSYSTEM@1.1:ContentObjectClass meta class is specifically designed for object classes containing content properties or compound properties of type COOSYSTEM@1.1:Content.
  • The COOSYSTEM@1.1:CompoundObjectClass meta class is specifically designed for object classes where an object list property is the main property.

Defining the Base ClassPermanent link for this heading

Each object class must have a base class which must either directly or indirectly be derived from object class COOSYSTEM@1.1:Object. This object class is the base class of all other object classes.

The base class is specified after a colon following the reference of the newly defined object class.

If you do not find a more specific base class that is appropriate for your new class, you will usually derive it from one of the following most common base classes:

  • COOSYSTEM@1.1:ContentObject for object classes that are intended for storing content. These object classes usually have COOSYSTEM@1.1:ContentObjectClass as their meta class.
  • COOSYSTEM@1.1:CompoundObject for object classes that are intended for storing object lists.
  • COOSYSTEM@1.1:ComponentObject for object classes where the instances must be shipped with a software component.
  • COOSYSTEM@1.1:BasicObject for other object classes when you do not find a more appropriate base class.
  • Base classes are restricted to
  • COOSYSTEM@1.1:ContentObject, COOSYSTEM@1.1:CompoundObject, COOSYSTEM@1.1:BasicObject
  • COOSYSTEM@1.1:ContentObjectClass, COOSYSTEM@1.1:CompoundObjectClass, COOSYSTEM@1.1:ObjectClass
  • classes marked as derivable
  • classes from own and friend components

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  // New object class for storing resources that is derived from
  // COOSYSTEM@1.1:BasicObject

  class Resource : BasicObject {
    ...
  }

  // New object class for specification documents that is derived from
  // COOSYSTEM@1.1:ContentObject

  class<ContentObjectClass> SpecificationDocument : ContentObject {
    ...
  }

  // New object class for projects that is derived from
  // COOSYSTEM@1.1:CompoundObject

  class Project : CompoundObject {
    ...
  }
}

Defining Miscellaneous Object Class AspectsPermanent link for this heading

Besides choosing the meta class and the base class, you can also define several other aspects of the object class, such as whether it should be abstract, programmatic, common, derivable, or compound.

Setting an Object Class to Abstract

The abstract keyword allows you to define an abstract object class.

An abstract object class can only be used as a base class from which child object classes may be derived, and which cannot be instantiated itself.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Vehicle : BasicObject {
    abstract = true;
  }

  class Car : Vehicle {
  }
}

Setting an Object Class to Programmatic

Using the programmatic keyword, an object class can be marked as not creatable by a user via the user interface. Thus, for example, the object class is not available in the “Object” > “New” dialog but objects of the object class can be created programmatically.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : BasicObject {
    programmatic = true;
  }
}

Marking an Object Class as Compound

Using the compound keyword, an object class can be marked as compound. Only the instances of a compound object class are displayed in the tree view on the left-hand side of the Fabasoft Web Client.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Product : CompoundObject {
    compound = true;
  }
}

Marking an Object Class as a Common Class

Using the common keyword, an object class can be marked as a common class. This means that this class is creatable or usable in common locations like in a folder (i.e. in the COOSYSTEM@1.1:objchildren property). Using the allowcommonclasses keyword with a property can be marked as a property for containing common classes.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Product : CompoundObject {
    common = true;
  }
}

Marking an Object Class as Derivable

Using the derivable keyword, an object class can be marked as derivable. This means that classes from other software components can use this class as base class.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Product : CompoundObject {
    derivable = true;
  }
}

Adding Properties to an Object ClassPermanent link for this heading

Each object class can have several properties to store data of various data types.

Reusing Existing PropertiesPermanent link for this heading

Existing properties can be reused and referenced within an object class or within a compound type.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class FileCategory : BasicObject {
    // Reusing object pointer property FSCFOLIO@1.1001:dcstate
    FSCFOLIO@1.1001:dcstate;
  }
}

When reusing properties, keep in mind that changes to the original property might affect your solution. For instance, the definition of the selectable object classes in the FSCFOLIO@1.1001:dcstate property in the example might be changed in a future version. As a result, reusing properties might result in unpredictable side-effects in some cases.

Defining New PropertiesPermanent link for this heading

Syntax

datatype reference modifiers;

New properties can be defined within an object class, a compound type, or the fields block. For each property defined using Fabasoft app.ducx, a corresponding property component object is created as part of the project’s software component.

A property must be defined based on a valid data type. Each property can either store a scalar value or a list of values of the specified data type. Add square brackets after the data type to specify a list.

There are two ways of defining a new property: the simple shorthand notation and the extended notation for including triggers and constraints in a property definition.

A shorthand property definition consists of

  • a data type
  • square brackets as an optional list marker
  • a reference
  • a semicolon

Additional features concerning property definitions are covered in chapter “Extended Property Definitions”.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Project : CompoundObject {
    // Creates a new property of data type COOSYSTEM@1.1:STRINGLIST
    // with the reference APPDUCXSAMPLE@200.200:projectdescription

    string[] projectdescription;
  }
}

Data TypesPermanent link for this heading

Every property must have a data type. Simple data types, enumeration types, and compound types are supported.

Simple Data Types

Simple data types are provided by software component COOSYSTEM@1.1. The following table shows all supported simple data types.

Simple data type

Keyword

Maximum Size

COOSYSTEM@1.1:STRING

string

4000 characters

COOSYSTEM@1.1:BOOLEAN

boolean

COOSYSTEM@1.1:INTEGER

integer, time, timespan

64 Bit

COOSYSTEM@1.1:FLOAT

float

16 digits

COOSYSTEM@1.1:DATETIME

date, datetime

COOSYSTEM@1.1:Currency

currency

COOSYSTEM@1.1:CONTENT

content

COOSYSTEM@1.1:DICTIONARY

dictionary

COOSYSTEM@1.1:OBJECT

object

For most simple data types, a corresponding list data type is provided for storing lists of items of this type. The supported list data types are depicted in the next table.

Simple data type

Keyword

COOSYSTEM@1.1:STRINGLIST

string[]

COOSYSTEM@1.1:BOOLEANLIST

boolean[]

COOSYSTEM@1.1:INTEGERLIST

integer[], time[], timespan[]

COOSYSTEM@1.1:FLOATLIST

float[]

COOSYSTEM@1.1:DATETIMELIST

date[], datetime[]

COOSYSTEM@1.1:CONTENTLIST

content[]

COOSYSTEM@1.1:DICTIONARYLIST

dictionary[]

COOSYSTEM@1.1:OBJECTLIST

object[]

Enumeration Types

For information concerning the definition and extension of enumeration types, please consult chapter “Defining Enumeration Types”.

Compound Types

For information concerning the definition and extension of compound types, please consult chapter “Defining Compound ”.

Defining a String PropertyPermanent link for this heading

The string keyword denotes a data type that stores a string of characters and can be used for defining string properties.

The length of a string can be specified enclosed in parentheses. If the length is not specified, the default length of 254 characters is assumed.

Note: String lists can be used for multiline input fields.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Task : BasicObject {
    // String property holding a maximum of 254 characters
    string shortdescription;

    // Sized string property holding a maximum of 2 characters
    string(2) state;

    // String list property that is displayed as a multiline input field
    string[] longdescription;
  }
}

Defining a Boolean PropertyPermanent link for this heading

The boolean keyword is used to define properties to store the Boolean values (true and false).

Note: A Boolean property can actually have three states as it can also be undefined.

Example

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

  class Application : Case {
    boolean approved;
  }
}

Defining an Integer Number PropertyPermanent link for this heading

The integer keyword denotes a simple data type and can be used to define integer number properties that store values consisting of up to 10 digits.

Integers may be signed or unsigned. Unsigned integers are capable of representing only non-negative values whereas signed integers are capable of representing negative values as well.

For defining unsigned integers use the unsigned integer keywords.

For an integer, its display size in digits can be specified enclosed in parentheses. If no size value is specified, the default size of 10 digits is assumed.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class TaxReport : BasicObject {
    // Integer number property with 10 digits
    integer netchange;

    // Sized integer number property with 3 digits
    integer(3) taxrate;

    // Unsigned integer number property with 10 digits
    unsigned integer taxpayers;

    // Sized unsigned integer number property with 4 digits
    unsigned integer(4) fiscalyear;
  }
}

Defining a Floating-Point Number PropertyPermanent link for this heading

The float keyword denotes a floating-point number data type that represents a real number and can be used to define floating-point number properties to store values consisting of up to 16 digits in total with a maximum of 9 digits of precision.

The float keyword can be preceded by the keyword unsigned to denote an unsigned float which can only represent non-negative values.

For a floating-point number property, the size in digits before and the number of digits of precision can be specified enclosed in parentheses. If no size values are specified, the default sizes of 10 digits before with 2 digits of precision are assumed.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class AssetPerformance : BasicObject {
    // Floating-point number property with 10 digits before
    // and 2 digits of precision

    float assetvalue;

    // Sized floating-point number property of a 3 digit number with
    // 2 digits of precision

    float(3,2) ytdchange;

    // Sized unsigned floating-point number property of an 8 digit number
    // with 6 digits of precision

    unsigned float(8,6) exchangerate;
  }
}

Defining a Date PropertyPermanent link for this heading

The date keyword is used for defining a property to store a date value.

The datetime keyword is used for defining a property that stores both a date and a time value.

By default, there is no conversion of the date time value to the current time zone. This behavior is also described by the optional suffix universal.

The suffix local is used for time zone dependent values, where a conversion to local time is done each time the property is accessed using a kernel interface.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Milestone : BasicObject {
    // Date only property
    date deadline;

    // Date and time property with local time conversion enabled
    datetime documentreceivedat local;

    // Date and time property with local time conversion disabled
    datetime kickoffmeetingat;
  }
}

Defining a Time PropertyPermanent link for this heading

The time keyword is used for defining a property to store a time value.

Time values are stored in seconds in an integer number property of data type COOSYSTEM@1.1:INTEGER. The COOATTREDIT@1.1:CTRLTimestamp control is used for displaying time values in time format.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  struct Session {
    string sessiontitle;
    time sessionstartat;
    time sessionendat;
  }

  class Conference : BasicObject {
    // Compound property storing a list of conference sessions
    Session[] sessions;
  }
}

Defining a Time Span PropertyPermanent link for this heading

The timespan keyword is used for defining a property to store a time span.

The time span is stored in an integer number property of data type COOSYSTEM@1.1:INTEGER. The COOATTREDIT@1.1:CTRLTimespan control is used for displaying the time span in days, hours, minutes, and seconds.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class MailPollingDefinition : BasicObject {
    // Time span for defining the e-mail polling interval
    timespan pollinginterval;
  }
}

Defining a Currency PropertyPermanent link for this heading

The currency keyword is used to define a currency property that represents an amount of money in a certain currency.

For a currency property, the size in digits before and the number of digits of precision can be specified enclosed in parentheses.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Project : CompoundObject {
    // Currency property for storing an amount of money in a currency
    // that can be selected by the user

    currency projectvalue;

    // Sized currency property of a 12 digit number with
    // 4 digits of precision

    currency(12,4) foreignchange;
  }
}

Defining a Content PropertyPermanent link for this heading

A content property that is defined using the content keyword can store any kind of binary data, such as a Microsoft Word or LibreOffice document.

A file can be imported into a content property from the file system, and conversely content that is stored in a content property can be exported to the file system.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Project : CompoundObject {
    // Content property for storing a project plan in a binary format
    content projectplan;
  }
}

Defining a Dictionary PropertyPermanent link for this heading

A dictionary is a list of key-value pairs. Each key-value pair consists of a string which is used as a key for addressing the dictionary entry, and a value or a list of values of the same data type.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class StatusLog : BasicObject {
    // Dictionary property for storing key-value pairs
    dictionary valuedict;
  }
}

Defining an Object Pointer PropertyPermanent link for this heading

An object pointer property can store a pointer to an object or a list of pointers to objects.

No explicit keyword is required for defining an object pointer property. Instead, the object class of the objects that shall be referenced by the new object pointer property is used as data type.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class PeerGroup : BasicObject {
    // Object pointer for referencing a user object
    User responsibleuser;

    // Object list for storing a list of user objects
    User[] groupmembers;
  }
}

By default, instances of all object classes that are directly or indirectly derived from the object class provided in the definition can be selected in the object pointer. A default object pointer property will also allow the creation of new object instances within the property. However, for object pointer properties, you can provide an extended definition of the object classes allowed and not allowed in the property in order to limit the selectable and creatable items.

The allow keyword can be used to define a list of object classes that should be allowed in the object pointer property, and the exclude keyword can be used for excluding object classes. Both the object classes listed in the allow block and in the exclude block must be either directly or indirectly derived from the object class provided in the object pointer property definition.

For each of the object classes listed within an allow block, the create keyword can be added after the reference of the object class to allow the creation of instances of this object class in the object pointer property. If the create keyword is omitted, no instances can be created directly within the object pointer property.

Object classes listed within an allow or exclude block must be separated by semicolons.

The allowcommonclasses keyword defines that the object pointer property can contain all common object classes (all object classes with common set to true).

Example

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

  class Department : BasicObject {
    // This definition creates an object pointer for referencing a
    // person object; it will not allow the creation of a new person

    // object within the object pointer

    Person deptmanager {
      allow {
        Person;
      }
    }

    // This definition creates an object list for storing objects that
    // can contain persons and employees but no contact persons; only

    // employee objects can be created in this list

    Person[] deptmembers {
      allow {
        Person;
        Employee create;
      }
      exclude {
        ContactPerson;
      }
    }
    // This definition creates an object list for storing all
    // common documents classes

    ContentObject[] documents {
      allowcommonclasses = true;
    }
  }
}

Defining Enumeration TypesPermanent link for this heading

In contrast to properties, which are defined directly within an object class, an enumeration type can only be defined as a separate objmodel model element.

A custom enumeration type can be defined using the enum keyword which must be followed by a reference and a block containing the permissible enumeration items separated by commas.

Each enumeration item can be assigned a specific integer value. For enumeration items that are not assigned to a specific integer value, the compiler automatically generates integer values. To do so, the compiler simply increments the integer value of the preceding enumeration item by 1, starting with value 1 for the first item if it does not have an assigned value.

Note: It is highly recommended to explicitly assign integer values to every enumeration item to avoid errors with extensions of the enumeration type later on.

Example

objmodel APPDUCXSAMPLE@200.200
{
  // Enumeration type consisting of three enumeration items which are
  // assigned system generated integer values, starting with 1

  // This kind of writing is not recommanded, because of the automatic numeration

  // it is highly error-prone on extension or expansions

  enum ProjectState {
    PS_PLANNED,
    PS_UNDERWAY,
    PS_COMPLETED
  }

  // Enumeration type consisting of four enumeration items with
  // explicitly assigned integer values

  enum ShipType {
    ST_DESTROYER = 100,
    ST_CRUISER = 200,
    ST_BATTLESHIP = 300,
    ST_SUBMARINE = 400
  }

  // Enumeration type consisting of six enumeration items with an
  // explicitly assigned starting value; the remaining enumeration items

  // get assigned integer values 5001, 5002, 5003 and so on.

  enum NewEnglandStates {
    ST_CONNECTICUT = 5000,
    ST_NEWHAMPSHIRE,
    ST_MAINE,
    ST_MASSACHUSETTS,
    ST_RHODEISLAND,
    ST_VERMONT
  }

  // Enumeration type consisting of enumeration items with partially
  // assigned integer values

  enum DrinkType {
    DT_SHOT = 800,        // integer value 800 manually assigned
    DT_LONGDRINK = 850,   // integer value 850 manually assigned
    DT_SOUR,              // integer value 851 generated by compiler
    DT_FIZZ,              // integer value 852 generated by compiler
    DT_HIGHBALL,          // integer value 853 generated by compiler
    DT_SOFTDRINK = 900,   // integer value 900 manually assigned
    DT_OTHER              // integer value 901 generated by compiler
  }
}

Note: Existing enumeration types can be extended with additional enumeration items. For details on how to extend an existing enumeration type with additional enumeration items, please consult chapter “Extending an Existing Enumeration Type”.

Using the keyword obsolete, enumeration values can be defined as disabled. Drop-down lists for enumeration values in the user interface do not contain disabled values.

Example

objmodel APPDUCXSAMPLE@200.200
{
  // Enumeration type consisting of four enumeration items with
  // explicitly assigned integer values, some of them disabled

  enum ShipType {
    ST_DESTROYER = 100,
    ST_CRUISER = 200,
    obsolete ST_BATTLESHIP = 300,
    obsolete ST_SUBMARINE = 400
  }
}

In rare cases it is necessary to specify some other attributes for the type. This is accomplished by using generic assignments before the block of enumeration values.

Example

objmodel APPDUCXSAMPLE@200.200
{
  // Enumeration type consisting of four enumeration items with
  // explicitly assigned integer values and some generic assignments before

  enum ShipType {
    typemultiple = true;
    typelistunique = true;
    ST_DESTROYER = 100,
    ST_CRUISER = 200,
    ST_BATTLESHIP = 300,
    ST_SUBMARINE = 400
  }
}

Defining an Enumeration PropertyPermanent link for this heading

As with an object pointer property, no explicit keyword is required for defining an enumeration property. Instead, you must provide the enumeration type which you want to assign to the new enumeration property.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  enum Transmission {
    TR_AUTOMATIC = 10000,
    TR_MANUAL = 10001
  }

  enum Options {
    OP_LEATHERSEATING = 20000,
    OP_AIRCONDITIONING = 20001,
    OP_FOGLAMPS = 20002
  }

  class Car : BasicObject {
    // Enumeration property that can store a single enumeration item
    Transmission transmission;

    // Enumeration property that can store a list of enumeration items
    Options[] options;
  }
}

Defining Compound TypesPermanent link for this heading

A compound type is a structure that can contain a list of properties of any valid data type. Similar to enumeration types, compound types can be defined or extended within an objmodel block.

Defining a New Compound Type

A custom compound type can be defined using the struct keyword that must be followed by a reference and a block containing the properties that should become part of the compound type separated by semicolons.

You can either reference existing properties or define new properties directly within a compound type.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  // Compound type for storing publications
  struct Publication {
    string title;                   // new property for storing the title
    userfirstname;                  // reuse first name property
    usersurname;                    // reuse surname property
    integer(4) yearofpublication;   // new year of publication property
    string isbn;                    // new isbn property
    typecast = TypeCastPublication; // type cast
  }
}

Defining the Key for a Compound Type

For a compound type, you can define one or more key properties. The key is evaluated for determining unique entries in a property using the compound type.

The properties making up the key must be referenced in a block that is assigned to COOSYSTEM@1.1:typeuniqueattrs.

In addition to setting the key properties for a compound type, you can also define that compound properties using this compound type can contain unique entries only. To enforce unique keys, COOSYSTEM@1.1:typelistunique must be set to true.

In the following example, the APPDUCXSAMPLE@200.200:isbn property is specified as the key for compound type “Publication”. Additionally, the COOSYSTEM@1.1:typelistunique is set to true so the compound type will only allow entries with a unique ISBN.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  struct Publication {
    string isbn;
    string title;
    typelistunique = true;    // require unique entries
    typeuniqueattrs = {       //
      isbn                    // list of key properties
    }                         //
  }
}

Note: You can also extend existing compound types with new properties. For details on how to extend an existing compound type with new properties, please consult chapter “Extending an Existing Compound Type”.

Defining a Type Cast for a Compound Type

Optionally a type cast may be defined. In expressions the type cast allows you to assign values directly to properties of the compound type, based on the type of the value without need to explicitly specify the property. The type cast defines which type should be assigned to which property. The TypeCastPrototype has two parameters: source and target.

In the following example string lists are assigned to features and integers and floats are assigned to productioncosts. In an expression you can make following assignment: Feature feature = 300;. The result is the same as when writing feature.productioncosts = 300;.

Note: The direct assignment to the compound property productioncosts works because for currencies a default type cast is provided by default.

Example

app.ducx Object Model Language

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  struct Feature {
    currency productioncosts;
    string[] features;
    typecast = TypeCastFeature;
  }
}

app.ducx Use Case Language

usecases APPDUCXSAMPLE@200.200
{
  TypeCastFeature(parameters as TypeCastPrototype) {
    variant Object {
      impl = expression {
        Feature target = {};
        if (typeof(source) == #STRINGLIST) {
          target.features = source;
        }
        else if (typeof(source) == #INTEGER || typeof(source) == #FLOAT) {
          target.productioncosts = source;
        }
        target;
      }
    }
  }
}

Using a Primary Property for Direct Assignments

For some compound properties it is likely that only one property of the compound property is set (e.g. FSCVAPP@1.1001:applytofield). For this case a property of the compound property can be marked by setting COOSYSTEM@1.1:typeprimaryattr. Additionally, the predefined type cast COOSYSTEM@1.1:TypeCast has to be used.

In the following example title is marked as primary property thus assignments like Publication publication = “An Introduction to Fabasoft app.ducx”; are possible.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  struct Publication {
    string title;
    userfirstname;
    usersurname
    integer(4) yearofpublication;
    string isbn;
    typecast = TypeCast;
    typeprimaryattr = title;
  }

}

Samples for compound types with primary properties are listed in the following table:

Compound type

Primary property

FSCVAPP@1.1001:Branch

FSCVAPP@1.1001:identopt

FSCVAPP@1.1001:ApplyToField

FSCVAPP@1.1001:identopt

COOSYSTEM@1.1:LanguageStringList

COOSYSTEM@1.1:langstring

COOSYSTEM@1.1:LanguageContentList

COOSYSTEM@1.1:langcontent

COOSYSTEM@1.1:Content

COOSYSTEM@1.1:contcontent

COOSYSTEM@1.1:Currency

COOSYSTEM@1.1:currvalue

Note: Direct assignments can not only be utilized in expressions but also in domain specific languages for initializing values or defining instances.

Specifying a Concrete Compound Type

Sometimes it is necessary to use a more specialized compound type as type for a compound property. This can be achieved by listing the required type after the struct keyword.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  struct<TypeCustomizationPointDef> CPManualCreated {
    ObjectClass objcls;
    component;
    string title;
  }
}

Defining a Compound PropertyPermanent link for this heading

In the same way as for an enumeration property, the definition of a compound property does not require a special keyword. Instead, the compound type is used as data type for the new compound property.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  struct OrderPosition {
    integer quantity;
    Product product;
  }

  class Product : BasicObject {
    ...
  }

  class Order : CompoundObject {
    // Compound property for storing a list of order positions
    OrderPosition[] orderpositions;

    // Compound property that is reusing the compound type
    // COOSYSTEM@1.1:Content

    Content orderdescription;
  }
}

Defining a Link PropertyPermanent link for this heading

A link property allows you to create a bidirectional relationship between objects. There are two ways to establish such a relationship. On the one hand the backlink keyword can be used to create explicitly back link objects that manage the bidirectional relationship, on the other hand just the link keyword can be used. In the second case no back-link objects are created.

The backlink keyword is used for defining a back-link property. It must be followed by a reference and curly braces.

Use link when referencing a link or back link property from an object pointer property in order to establish a bidirectional relationship.

Back link properties must be directly assigned to an object class. You cannot use the back-link properties in a compound type. However, an object pointer property referencing a back-link property can also be part of a compound type.

In the following example, an order has an object pointer property pointing to an invoice. However, an invoice also needs to know to which order it belongs to. Therefore, a back-link property is defined for pointing back to the order. The back link ensures that the integrity of the relationship between linked objects is maintained automatically. Whenever the order’s invoice object pointer property is changed, the change is reflected in the invoice.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    Invoice orderinvoice {
      link = invoiceorder;
    }
  }

  class<ContentObjectClass> Invoice : ContentObject {
    backlink<Order> invoiceorder readonly;
  }
}

The following example shows the use of only the link keyword. The links ensure that the integrity of the relationship between linked objects is maintained automatically. Whenever the order’s invoice object pointer property is changed, the change is reflected in the invoice and vice versa.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    Invoice orderinvoice {
      link = invoiceorder;
    }
  }

  class<ContentObjectClass> Invoice : ContentObject {
    Order invoiceorder {
      link = orderinvoice;
    }
  }
}

Defining an Expression PropertyPermanent link for this heading

An expression property is a string list property to hold the expression and has additional information regarding the evaluation context. Most of this additional information can be specified with additional elements in the property definition.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class StatusLog : BasicObject {
    expression boolean logexpr(string data, out string line) {
      scope = PARSCOPE_GLOBALSCOPEWITHOBJECT;
    }
  }
}

This expression property describes an expression with the following behavior:

  • It operates on an object.
  • It gets a string value data in the global scope.
  • It creates a string value line in the global scope.
  • It returns true or false.

It is also possible to specify the argument list using a prototype (see chapter “Defining a Prototype”).

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class StatusLog : BasicObject {
    expression boolean logexpr(parameters as LogPrototype) {
      scope = PARSCOPE_GLOBALSCOPEWITHOBJECT;
    }
  }
}

Property Modifier PrefixesPermanent link for this heading

When defining new properties within an object class or within a compound type, you can add so-called property modifier prefixes before the property’s reference.

unique is currently the only available modifier prefix, which can be used to define that values of a list property have to be unique.

Example

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

  class Order : CompoundObject {
    unique OrderPosition[] orderpositions;
  }
}

Property Modifier SuffixesPermanent link for this heading

When defining new properties within an object class or within a compound type, you can add so-called property modifier suffixes (also referred to as modifiers) after the property’s reference. Modifiers allow you to change the appearance of a property as well as some other aspects pertaining to the representation of property values.

The following table contains a list of supported modifiers along with a brief description.

Modifier

Description

not null

The property must contain a value.

readonly

The property is not changeable in any circumstance.

readonly(ui)

The property is not changeable in the user interface.

readonly(inst)

The property is not changeable when the software component of the component object is in the “Installed” state.

volatile

The property is not cached by the kernel. This means that the get action or value expression is evaluated each time the property is accessed.

volatile(tx)

The property is cached in the current transaction by the kernel. This means that the get action or value expression is evaluated each first time the property is accessed in a transaction. This will improve performance if the calculation of the property is extensive. Please have in mind that the values stored in the transaction are transferred to the client in case of a detach operation. Therefore, values like large lists or contents will produce an additional higher overhead.

Note: You can use COOSYSTEM@1.1:ObjectRefresh() to enforce the recalculation of all properties defined with a get action or value expression. In the first parameter of COOSYSTEM@1.1:ObjectRefresh() a property list can be specified to limit the refresh to those properties.

invisible

The property is not visible in the user interface.

nocopy

The property is cleared on copied objects.

nocreate

In the user interface the create functionality is not provided.

Note: In object pointer controls only.

nosearch

In the user interface the search functionality is not provided.

Note:

  • In object pointer controls only.
  • To make full use of this flag, a filter expression should be defined to get a list of objects

If multiple modifiers are appended to a property, they must be separated by whitespaces.

Example

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

  enum OrderState {
    OS_PENDING = 100,
    OS_APPROVED = 101,
    OS_DISCARDED = 102,
    OS_SHIPPED = 103,
    OS_COMPLETED = 104,
    OS_ARCHIVED = 105
  }

  class Order : CompoundObject {
    datetime orderdate readonly(ui);
    OrderState orderstate readonly(ui);
    Person ordercustomer not null {
      allow {
        Person;
      }
    }
    OrderPosition[] orderpositions not null;
    integer orderpositioncount volatile readonly;
    Invoice orderinvoice readonly(ui);
    currency ordertotal readonly(ui);
  }
}

Extended Property DefinitionsPermanent link for this heading

An extended property definition allows you to define initialization values, to assign triggers to and to define constraints for your properties.

The following syntax example illustrates the structure of extended property definitions.

Syntax

datatype reference modifiers {
  // Definition of an initialization value
  init = initvalue;

  // Definition of the access type required to read the property
  accget = getaccesstype;
  // Definition of the access type required to write the property
  accset = setaccesstype;

  // Trigger that is called when the property is created
  ctor = constructortriggeraction;
  // Trigger that is called when the value of the property is read
  get = getvaluetriggeraction;
  // Trigger that is called when the value of the property is written
  set = setvaluetriggeraction;
  // Trigger that is called when the value of the property is copied
  copy = copytriggeraction;

  // Value constraint
  value = expression {
    ...
  }
  // Filter constraint
  filter = expression {
    ...
  }
  // Filter constraint in a search form
  searchfilter = expression {
    ...
  }

  // Behavior in form pages: Check if property is visible
  visible = expression {
    ...
  }
  // Behavior in form pages: Check if property is changeable
  changeable = expression {
    ...
  }
  // Behavior in form pages: Check if property must be defined
  mustbedef = expression {
    ...
  }
  // Behavior in form pages: Validate property
  validate = expression {
    ...
  }
  // Behavior in form pages: Execute if value of property has changed
  uichange = expression {
    ...
  }
  // Behavior in form pages: Execute if value or property has changed in a
  //
search form
  uisearchchange = expression {
    ...
  }
  // Behavior in form pages: Access types for the property
  accset = expression {
    ...
  }
  // Behavior in form pages: Access types for a line of the property
  accsetline = expression {
    ...
  }
  // Behavior in form pages: Weight to display the property
  weight = expression {
    ...
  }
  // Behavior in form pages: Control style of the property
  controlstyle = expression {
    ...
  }
  // Behavior in form pages: Control options of the property
  controloptions = expression {
    ...
  }
}

Initializing a Property With a ValuePermanent link for this heading

Using the init keyword, you can assign an initialization value to a property. The property is initialized with this value when you create a new instance of the object class the property is assigned to.

In addition to static initialization values, you can also use app.ducx expression language to calculate the value used for initializing a property. An expression block is required to define an initialization expression.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    datetime orderdate readonly(ui) {
      // initialization with an expression block
      // example of a static initialization for datetime:

      // init = 2010-10-10T09:13:27;

      init = expression {
        coonow;
      }
    }
    OrderState orderstate readonly(ui) {
      init = OS_PENDING;
    }
    string ordershortdescription {
      init = "Please enter a description text!";
    }
    integer(3) orderitems readonly(ui) {
      init = 0;
    }
  }
}

Protecting a Property With Access TypesPermanent link for this heading

A property can be protected with an access type for reading the property value and with an access type for changing the property:

  • The accget keyword is used to assign an access type for reading the property value. When protected by an access type for reading the property value, the property is only displayed if the access type is granted to the user by the object’s ACL.
  • The accset keyword is used to assign an access type for changing the property value. When protected by an access type for changing the property value, the property may only be changed if the access type is granted to the user by the object’s ACL.

Both the access type for reading and for changing the property value are optional.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    Product[] orderedproducts not null {
      accget = AccTypeReadComp;
      accset = AccTypeChangeComp;
    }
  }
}

Assigning Triggers to a PropertyPermanent link for this heading

Triggers are actions that are invoked or “fired” automatically when a predefined event occurs. Each trigger has assigned a specific action prototype listing the parameters that are passed to the implementation of the trigger action when it is called.

Keyword

Description

ctor

The constructor trigger is fired when the property is created. A constructor trigger action must have assigned the prototype COOSYSTEM@1.1:AttrConstructorPrototype.

linector

The line constructor trigger is fired when a new entry is created in a compound property. A line constructor trigger action must have assigned the prototype COOSYSTEM@1.1:AttrLineConstructorPrototype.

dtor

The destructor trigger is fired when the property is destroyed. A destructor trigger action must have assigned the prototype COOSYSTEM@1.1:AttrDestructorPrototype.

get

The get value trigger is fired after the property value is read from the database. A get value trigger action must have assigned the prototype COOSYSTEM@1.1:AttrGetPrototype.

set

The set value trigger is fired before the property value is written to the database. A set value trigger action must have assigned the prototype COOSYSTEM@1.1:AttrSetPrototype.

copy

The copy trigger is fired when an object containing the property is duplicated. A copy trigger action must have assigned the prototype COOSYSTEM@1.1:AttrCopyPrototype.

display

The display trigger is fired to format the property value for being displayed in a list view column. A display trigger action must have assigned the prototype COOSYSTEM@1.1:AttrGetDispPrototype.

search

The search trigger is fired when searching for values in the property. A search trigger action must have assigned the prototype COOSYSTEM@1.1:AttrSearchPrototype.

getver

The get versioned value trigger is fired when a version of the property value is read. A get versioned value trigger action must have assigned the prototype COOSYSTEM@1.1:AttrGetVersionPrototype.

fixver

The fix version trigger is fired when a version of the property value is created. A fix version trigger action must have assigned the prototype COOSYSTEM@1.1:AttrFixVersionPrototype.

delver

The delete version trigger is fired when a version of the property is deleted. A delete version trigger action must have assigned the prototype COOSYSTEM@1.1:AttrDelVersionPrototype.

restver

The restore version trigger is fired when a version of the property value is restored. A restore version trigger action must have assigned the prototype COOSYSTEM@1.1:AttrRestVersionPrototype.

archive

The archive trigger is fired when the property value is archived. An archive trigger action must have assigned the prototype COOSYSTEM@1.1:AttrArchivePrototype.

restore

The restore trigger is fired when the property value is being restored from an archive. A restore trigger action must have assigned the prototype COOSYSTEM@1.1:AttrRestArchivePrototype.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    // The value of the ID property of an order must not be copied when
    // an order object is duplicated

    unsigned integer(6) orderid {
      copy = NoOperation;
    }
  }
}

Note: The keyword nocopy can be used to specify a property with copy = NoOperation.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : CompoundObject {
    // The value of the ID property of an order must not be copied when
    // an order object is duplicated

    unsigned integer(6) orderid nocopy;
}

For further information on how to implement trigger actions, please consult chapter “Implementing Triggers”.

Assigning Constraints to a PropertyPermanent link for this heading

Integrity and value constraints for calculating and validating values are used to prevent invalid data entry into a property.

Fabasoft app.ducx currently supports following types of constraints:

  • Value constraints are useful for automatically calculated property values such as the grand total of an order or the number of order positions.
  • Filter constraints allow you to limit the selectable values for object pointer properties.
  • Validation constraints are typically expressed as conditions or measurements that must remain true as the user uses your solution.
  • User interface change constraints are evaluated when the value of an object pointer property is changed by the user, and can be used to trigger the re-evaluation of filter constraints or to set or clear the values of other properties.

Defining Value Constraints for a PropertyPermanent link for this heading

A value constraint can be defined for the automatic calculation of a property value.

The keyword value is used for defining a value constraint where the app.ducx expression language must be used for the implementation.

Value constraints only make sense for calculated properties that cannot be changed by the user. Therefore, in most cases, properties with value constraints should also be set to volatile and readonly using appropriate property modifiers. If the property is not read-only the app.ducx expression must have a result that can store the changeable value (e.g. a property).  Additionally, value constraints cannot be used within compound properties.

Please note that volatile properties do not show up in the search dialog box by default unless you attach a search trigger action to the property as demonstrated in the example.

When implementing a value constraint in app.ducx expression language, the current object can be accessed using the local scope this or global scope ::this. The calculated value that should be displayed in the property must be passed back as the return value of the expression.

In this example, the total value of all individual order positions of an order is calculated and displayed in a property with a value constraint.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  struct OrderPosition {
    Product product not null;
    unsigned integer quantity not null;
  }

  class Product : BasicObject {
    mlname;
    string[] productdescription;
    currency unitprice;
  }

  class Order : CompoundObject {
    OrderPosition[] orderpositions;
    currency ordertotal readonly volatile {
      value = expression {
        // Add up the price of the order positions
        currency @total = 0;

        for (OrderPosition @position : orderpositions) {
          Product @product = @position.product;
          if (@product != null) {
            @total += @product.unitprice * @position.quantity;
          }
        }
        // Return the grand total
        return @total;
      }
    }
  }
}

Defining Filter Constraints for a PropertyPermanent link for this heading

Filter constraints allow you to limit the values that are selectable in an object pointer property.

The keyword filter or searchfilter is used for defining a filter constraint. The app.ducx expression language must be used for implementing the filter constraint. Search filters are used to reduce available objects of object pointer properties in context of a search.

There are two distinct variants of filter constraints that have different scopes depending on the data type of the return value of the filter expression:

  • Object list filter constraints
  • Boolean filter constraints

Note:

  • COOSYSTEM@1.1:attrfilterexpr
    A Fabasoft app.ducx Expression that determines or filters the possible selection of objects in an object pointer property. If used in conjunction with COOSYSTEM@1.1:AttrFilterCheckSet this Fabasoft app.ducx Expression is used to test the value of a property (of any type).
  • COOSYSTEM@1.1:attrsearchfilterexpr
    This filter is evaluated if CAM_SEARCH is passed in the parameter mode to the action COOSYSTEM@1.1:LocalObjectsGet. Additionally, the transaction variable TV_SEARCHOBJCLASSES of type OBJECT is provided to be able to specify already chosen object classes in the search filter. The transaction variable contains the chosen object class (several in case of a quick search).

Object List Filter Constraints

An object list filter constraint is an expression returning a list of objects. For properties with an object list filter constraint, users can only select values out of the list of permitted values.

For an object list filter constraint, the object containing the object pointer property can be accessed using the local scope this. The global scope ::this contains a list of objects that should be filtered, i.e. after a quick search operation in the object pointer property control. If empty, the Fabasoft app.ducx Expression should retrieve a meaningful default for objects that are usable as a value for the property.

If an object list filter is terminated with the exception COOSYSTEM@1.1:COOSTERR_BREAK, the default behavior is provided. This means, that fitting objects that have been recently used, are provided as the filter result.

Note: The type of the expression must be determinable before the expression is evaluated. To provide a precise type, a cast to OBJECTLIST() is sometimes helpful.

Boolean Filter Constraints

A Boolean filter constraint is evaluated on each of the objects that would be selectable in an object pointer property based on the definition of allowed object classes for the property. If the filter expression returns true, the object is included in the list of selectable values displayed to users. Otherwise, the object is filtered out and cannot be selected.

For a Boolean filter constraint, the local scope this contains the object that is to be selected and the global scope ::this contains the object containing the object pointer property.

For instance, if a Boolean filter constraint is defined for an object pointer property for selecting users in a domain containing 300 user objects, the filter expression would be evaluated up to 300 times as it is evaluated for each user object.

In the example, there are three object classes: product, vendor, and order. An instance of vendor has a list of products sold by the vendor. An order contains an object pointer property pointing to a vendor, and a list of products ordered from this vendor. For the order’s vendor object pointer property, a Boolean filter constraint has been defined only allowing the user to select vendors that actually sell products. Moreover, the list of ordered products has attached an object list filter constraint that limits the selectable products to the list of products offered by the vendor referenced in the vendor object pointer property.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Product : BasicObject {
    string productid not null;
    User defaultagent;
  }

  class Vendor : CompoundObject {
    Product[] offeredproducts;
  }

  class Order : CompoundObject {
    Vendor vendor not null {
      // Boolean filter:
      // Only a vendor offering products can be selected.

      filter = expression {
        offeredproducts != null;
      }
    }
    Product[] orderedproducts not null {
      // Object list filter:
      // If <vendor> is valid and <offeredproducts> contain values,
      // these values can be selected.

      filter = expression {
        vendor.offeredproducts;
      }
    }
    User agent not null {
      // Object list filter with default behavior:
      // If there is a quick search result in ::this,
      // this is used whether the user found is a default agent or not.

      // If no default agents are configured for the ordered products,
      // the default behavior is performed.

      filter = expression {
        if (::this) {
          return OBJECTLIST(::this);
        }
        User[] @agents = orderedproducts.defaultagent;
        if (!@agents) {
          throw #COOSTERR_BREAK;
        }
        OBJECTLIST(@agent);
      }
    }
  }
}

When a value, filter, validation or user interface change constraint is evaluated, the transaction variables of software component COOSYSTEM@1.1 listed in the following table provide you with information on the path to the currently selected property.

Transaction Variable

Description

TV_ATTRPATHATTRDEFS

TV_ATTRPATHATTRDEFS contains the property path to the currently selected property.

For example, if the object pointer property APPDUCXSAMPLE@200.200:product in the third row of the compound property APPDUCXSAMPLE@200.200:orderpositions of an order is selected, TV_ATTRPATHATTRDEFS contains an object list consisting of the elements APPDUCXSAMPLE@200.200:orderpositions and APPDUCXSAMPLE@200.200:product.

TV_ATTRPATHINDICES

TV_ATTRPATHINDICES contains the zero-based indices of the selected rows to the currently selected property.

For example, if the object pointer property APPDUCXSAMPLE@200.200:product in the third row of the compound property APPDUCXSAMPLE@200.200:orderpositions of an order is selected, TV_ATTRPATHINDICES contains an integer list consisting of the elements 2 and 0.

The following example demonstrates how to access the transaction variables provided by software component COOSYSTEM@1.1.

Example

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

  class Product : BasicObject {
    mlname;
    string[] productdescription;
    currency unitprice;
  }

  class Vendor : CompoundObject {
    Product[] offeredproducts;
  }

  struct OrderPosition {
    Vendor vendor;
    Product product {
      // This filter constraint limits the selectable products so that only
      // products offered by the selected vendor can be selected in the

      // APPDUCXSAMPLE@.200.200:product property

      filter = expression {
        Product[] @products = null;
        integer[] @line = #TV.TV_ATTRPATHINDICES[0];
        Object @order = this;
        OrderPosition @orderpositionaggr = @order.orderpositions[@line];
        if (@orderpositionaggr != null) {
          Object @vendor = @orderpositionaggr.vendor;
          if (@vendor != null) {
            @products = @vendor.offeredproducts;
          }
        }
        @products;
      }
    }
    integer quantity;
  }

  class Order : BasicObject {
    OrderPosition[] orderpositions;
  }
}

Defining Validation Constraints for a PropertyPermanent link for this heading

A validation constraint is an app.ducx expression that is evaluated to check whether the value entered into the property is valid in order to prevent invalid data entry into a property.

The expression must return a Boolean value. The return value true signals that the value entered by the user is valid. If the expression returns false, an error message is displayed on the form page containing the property requiring the user to correct the entered value.

Alternatively, the expression can also throw an exception for displaying a custom error message in case the validation fails.

The local scope contains the vApp state dictionary. The global scope contains a dictionary holding the keys listed in the next table.

Key

Description

value

The value key stores the current value of the property.

root

The root key stores the object containing the property.

parent

This key stores the parent of the property. If the property is part of a compound type, the parent is holding the compound property. If the property is not embedded within a compound property, the parent key holds the object containing the property. Thus, for properties that are not part of a compound property, the value stored in the parent key is equal to the value stored in the root key.

attribute

The attribute key contains the property definition.

Example

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

  class Project : CompoundObject {
    // Validation constraint ensures that project priority must be in
    // the range from 1 to 10

    unsigned integer(2) priority not null {
      validate = expression {
        ::value >= 1 && ::value <= 10;
      }
    }
    // COOSYSTEM@1.1:CheckWorkDay throws an exception to show a custom
    // error message if the date is not set to a work day in the

    // future

    datetime kickoffmeeting not null {
      validate = expression {
        ::root.CheckWorkDay(datetime(::value).local, true);
        return true;
      }
    }
    // Validation constraint throwing a custom error message
    date deadline not null {
      validate = expression {
        if (::value > coonow) {
          throw #COODESK@1.1:InValidDate;
        }
        else {
          true;
        }
      }
    }
  }
}

Defining User Interface Change Constraints for a PropertyPermanent link for this heading

A user interface change constraint can be defined in order to trigger the re-evaluation of filter constraints or to set or clear the values of other properties.

The keywords change (value gets changed) and searchchange (value gets changed in context of a search) are used for defining a user interface change constraint. The weight constraint can be used to change the background color of an object. The app.ducx expression language must be used to implement a user interface change constraint.

The local scope contains the vApp state dictionary. When implementing a user interface change constraint in app.ducx expression language, a dictionary holding the keys listed in the previous table is made available in the global scope ::this. The expression must return the value true to trigger a round-trip to the web service, which is necessary for the re-evaluation of filter constraints.

In the following example, the invoice date, the payment date and the processing state properties are cleared if the object pointer property referencing the order is changed.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class<ContentObjectClass> Invoice : ContentObject {
    Order invoiceorder {
      change = expression {
        ::root.invoicestate = "IS_PENDING";
        ::root.invoicedate = null;
        ::root.invoicepaymentdate = null;
      }
    }
    date invoicedate;
    date invoicepaymentdate;
    InvoiceState invoicestate readonly(ui);
  }
}

Object Pointer Property Containing ChildrenPermanent link for this heading

Object pointer properties can be set as child, if the referenced object values do not exist as separate entities; but have a meaning only as part of the container object. For child properties the following are the default values:

  • copy is COOSYSTEM@1.1:NoOperation,
  • accget is COOSYSTEM@1.1:AccTypeReadComp,
  • accset is COOSYSTEM@1.1:AccTypeChangeComp,
  • fixver is COOSYSTEM@1.1:AttrChildrenFixManualVersion,
  • COOSYSTEM@1.1:attrrecursion is false.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : BasicObject {
    OrderPosition[] orderpositions {
      child = true;
      ...
    }
  }
}

After an upload to the configured web service, a recycle is necessary, because GetChildredAttrDef collects the child information of the class attributes only at the first call of the class for performance reason.

Object Pointer Property Describing a HierarchyPermanent link for this heading

Object pointer properties can be set as hierarchy, if the referenced object values are of a similar class as the container so that the values are in a hierarchical context. When selecting a value for a hierarchy property in a search form, the hierarchy relations can be specified in the user interface.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  class Order : BasicObject {
    Order baseorder {
      hierarchy = true;
      ...
    }

    Order[] suborders {
      hierarchy = true;
      ...
    }
  }
}

Defining FieldsPermanent link for this heading

A field is a property that does not belong to an object class. Thus, fields may be used to temporarily compute values that are not persistently stored.

The fields keyword is used to define fields. It must be nested within the objmodel block.

Each field consists of a datatype followed by a reference and a semicolon.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  fields {
    User orderagent;
    currency ordertotal;
    string orderstatusmsg;
  }
}

Extending Existing Component ObjectsPermanent link for this heading

Fabasoft app.ducx allows you to extend existing object classes, enumeration types, and compound types that belong to another friend software component.

Extending an Existing Object ClassPermanent link for this heading

You can extend an existing object class with new properties of your software component.

Please note that you cannot extend object classes not belonging to your software component with properties not belonging to your software component. However, you can reuse existing properties of your software component for extending an existing object class, even if the object class belongs to a different software component.

When extending an existing object class, be sure to add a reference to the software component of this object class. Otherwise, your project will not compile.

The extend keyword must be used for denoting an extension, followed by the class keyword and the reference of the object class to be extended. Then the new properties that should be added to the object class can be defined or referenced inside the class block.

Syntax

extend class reference {
  ...
}

In the following example, object class FSCFOLIO@1.1001:Organisation is extended with a list of projects.

Example

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

  class Project : CompoundObject {
    ...
  }

  // FSCFOLIO@1.1001:Organisation is extended with the object pointer
  // list APPDUCXSAMPLE@200.200:orgprojects

  extend class Organisation {
    Project[] orgprojects;
  }
}

Extending an Existing PropertyPermanent link for this heading

The extend keyword followed by the keyword property and a block holding the properties allows you to extend properties of other software components.

Syntax

extend property reference {
  ...
}

A common use case for this feature is to extend the allowed object classes in object pointer properties of other software components.

Example

objmodel APPDUCXSAMPLE@200.200
{
  // Extend compound type FSCFOLIO@1.1001:Address with a new string
  // property for storing the state

  extend property admobjchildren {
    allow {
      MyConfigClass;
    }
  }
}

Extending an Existing Enumeration TypePermanent link for this heading

With the extend keyword followed by the enum keyword and a block holding the enumeration items, you can add enumeration items to an existing enumeration type belonging to another software component.

Syntax

extend enum reference {
  ...
}

When extending an existing enumeration type, be sure to assign integer values for the new enumeration items that will not conflict with possible entries that might be added in a future version.

For example, the enumeration type FSCFOLIO@1.1001:DocState already contains enumeration items with the integer values of 10, 20, 30 and 40. A logical continuation of this sequence would include the integer values 50, 60, 70 and so. Therefore, you should avoid these integer values when extending the enumeration type.

Example

objmodel APPDUCXSAMPLE@200.200
{
  // Extend enumeration type FSCFOLIO@1.1001:DocState with a new
  // enumeration item for approved documents

  extend enum FSCFOLIO@1.1001:DocState {
    DS_APPROVED = 30000
  }
}

Extending an Existing Compound TypePermanent link for this heading

The extend keyword followed by the keyword struct and a block holding the properties allows you to extend compound types of other software components with properties belonging to your software component.

Syntax

extend struct reference {
  ...
}

When extending a compound type of another software component, reusing properties that belong to other software components is not permitted in the compound type.

Furthermore, be aware that compound types belonging to software component COOSYSTEM@1.1 cannot be extended.

Example

objmodel APPDUCXSAMPLE@200.200
{
  // Extend compound type FSCFOLIO@1.1001:Address with a new string
  // property for storing the state

  extend struct FSCFOLIO@1.1001:Address {
    string(2) addrstate;
  }
}

Defining RelationsPermanent link for this heading

To define a connection between two classes, use the keyword relation. This relation can have 1:1, 1:n or n:m cardinality. A relation can have its own properties.

A special case is a “self-relation”, when the two related classes are identic.

Syntax

// relation without properties
// 1:1 relation
relation<classreferencea, classreferenceb> relationreference;

// relation with properties
// 1:n relation, use [] to denote cardinality

relation<classreferencea, classreferenceb[]> relationreference {
  ...
}

//self-relation
relation<classreference> relationreference;

In order to facilitate the use of relations app.ducx implicitly defines the necessary attributes to have access from the relation instance to the class instances and from class instances to the relation instance. For the relation<ClassA, ClassB> Relation the implicitly defined attributes have the following names:

Name

Container

Type

relation_classa

Relation

ClassA

relation_classb

Relation

ClassB

classa_relation

ClassA

Relation

classb_relation

ClassB

Relation

In case of the self-relation relation<Class> Relation:

Name

Container

Type

relation_class1

Relation

Class

relation_class2

Relation

Class

class_relation

Class

Relation

If ClassA is marked as multiple, then classb_relation is a list: instances of ClassB can have a set of Relation instances with different instances of ClassA.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  //1:n relation:
  //more persons are hired at a company, one person can be hired at one company

  relation<Company, Person[]> Employee {
    date membersince;
    integer contractid;
  }

  //n:m relation:
  //more persons can work on a project, one person can work on multiple projects
  relation<Project[], Person[]> ProjectMember;

  // self-relation
  // 1:1 relation: each person can be married to exactly one other person
  relation<Person> Marriage;

For instance, assume at a company several employees can be hired. This can be modeled as a 1:n relation called Employee between the Company and Person class. In our case, the contractid and membersince properties could describe the Employee relation.

Defining and Extending Component Object InstancesPermanent link for this heading

This chapter describes how to define an extend component object instances.

Defining a New Component Object InstancePermanent link for this heading

Using the instance keyword, you can define instances of component objects that become part of your software component. You can only define instances of object classes that are either directly or indirectly derived from COOSYSTEM@1.1:ComponentObject.

The instance keyword must be followed by the object class of which an instance should be created, and by the reference of the new instance. You can use generic assignment statements inside the instance block to assign values to the properties of the instance.

Syntax

instance objectclass reference {
  ...
}

For maintaining a well-organized project structure, it is recommended that you create a separate .ducx-om file for defining instances of component objects although – from a syntactic point of view – you could define all object model-related elements within a single .ducx-om file.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;
  import FSCFOLIO@1.1001;
  import COOTC@1.1001;
  import COOAR@1.1;
  import FSCCHE@1.1;

  // Definition and initialization of a document category
  instance ComponentDocumentCategory DocumentCategoryOrder {
    dcshortform = "Order";
    categorycommon = false;
  }

  // Definition and initialization of an active report
  
instance ActiveReport_Web CaseReport {
    content = file("resources/test.txt");
    transform<transapp, transpassive, transencoding, transactive,
      transembplain<textembbeg, textembend>> = {
      { APP_NONE, FORMAT_TXT, windows1252, "JavaScript", { { "<%", "%>" } } }
    }
  }
}

Extending an Existing Component Object InstancePermanent link for this heading

Syntax

extend instance <reference> {
  ...
}

Using the extend instance keywords, it is possible to extend an existing component object.

The extend instance keywords must be followed by the reference of the component object to be extended. You can use generic assignment statements inside the extend instance block to assign values to the properties of the instance. The values are added to the current values of the extended object.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  // Exclude order and invoice objects from the list of objects allowed
  // on the user's desk

  extend instance objchildren {
    attrnotallowed<attrallclass, attrallcomponent> = {
      { Order, APPDUCXSAMPLE@200.200 },
      { Invoice, APPDUCXSAMPLE@200.200 }
    }
  }
}

Updating an Existing Component Object InstancePermanent link for this heading

Syntax

update instance <reference> {
  ...
}

Using the update instance keywords, it is possible to update selected properties of an existing component object.

The update instance keywords must be followed by the reference or the object address of the component object to be updated. You can use generic assignment statements inside the update instance block to assign values to properties of the instance. The values of the referenced properties are replaced by the values inside the block.

Example

objmodel APPDUCXSAMPLE@200.200
{
  import COOSYSTEM@1.1;

  // Exclude exclusively order and invoice objects from the list of objects allowed
  // on the user's desk

  update instance objchildren {
    attrnotallowed<attrallclass, attrallcomponent> = {
      { Order, APPDUCXSAMPLE@200.200 },
      { Invoice, APPDUCXSAMPLE@200.200 }
    }
  }
}

Defining Proprietary Types and Property ClassesPermanent link for this heading

In some rare cases it is necessary to define special type instances. This is done by creating an instance of a type class. Properties using this type can be declared by using a special syntax, specifying the property class and the new type.

It is even possible to declare a special property class to be able to specify new property attributes. This class has to be a subclass of an existing property class.

The software component NUMERATOR@1.1001 uses this method to define special property types for key numerators. See the sample code how to use these.

Note: Using proprietary types and property definition classes might result in unexpected behavior of the generic UI controls, so the general advice is not to use this feature unless you are really familiar with the existing object model.

Example

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

  /*
   * new type to be used in this project
   */
  instance TypeStringDef ZIPCODE {
  }

  /*
   * property definition class for properties of new type
   */
  class AttributeZipcodeDef : AttributeStringDef {
    programmatic = true;
    attrinitobjs<attrobjmember, attrinitobjval> = {
      { attrtype, ZIPCODE }
    }
    boolean digitsonly;
  }

  /*
   * class using the new property and type definitions
   */
  class Location {
    AttributeStringDef<ZIPCODE> anyzipcode;
    AttributeZipcodeDef<ZIPCODE> germanzipcode {
      digitsonly = true;
    }
    KeyNumerator<INTEGER> locnumber {
      KeyEntryList<NUMERATOR@1.1001:objclass, keyattrlist> = {
        { Location, objowner }
      }
    }
  }
}

Well-Known Custom Property Classes

There are some property classes already specified in the type system, which can be used with an alias.

Password

Properties of the type password are handled like string properties, but are displayed with the password control. The AttrPrivateSet and the AtrPrivateGet actions are set automatically.

Defining a Loader ObjectPermanent link for this heading

A loader object is a sequence of object descriptions to be created from a data input line consisting of unique columns.

Syntax

loader reference {
  columns {
    Col1, Col2, "Column 3", ...
  }
  ...
}

Columns can be references or strings.

The rest of the loader consists of loader object descriptions. These specify the object class, an optional class index, an optional name and class properties. If the create keyword is used, the object is created if not found.

This is followed by a list of property assignments. Each property assignment can have optional options before the assignment symbol.

Syntax

[create] class [<index>] [as Name] {
  userlogname = column Name; // [1]
    emailinformation = [{ // [2]
    emailaddress MAPOPT_AGGREGATEKEY, MAPOPT_MUSTBEDEF, key = column U1EMail1;
    addrtopic = #TermEMail1;
  },
  {
    emailaddress MAPOPT_AGGREGATEKEY, MAPOPT_MUSTBEDEF = column U1EMail2;
    addrtopic = #TermEMail2;
  }]
  usermemberof = { [3]
    Group
  }
}

Value Assignments

The simplest assignment is just a property definition and a value. The assigned value can be any of the following types:

  • the keyword column and a column as reference or string
  • a fixed string
  • a reference to an object as object address or reference

Aggregate Assignments

To create aggregate properties or aggregate list properties, the values of the aggregate properties have to be specified as assignments just like object descriptions.

Object Assignments

To create object pointer properties, a list of loader object definitions or loader object references can be used.

Loader object references are either the specified name or the object class with an optional index.