Namespace

  • Namespace

Application

An application extends TApplication either directly or through one of the following base classes:

TDefaultApplication sets up a basic user interface which is convenient for most applications and therefore is the preferred base class for desktop applications. If your application needs to be able to connect to a Charta database and codebase as well then you can use TChartaBasedApplication which takes care of the basic setup involved. You can use TWebServerApplication when you want to create an application which uses a TWebServer. Finally, console applications currently do not need to extend TApplication but this will probably change in the future.

Desktop application

It is convenient to derive your desktop application from TDefaultApplication. This will set up a user interface suitable for most purposes. The resulting user interface is very dynamic as it provides a lot of freedom to its user to rearrange controls on the screen. Also, the user interface is very much object oriented in the sense that most functionality can be accessed by the user by opening and executing functionality on objects that are currently focused or selected.

TDefaultApplication heavily relies on factories to which it delegates the construction of controls and actions for a specific object. The following classes and their respective factories play an important role in a desktop application:

Every user interface component extends TControl. There are a lot of standard components including a button, a table control, a tree control, a split panel, a form panel, and so on. Whenever TDefaultApplication needs a control to display the object the user has opened it uses the installed TControlFactory to create a TControl. The default installed control factory is able to create controls for a variety of objects. However, you can subclass TControlFactory if you need total control over the exact controls that are created for an object.

A table is a powerful means to display and manipulate a variety of (collections of) objects. That is why TTableControl is the most important control used in a desktop application. It uses a TTableModel to translate from user data to a tabular format. The installed TTableModelFactory is used to create a TTableModel for a specific object. It is preferred to first determine whether it is sensible to implement a table model for an object before resorting to creating a custom control for an object.

An other way to link an object to the user interface is through an TObjectModel which is created by the installed TObjectModelFactory. An object model maps user data to a list of named properties together with their values. Please consider whether creating an object model for user data is more appropriate before creating a table model for it. The default installed control factory will use an object model to create a table control when applicable.

Once an object is displayed in the user interface, the user can call functionality on it using the context menu. An option available in this context menu should be an implementation of TObjectAction which is created using the installed TObjectActionFactory.

Finally, the default installed factories will use published properties (and functions in the near future) of objects to generate applicable object and table models by default. So, by carefully designing the published interface of an object you can create a proper user interface for that object without any extra coding and this makes it the preferred way to embed user data in the user interface. Moreover, the published interface of an object can also be used in, for instance, persistence and communication layers.

Web application

You can use TWebServerApplication as the base class for a web server application. This class performs most of the basic setup needed for a working web server. The resulting application can be used both as a desktop application, for interactive debugging, and as a (Windows service), for running unattended in the background. The toolbar provides buttons to install and uninstall the application as a service.

At its base a web server application is made up of the following basic components:

The basic HTTP infrastructure like installing sockets and reading and writing THttpRequest and THttpResponse instances over sockets is handled by a TWebServer. For most purposes the basic implementation of TWebServer will be sufficient. It will translate an HTTP request to a TWebSiteRequest and will forward it to the appropriate TWebSite. In most cases only one such TWebSite will be installed.

A TWebSite is composed of a tree of TWebSiteModule instances. This tree determines the static structure of a web site and is reflected in the URI path. It also is used to create a menu on a web page. The usual way to nest modules is to use a TCompoundWebSiteModule which provides various methods to aggregate over child modules.

Whenever a TWebSiteRequest is handled by a web site, first the URI path is parsed into segments and for every segment a TWebSiteRequestHandler is created, resulting in a path of handlers. The last handler in the path is used to actually handle the request. Earlier handlers in the path are used for other contextual purposes, for instance, to create contexual menus. While parsing the URI path, the handler for the next segment is created by the handler created for the current segment.

To implement the actual functionality for a web site you can use a number of standard modules, for instance:

As said, TCompoundWebSiteModule can be used to nest modules in a variety of ways. TSimpleTableWebSiteModule is used to allow to user to view and edit (SQL) tables, which is suitable and preferred for most instances when the user should be able to manipulate data. To display fully interactive diagrams on a web page you can use a TControlWebSiteModule.

When the default modules are not sufficient and you need to create a custom TWebSiteModule, you can build a tree of TWebPageComponent instances to model the layout of the web page. There are a number of standard implementations of this class which include a form, a button, a text field, a tabbed panel, a suggest field, and so on. A web page component provides a means to interact with the user agent on its own. This is achieved by reserving part of the URI space for a component. This way an HTTP request can be targeted to and handled by a specific component. This is used for instance to allow a TFormSuggestField to update its suggestions when the user interacts with the component.

