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:
TControl
andTControlFactory
TTableModel
andTTableModelFactory
TObjectModel
andTObjectModelFactory
TObjectAction
andTObjectActionFactory
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:
TMysql
(which supports both MySQL and MariaDB)TPostgresql
TSqlite
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:
TVersionedSqlDatabase
to maintain a change history for a record.TBranchingSqlDatabase
to maintain different versions of a record, for instance, for scenario management.TAuthorizingSqlDatabase
to impose an authorization scheme on tables and queries.THierarchicalSqlDatabase
to supported nested relational tables.TOptimizingSqlDatabase
to rewrite queries to support optimizations the underlying database does not support by itself.
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.