In the final stage of web page rendering, a TWebPageComponent is rendered to a THtmlElement. If you need a web site module which renders the page in a custom way, you can create an appropriate THtmlElement yourself. HTML elements model the contents of an HTML page using objects. The resulting tree of objects is then rendered to text using a THtmlDocumentWriter, which outputs a the requested HTML dialect and ensures content is properly escaped when needed. In this way the resulting text can be guaranteed to be free of syntax errors.

SQL database

Many applications require acces to an SQL database. Such access is abstracted using TSqlDatabase which enables a variety of implementations, both physical and logical. The following physical database implementations are currently supported:

Of the above implementations TMysql is the most thoroughly supported. Besides being able to connect to a physical database system, TSqlDatabase also provides a means to add extra functionality to an SQL database by preprocessing queries before they are sent to a physical database. Amongst others, the following logical database implementation are available:

A TSqlDatabase descendant usually is not instantiated directly. Instead a TSqlDatabaseConnector should be used. Always be sure to protect the database connection using the following template:

1 2 3 4 5 6 7 Database := Connector.OpenConnection(); try // All code that uses the database connection finally Connector.CloseConnection(Database); end;

TSqlDatabase executes queries which are modelled by a TSqlStatement which is a descendant of TSqlExpression. Most of the SQL language is modelled by the TSqlExpression inheritance hierarchy. Only in the final stage, when an expression actually needs to be sent to a physical database, the expression is rendered to text. The expression tree that models a specific query can be constructed directly or through the use of operator overloading. This approach to handling SQL queries has the following advantages:

  • Expression trees can be rendered to various SQL dialects which enables the support for various database systems, even on the query level.
  • Rendered expression trees can assure proper escaping of all literal values, thereby guaranteeing that SQL injection is not possible.
  • Logical database implementations can directly manipulate the expression trees without parsing an SQL string.
  • Operator overloading makes SQL seem like an integral part of the host programming language.

In order to be able to use tables and views physically present in an SQL database system, you first have to define a global variable of a class that derives from TSqlPhysicalTable or TSqlPhysicalView. In addition to that you can create TSqlSubqueryView descendants to structure your queries by factoring out common subquery expressions. When rendered to an SQL string these subquery views are expanded to their underlying SQL statements. You need to define a TType for every column in a table. Such a type does not need to be supported directly by the physical database system. Instead it is up to the TSqlDatabase implementation for the specific database system to translate the specified type to a type that is actually supported natively by the underlying database. TSqlTableType is an important type that can be used when a column references a row in an other table using its key. User interface components use this information to provide the user with a suggestion drop down to select such a row.

Type system

A common abstraction that is used in many application layers (like SQL and the user interface) is formed by TType and TValue. A TValue models an immutable value and is subclassed to support a wide range of physical storage options. A TType is a key element in creating a sound type system based on a set-theoretic approach. This means that a type is defined by the set of values that belong to it, its so-called value set. Using this definition subtyping relations cleanly translate to subset relations: type A is a subtype of type B when the value set of type A is a subset of type B. Please keep in mind that a value has no type, or more precisely, a value can belong to many types (be part of many value sets). The most specific type for a value V is the type that has a value set with V as its only member. This type can be represented using TValueType.

Task scheduling

To take advantage of parallel computing power without the many risks associated with the synchronization of multiple threads you can use the task system. A TTask models an atomic piece of functionality that can be executed independent of other parts of an application. Because of these properties, tasks can be scheduled and executed asynchronously to (in the background of) the rest of the application. Tasks are typically executed by a TTaskScheduler. This class takes care of maintaining a pool of worker threads, each of which is to able fetch a task from the work queue and execute it. This means that there can be many more tasks than threads. Also, tasks can be scheduled even when other tasks still are being worked on.

Besides ensuring that each task gets executed the task scheduler automatically gathers information on scheduled tasks which can be presented to the user (using progress bars for instance). Also, the scheduler allows tasks to be scheduled dependent on each other. This means you can indicate that you can indicate that a task only can be executed when the tasks it is dependent on have finished first. Finally, you can group tasks in a TTaskGroup to be able to perform operations on a group as a whole. For instance, it is possible to wait on a group of tasks to finish.

You can choose one of the following base class for a custom task implementation:

Use a TTask if your task is a simple atomic operation. Use a TControllableTask if your task is implemented as some kind of iterative procedure. Doing so will allow you to indicate the current progress of your task. In the future the user will also be able to pause, stop and resume a controllable task.

Please make sure while designing and implementing a task, that the task does not use synchronization objects (locks, critical sections, and so on), at least not as a primary means to synchronize operations that are spread over multiple tasks. The task system is designed to free the programmer of complicated synchronization code which more often than not is the source of hard-to-debug unpredictable errors. If data should be shared among tasks, please make sure that that data is read-only, or each task can work on its own copy of the data. If a task produces results it is often a good strategy to keep those results local to the task and schedule a separate task to merge the results of several other tasks after they have finished. Usually it takes far more time to produce results than to merge those results.