- What is Metastorage?
Metastorage is a persistence layer generator application based on the persistence module of the MetaL compiler engine. Metastorage is capable of generating the necessary software components to implement a persistence layer API from a description in a format based on XML named Component Persistence Markup Language (CPML). The generated code is self-contained and does not depend on MetaL or any libraries of code that are not supplied.
- What is MetaL?
MetaL is a meta-programming language compiler engine. Meta-programming is a method to develop programs in high-level language that can be translated into one or more programming languages.
MetaL compiler is able to process source code in XML and generate code in virtually any target language: PHP, Java, Perl, etc..
- What is a persistence layer?
Many applications need to manage information that contain the state of the processes and the entities that are involved in the applications.
The information is generated and stored in some container so it can be retrieved later is known as persistent because it can outlive the process that generated it. Common examples of persistence containers are: relational databases, flat file databases, XML files, LDAP accessible servers, etc..
A persistence layer is an applications programming interface (API) that is used to abstract the operations of storing and retrieving information from a persistence container.
- Motivation and goals
Managing information that is stored in a persistence container is a common need of many applications. However, the effort to implement a persistence layer often takes a significant amount of time and is tedious.
- Reduce the development effort
The main goal of this generator is to drastically reduce the development effort necessary to implement persistence layers without loosing the flexibility to customize the persistence API according to the application needs.
The component description should be simple enough to not take too much time to write by hand.
- Produce optimized code
Another goal is that the generator should produce optimized code according to hints specified in the component description.
The generator should analyze the component description to avoid producing generic code that is not needed by the application.
The code should always be generated according to the best strategy to execute in less time and if possible take less hardware resources.
- Persistence container independent
As any abstraction, the component description and the generated API should be independent of the type of container where the persistent data is stored. Therefore, the component description should not contain details that are specific to a particular type of persistence container.
This provides the flexibility to switch between container types if and when the component design developer finds it convenient.
- Features list
- XML definition format
Easy to write and understand component definition format in XML
- Database schemas
Automatic generation of (database) schema definition of the persistence container
- Automatic generation of all classes
Generates all the code for the data classes, factory class and (database) schema installation class
- Self-contained code
The generated code is self-contained and does not depend on MetaL or libraries of code that are not supplied
- Object Query Language
Provides an Object Query Language (OQL) to define expressions to filter objects in search queries
- No SQL
All code is generated without the need for the developer to write SQL code manually
- Forms
Generation of classes to create, validate and process Web forms to manage objects of the data classes.
- Reports
Generation of classes to execute queries to retrieve data from many objects to elaborate reports or to execute another kind of bulk processing
- UML Diagrams
Automatic generation of Entity-Relationship graphs in UML of the component class diagram
- Data classes
There are several approaches to implement persistence layers. The approach that was used here is object oriented. This persistence layer generator creates a set of data classes that implement the persistence API based on a CPML specification.
The data classes model entities are manipulated by the application. For instance, a content management application would have entities like: article, category, author, etc..
- Variables
The objects of the data classes have variables hold the information of the entities that is meant to be stored and retrieved from the persistence container. For instance, an article class managed by a content management application would have variables like: title, lead, body, date, etc..
- Data types
Variables may be of several basic data types like: text, integer, boolean, float, decimal, date, time and timestamp.
- Object IDentifiers (OID)
Each object of a class is unique. Although different objects of the same class may have variables with the same values, there must be a way to distinguish which object is which, so applications can locate and operate on each object individually.
The way to distinguish each object is by the means of special class variables that are known as Object IDentifiers (OID).
Usually, object identifiers are integer variables that are assigned with unique (sequential) values. Their existence is implicit, so they usually do not need to be explicitly declared and initialized by applications.
In particular situations, object identifiers may be defined explicitly by multiple class variables that have unique combinations of values across all objects of the class.
- Relations
Each component may have defined many data classes. Classes may be related to each other. For instance, in a content management system, an article may be published by an author, the article may belong to one or more categories, a category may contain many articles, etc..
The relations between classes may have different levels of multiplicity: one to one, one to many and many to many.
- References
Variables may also be of a special type of data that is a reference to an object of the same or other classes. A reference denotes a relation of multiplicity one to one.
As a special case, references may be undefined, meaning that an object may or may not reference another object, denoting a multiplicity of one to zero or one.
- Collections
When an object of a class may be related to many objects of the same or other classes, the group of related objects is known as a collection.
Collections are defined within the scope of a class. The definition of a collection specifies the class of the related objects of the collection.
The collection definition also specifies a reference which is either a variable or another collection of the related class. The reference variable or collection also references back to the specified collection.
If the reference specifies a variable of the related class, that denotes a relation of multiplicity one to many. If the reference specifies another collection of the related class, that denotes a relation of multiplicity many to many.
- Validation
The data stored in object may be subject of application specific rules that determine if it is consistent. For instance, in a content management system, the title of an article may not be empty, there may only be one author with given e-mail address, the name of each article category must be unique, etc..
The persistence API may also be able to verify the consistency rules defined by the component design developer when needed. This meant to help to prevent that inconsistent data be stored in the persistence container.
- Functions
Along with the variables, the data classes also contain functions that implement the actions that are important for the applications that use the generated persistence layer.
The generated functions can be of many types like: creating a new data object, validating the object data consistency, storing and retrieving from the persistence container, etc..
The generator produces the necessary code to implement functions specified by the component design developer according to the needs of the application. The definition of some types of functions may contain additional parameters to customize their behavior.
The generator may implicitly generate additional class functions and variables to support the operations that are needed.
- Object factory
Most actions performed using the persistence layer start by creating new data objects by either retrieving them from the persistence container or by creating a new object from scratch.
Data object creation is performed by a special type of class known as the factory class. This class manages the life cycle of all persistent objects. It can create individual objects or retrieve whole collections of objects from the persistence container.
Like the data classes, the functions that are provided by the factory class are specified by the component design developer.
The factory class also provides centralized services like error handling or transaction management.
- Object search filter language
The data object classes and the factory class can have several types of functions that access the persistence storage container and retrieve one or more objects.
Frequently applications need to search and retrieve only the objects that satisfy a given condition. For this purpose, a search filter expression can be associated with any of the functions that retrieve objects of a given class or objects that belong to a collection of related classes.
The CPML also includes the definition an object search filter language to be used in the definition of functions that are meant to return objects that satisfy given search conditions.
- Container schema setup
Before starting to store data class objects, the persistence container may need to be initialized. The initialization usually consists in the installation of a schema to accommodate the data objects.
For this purpose, the generator may also create a special class that can provide services to install and manage the schema of data to be stored in the persistence container.
- XML based component description format
The component description format used by Metastorage is based on XML. The idea is to use a format that is simple to write by hand while it is verbose enough to understand even by developers that are not yet familiar with Metastorage.
Metastorage XML format follows the Simplified XML (AKA Minimal XML) restrictions, i.e. it does use character entities and XML tags have no attributes. This makes the format easier to read while it honors the meaning of the letter X in XML: be eXtensible. If the format XML tags used attributes, it could not be as extensible because attributes may not contain any tags inside them.
- Format structure
The definition of components with CPML starts with the root tag that is always named component. Below you may find a complete example of a component definition using CPML.
Inside the component tag comes the definition of some component attributes and the definition of each of the classes that compose the component.
- Component name
(Required) Components must have a name. This the base name that will be given to the component factory and schema classes that are generated.
- Component description
(Required) Components must have a short description text that will be used for documentation purposes.
- External components
An application is often made of several components that may reference each other classes. A component that references classes of other components must declare them first in a special section that starts with the tag component.
- External component file
The specification of an external component must indicate the name of file that contains its definition. The file name may contain the file path that may be absolute or relative to the current component file path.
- External component name
An external component is defined by its name. This name must be exactly the same that is specified in the external component definition file.
- Data object classes
All data object classes are defined in sections starting with tag class. Alternatively, you may define individual classes in separate XML files. For that you need to use the tag includeclass specifying the class definition file name as value, like in the following example. The contents of the class definition file are same as for classes defined within the component file. The root tag of the class definition files is also class.
<includeclass>edition.class</includeclass>
- Element identifiers
Each class and any of its elements may have associated a custom identifier defined by the designer. This identifier is optional but once it is defined it should not be changed for all the component development lifetime.
An element identifier is defined by the tag id. Its value may be of any type but it must be unique across all types of elements that define their identifiers. Besides the class definitions, the types of elements that may define their unique identifiers are: variables, collections, validation rules and functions.
- Class name
(Required) Each class must have a name. This the same name that will be given to each class that is generated.
- Renamed class previous name
(Required when renaming a class) By default the name of a class is mapped to the name of the table on which the persistent objects are stored when using a database as persistent storage container.
When a class is renamed, the database table must also be renamed. The schema management class generated by Metastorage must know the new table name and the previous name, so it can rename the table without loosing previously stored objects.
The was tag must be used to specify the previous name of a class when it is renamed.
WARNING: when a class, a variable or a collection is renamed, make sure the was tag is set with the correct previous name that was used when the database schema was installed for the first time or updated for the last time. Failure to do so may prevent that the schema is upgraded correctly.
- Class variables
(Required) Each class usually contains several variables that are defined using separate variable sections for each.
- Variable name
(Required) Each variable must have a name. This is the same name that will be given to each variable member in the generated class.
- Renamed variable previous name
(Required when renaming a variable) The was tag must be used to specify the previous name of a variable when it is renamed.
Please read the section about "Renamed class previous name" to learn about important details about renaming classes, variables and collections.
- Variable basic data type
(Required alternative) Each variable must be of a type. The type must be either a basic data type or a reference to a object of the same or another class. The type tag is used to specify the basic data type of a variable. Supported basic data types are: text, integer, boolean, float, decimal, date, time and timestamp.
- Variable large data type
Besides the normal scalar data types, variables may store large data types usually known as BLOBs - Binary Large OBjects. Large data types are suitable for storing binary or text data with length that may exceed 255 bytes. A large data variable is defined by setting the type tag to the value largedata.
Large data variables cannot be assigned or retrieved directly like other type of variables. Instead, the functions of type setlargedata and getlargedata must be used.
- Variable large data binary and text types
Large data variables may be either binary (BLOB - Binary LOB) or text (CLOB - Character LOB). The distinction is made by setting the binary tag to 1 for binary and 0 for text large data variables. By default, large data variables are binary.
- Variable initial value
A variable of a basic data type may have an initial value. That value is automatically assigned to the variable when the object is created.
Also, when a new basic data type variable is added to a class, the initial value is automatically stored in the variable for all the previously persisted objects. This is not the case for large data variables.
The initialvalue tag is used to specify the initial value of a variable.
- Variable creation and updating automatic values
A variable may be set to be initialized automatically with a special value using the autocreate attribute tag. This attribute determines that the variable will be created with an automatic initial value and will never be changed in subsequent updates of the object done using a class persist type function.
Alternatively, a variable may be set to be initialized and updated automatically with a special value using the autoupdate attribute tag. This attribute determines that the variable will be set with an automatic value every time the object is updated using a class persist type function, including when the object is created and stored for the first time.
Either autocreate and autoupdate variables may not be changed directly by the applications access the objects of their classes.
Currently, only the variables of the types date, time and timestamp may be defined as autocreate or autoupdate. The values of either of these attributes may be either localdate and utcdate for date variables, and localtime and utctime for time or timestamp variables. localdate and localtime represent the current date and time in the local time zone and utcdate and utctime represent the current date and time in the UTC (Universal Time) time zone.
- Class object reference variable
(Required alternative) Variables may contain references to objects of the same or other classes. The class tag is used to specify the class of object reference variable. This attribute must contain the name of a class specified in the same component definition or in from another component specified by the component attribute.
- Reference variable of an external component class
A variable may contain a reference to an object of a class of another component. The component tag is used to specify the external component that defines the reference class. The specified external component must be previously declared in the current component definition.
- Optional variable
Setting the value of a variable can be made optional. An optional variable that is not initialized it is said to be set to null. The optional tag is used to specify a boolean value that determines whether it is intended to make a variable optional.
By default, all variables are required (not optional). Attempting to store an object with required variables that are not initialiazed makes the calls to the class persist type function fail.
- Length limited text variable
The length of a text type variable may be limited. The length tag is used to specify an integer value that specifies the maximum length that the variable value may have.
- Multiline text variable
A variable of type text may store multiple lines. The multiline tag is used to specify a boolean value that specifies whether the variable value may spawn multiple lines of text.
- Collections of objects
(Optional) The relation between the objects of a class with a group of objects of the same or other class is specified as a collection. A class may specify one or more collections using separate sections for each.
- Collection name
(Required) Each collection must have a unique name that may not be the same as of any variables of the class.
- Renamed collection previous name
(Required when renaming a collection) The was tag must be used to specify the previous name of a collection when it is renamed.
Please read the section about "Renamed class previous name" to learn about important details about renaming classes, variables and collections.
- Collection class
(Required) A collection references objects a of a specified class.
- Collection of an external component class
A collection may contain objects of a class of another component. The component tag is used to specify the external component that defines the collection class. The specified external component must be previously declared in the current component definition.
- Collection class reference
(Required) A collection reference specifies a variable or another collection of the referenced class.
- Validation rules
(Optional) Each class may have defined a set of validation rules to determine the consistency of the data hold in its objects.
- Validation rule type
(Required) Each validation must be of a specific type. Currently the types of validation that are supported are: notempty and unique.
- Validation error code
(Optional) A class may verify several types of validation rules at once. If one validation rule does not verify, applications may need to know which rule has failed. An errorcode maybe associated with each validation rule to let applications act upon the rule that failed.
- Validation rule specific parameters
(Required depending on rule type) Each type of validation may need additional parameters that define the details of the scope of the validation.
- notempty rule
The notempty validation verifies that one or more text specified class variables are set to a non empty string.
The validation specific parameters consist of the list of variables specified using the variable tag for each variable to be checked.
- unique rule
The unique validation verifies that there is no other stored object with the same combination of values of a group of class variables of a given object.
The validation specific parameters consist of the list of variables specified using the variable tag with the combination of values to be verified as unique.
- Class functions
(Optional) Each data class usually contains several functions that execute actions based on the data stored on the class objects. The class functions are defined using separate function sections for each function.
- Function name
(Required) Each function must have a name. This is the same name that will be given to each function member in the generated class.
- Function type
(Required) Each data class function is of a specified type defined according to the function purpose. Currently the types of functions that are supported are: addtocollection, cloneobject, custom, delete, getcollection, getlargedata, getreference, persist, removefromcollection, setlargedata, setreference and validate.
- Custom function arguments
(Optional) Some types of function may take additional arguments that can be used to configure details of the function behavior using values that are only be defined at run time by the application that calls the function.
This is the case for instance of functions that search for objects of a given class may take arguments that define values that can be used in a filter condition parameters.
A class function may have multiple custom arguments. If there are arguments that were defined but actually are not used in the function, probably because they were forgotten or for some other type of mistake, Metastorage fails specifying which argument was left unused.
Each argument is defined separately in sections that start with the tag argument. The argument details are defined inside the respective section.
- name
Each argument must have a unique name.
- type
The type of the argument is also a required parameter when the argument type is scalar or an array or an hash table. Currently supported scalar argument types are: integer, text, float, decimal, boolean, time, date, timestamp, array and hashtable.
- class
An argument may also be a reference to an object. The class parameter specifies the name of a class of the object argument.
- oid
An object argument may also be passed by using its object identifier value (oid). For object arguments of classes with an implicit oid, the argument is an integer number usually stored in the id class variable. An object identifier arguments is specified using an empty oid parameter.
- component
An argument may be an object of a class defined in an external component definition. The component parameter specifies the name of the external component on which the object class is defined.
- Class function specific parameters
(Required depending on function type) Each type of function may need additional parameters that define the details of the scope of the function.
- addtocollection
A addtocollection type function adds to a collection defined in a class an object of the class specified in the collection definition.
Notice that if the collection establishes a one-to-many type of relationship, the reference variable of the object added to the collection, is changed. So, the added object needs to be persisted to commit the change after calling the addtocollection function.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error. The function takes as argument a reference to the object to be added to the collection.
The function specific parameters are the collection that specifies the class collection name and the object section. This section must define the name of the object argument.
- cloneobject
A cloneobject type function creates a copy of the current object with the same variable values. The object copy is unsaved. Applications must call a persist type function to store the new object.
The generated function is of type OBJECT. It returns an object reference if it succeeded or null if there was an execution error. This function does not take any additional arguments.
- custom
A custom type function may be used to implement customizations of a class using arbitrary code in the target language written by the component designer.
The custom code is defined with the function specific do parameter.
The language parameter defines on which programming language the custom code is written. Only the current target language is supported, so this parameter may be omitted.
The type of the function return value may be specified within the return parameter section. If the function does not return a value, this section should be omitted.
The return type definition may include either a type name or a class name. The supported return types are the same as for the custom function arguments.
The class return type parameter may be used to specify that the return value is an object of a given class. Additionally, the oid return type parameter may be used to specify that function returns an object identifier value of that class, rather than an object reference.
If the specified class belongs to a different component, it must be specified also the component name as a separate return type parameter with the name component.
- delete
A delete type function deletes the object from persistent storage if it was already persisted.
If the object has any collections, it is also removed from those collections. For collections of the deleted object that establish one to many relationships, this means that all objects that belong to such collections will have the respective reference variable set to null before the object is deleted. This meant to assure referential integrity.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error. This function does not take any additional arguments.
- getcollection
A getcollection type function retrieves all the objects that belong to a class collection of a given object.
The generated function is of type ARRAY. It returns an array with a collection of objects if it succeeded or null if there was an execution error.
The only function specific parameter is the collection that specifies the name of the collection of the class to be retrieved.
- getlargedata
A getlargedata type function retrieves data stored in a large data class variable.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error.
The main function specific parameter is the name of the large data variable of the class that should be retrieved.
Currently, a large data variable may be retrieved all at once either to a data string variable, or to a file, or as the output of the current script. The parameter tags data, file and output specify respectively which retrieval mode is used. These tags define sections that specify the name of the argument that will be used to return by reference the retrieved data or the name of the file on which the data will be stored. The output parameter does not take additional arguments.
If a variable is optional, its value may be undefined when it is retrieved. In this case the large data variable contents may not be retrieved and a getlargedata function may fail.
To avoid this problem it may be specified the parameter tag undefined. This tag defines a section that is used to specify the name of an additional boolean argument that is passed to the function by reference to return the information of whether the specified large data variable is undefined.
- getreference
A getreference type function retrieves an object referenced by class variable of a given object.
The generated function is of type OBJECT. It returns an object reference if it succeeded or null if there was an execution error.
The main function specific parameter is the name of the variable of the class that references the object to be retrieved.
Optionally, the oid parameter may be specified to tell that the function should return the object identifier stored in the reference variable, instead of the actual referenced object. The oid parameter tag must be empty.
Usually, a getreference type function may not be used to retrieve an object pointed by a reference variable of a class defined in an external component. However, it can be used to retrieve just the object identifier stored in the reference variable, when the function is defined using the oid parameter. Then that object identifier can be passed to a getobject function of the external component factory class to retrieve the actual object.
- persist
A persist type function stores in the persistence container the changes made to a given object.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error. This function has no specific parameters
- removefromcollection
A removefromcollection type function removes from a collection defined in a class an object of the class specified in the collection definition.
Notice that if the collection establishes a one-to-many type of relationship, the reference variable of the object removed from the collection, is changed. So, the removed object needs to be persisted or permanently deleted to commit the change after calling the removefromcollection function.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error. The function takes as argument a reference to the object to be removed from the collection.
The function specific parameters are the collection that specifies the class collection name and the object section. This section must define the name of the object argument.
- setlargedata
A setlargedata type function sets the value of a large data class variable.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error.
The main function specific parameter is the name of the large data variable of the class that should be changed.
Currently, a large data variable can be set to the contents of a data string variable, or to the contents of a file, or be set to an undefined value if the large data variable is optional.
The parameter tags data, file and undefined specify respectively how to set the large data variable value. These tags define sections that specify the name of the argument that will be used to pass the data string, or the name of the file from which the data will be retrieved. The undefined parameter does not take additional arguments.
- setreference
A setreference type function stores a reference to an object in the class variable of a given object. If a null object is passed by argument the function will store a null value in the object class variable. Changes are only effective when a function of type persist is called on the specified object.
The main function specific parameters consist of the definition of the reference variable and a section with the argument in which it is stored the reference.
Optionally, the oid parameter may be specified to tell that the function reference argument is actually the object identifier to be stored in the reference variable, instead of an object reference. The oid parameter tag must be empty.
Usually, a setreference type function may not be used to store a reference to an object of a class defined in an external component. However, it can be used to store the identifier of the external object in the variable, when the function is defined using the oid parameter.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error.
The only situation on which this type of function may fail is when it is passed a reference to an object that is not yet persisted. This situation exposes a bug in the calling application that must be fixed by invoking the persist type function of the reference object class before passing it to a setreference type function.
- validate
A validate type function validates the consistency of the data of an object verifying all the validation rules defined for the class of the object. Validation rules are verified by the order that they are specified in the component definition.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error. If it succeeded but one validation rule was not verified, the function returns the respective error code in a out argument. The error code value 0 means that all validation rules were verified.
The validation specific parameters consist of a section with the definition of the argument in which it is stored the errorcode. This section must define the name of the error code argument.
- Indexes
(Optional) Each class may have defined a set of indexes to accelerate object queries with conditions that involve one or more class variables.
- Index name
(Required) Each index must have a name that must be unique among all indexes defined in a component or related components.
- Unique index
(Optional) An index may be defined as unique if for all objects of the class there will be at most one object with the same combination of values in the index variables.
- Index variables
Each index must be associated to one or more variables. A variable may only be specified once for each index and may not be of type largedata.
- Variable name
(Required) The definition of each index variable must specify the class variable name.
- Variable sorting
(Optional) The definition of each index variable may specify the sorting order by which a variable is expected to be used in object queries that sort the returned results.
- Object factory class
The object factory class is a special purpose class that is meant to create new objects from scratch or retrieve them from the persistence container.
For each component, there should be only one factory class. Like the data classes, the factory class may also have customizable functions defined in a similar way. The only function definition attributes that are different are the function types and their parameters.
The currently supported factory class function types are: createobject, custom, finishtransaction, getallobjects, getobject and starttransaction.
- createobject
A createobject type function creates a new object of a given class from scratch. The object is only effectively stored in the persistence container when a class function of type persist is called on the created object.
The generated function is of type OBJECT. It returns an object reference if it succeeded or null if there was an execution error.
The only function specific parameter is the class that specifies the name of the class of the object to be created.
- custom
A custom type function may be used to implement customizations of the factory class using arbitrary code written by the component designer in the target language.
The factory class custom functions follows the same definition rules as of data class custom functions.
- finishtransaction
A finishtransaction type function terminates a pending transaction started with a function of type starttransaction.
The generated function is of type BOOLEAN. It returns true if it could finish a transaction, or false if there was an execution error.
The commit parameter must be defined to specify how the transaction should be finish. This parameter may have three possible values: yes to make the function commit the changes made during the transaction, no to rollback any changes, and argument to take a boolean argument that will tell whether the changes should be commited in case the argument is true, or to rollback the changes otherwise.
If the commit parameter value is argument, it must also defined a section starting with the tag argument to specify the name of the argument.
- getallobjects
A getallobjects type function retrieves all objects of a given class from the persistence container.
The generated function is of type ARRAY. It returns an array of object references if it succeeded or null if there was an execution error.
The only function specific parameter is the class that specifies the name of the class of the objects to be retrieved.
- getobject
A getobject type function retrieves an object of a given class from the persistence container specifying as argument the primary key value.
The generated function is of type OBJECT. It returns an object reference if it succeeded or null if there was an execution error.
The function specific parameters are the class that specifies the name of the class of the objects to be retrieved and a id section that specifies name of the argument that will be used to pass the object identifier to the function.
- starttransaction
A starttransaction type function initiates a transaction. Transactions must be ended by calling a function of type finishtransaction.
The generated function is of type BOOLEAN. It returns true if it can start a transaction, or false if there was an execution error. This function does not take any additional arguments.
- Object search filters
Metastorage provides a means to search for objects using special filter parameters. These parameters are actually search condition expressions that are associated with functions that retrieve one or more objects. These expressions are defined with the syntax of the object search filter language that is part of the CPML definition.
The object search functions are actually functions of the type getobject and getallobjects of the factory class and getcollection of the data object classes.
Metastorage provides a means to search for objects using special filter parameters. These parameters are actually search condition expressions that are associated with functions that retrieve one or more objects. These expressions are defined with the syntax of the object search filter language that is part of the CPML definition.
- Filter expressions
Each object search function may be associated with only one filter expression, but there may be many object retrieval functions in one class and each may satisfy different search conditions.
A filter condition is associated to an object retrieval function by adding a filter section inside the function specific parameters section.
A filter section must define a boolean expression composed by a sequence of operand expressions and operators. When the filter expression is evaluated for each of the objects being queried, it determines which objects are selected to be returned as result of the execution of the function.
- Filter operands
Filter operands are expressions that are evaluated when the function is executed.
Each operand expression is represented by a tag with a name that specifies the type of operand. Some types of operands take additional parameters that are specified inside the section defined by the operand tag.
The result of the evaluation of each filter operand expression is a value of a data type that depends on the type of expression.
A considerable list of operand expression types is supported for several distinct purposes.
- variable
- isnull
variable represents an expression that evaluates the value of a given class variable. The type of the expression is the same of the specified variable.
isnull represents a boolean expression that evaluates whether a given class reference variable is set to a null object.
This operand takes as required argument the name of the reference variable to be accessed. As an optional parameter it may also be specified the object of the variable to be accessed. The default object is of the class or collection that is queried by the function to switch the filter is being applied.
Examples:
<variable>
<name>title</name>
<object>article</object>
</variable>
<isnull>
<variable>article</variable>
<object>author</object>
</isnull>
object
The object operand expression type is meant to declare an object of a given class involved in a filter expression that refers to multiple objects of related classes. This type of filter expression is also known as joins, exactly in the same sense of SQL query joins.
The object operand expression itself does not represent a value but rather an alias to an object that is assigned implicitly to another object type expression using for instance the equalto operator.
This operand type takes the name of the object as required parameter. This name will be used by other types of expressions to refer to this object.
The actual class of the object may be defined as an optional parameter. If the class parameter is omitted, it is assumed that the name of class is the same defined by the name parameter.
If the object class is defined in an different component, the component tag should be used to specify the name of that external component.
Example:
<object>
<name>author</name>
<class>writer</class>
</object>
<equalto />
<variable>
<object>article</object>
<name>author</name>
</variable>
collection
The collection operand expression type is meant to specify a collection of objects related to an object of a given class. It should be used in expressions that check the presence of objects in class collections, for instance using the in operator.
This operand type takes the name of the object and the name of the collection as required parameters.
Example:
<object>
<name>article</name>
</object>
<in />
<collection>
<object>author</object>
<name>articles</name>
</collection>
text
integer
float
decimal
boolean
date
time
timestamp
A filter expression may use literal values to represent constants of every data type.
A literal value can be specified with an operand expression defined by tags with the same name as the data type that they represent. The actual constant value should be defined inside the literal expression tag.
Examples:
<text>John</text>
<timestamp>2004-05-28 05:03:04</timestamp>
argument
The argument operand expression type represents a literal value that is passed as argument to the function when it is called.
This expression type should specifies the name of the argument that it represents. The type of the expression is the same as the type of the argument specified in the argument declaration.
Example:
<argument>title</argument>
The date and time expressions allow the use of constant values in expressions that are relative to the current date and time. These constants are computed at runtime, when the query statements are being prepared to be executed.
Each of the supported expressions have variants to express dates and times in two main time zones: the local time zone and the Coordinated Universal Time (UTC) time zone. The UTC expressions have the same names, except that they have the utc suffix.
now
nowutc
Current timestamp
todaytime
todaytimeutc
Current time of the day
today
todayutc
Today's date
time
timeutc
Current time
weekday
weekdayutc
Current day of the week: 1 - Sunday, 2 - Monday, 3 - Tuesday, 4 - Wednesday, 5 - Thursday, 6 - Friday, 7 - Saturday
not
The not operand expression type is meant to negate the logical value of an boolean expression defined inside its tags. The type of this expression value is also boolean.
<not>
<isnull>
<variable>article</variable>
<object>author</object>
</isnull>
</not>
group
The group operand expression type is meant to group a sub-expression assuring that it is evaluated with priority relatively to other expressions that follow the group.
<group>
<variable>
<name>article</name>
</variable>
<minus />
<integer>18</integer>
</group>
<multiplyby />
<argument>factor</argument>
Row grouping expressions are functions that can be used in column value expressions of reports that group rows with the same object variable values.
count
The count expression is a grouping function that returns the number of group rows with the same values that were merged into a single row.
It may take optional parameters that specify an object and an object variable. In this case, the function returns only the number of objects that have that variable set.
<column>
<name>count</name>
<value><count>
<variable>author</variable>
<object>article</object>
</count></value>
</column>
minimum
maximum
The mimimum and maximum expressions are grouping functions that return respectively the lowest and highest value of a given object variable in the grouped rows.
These functions can be used with object variables of the types integer, decimal, float, date, time and timestamp.
<column>
<name>middle</name>
<value>
<group>
<maximum>
<variable>
<name>distance</name>
<object>office</object>
</variable>
</maximum>
<plus />
<minimum>
<variable>
<name>distance</name>
<object>office</object>
</variable>
</minimum>
</group>
<divideby />
<integer>2</integer>
</value>
</column>
sum
average
The sum and average expressions are grouping functions that return respectively the total of the sum and the average of the values of a given object variable in the grouped rows.
These functions can be used with object variables of the types integer, decimal and float.
<column>
<name>total</name>
<value>
<sum>
<variable>
<name>price</name>
<object>item</object>
</variable>
</sum>
</value>
</column>
<column>
<name>average</name>
<value>
<average>
<variable>
<name>price</name>
<object>item</object>
</variable>
</average>
</value>
</column>
Filter operators
The filter operators specify operations that are be used to combine the expression operands and successively evaluate the whole expression.
Usually an operator is placed between two operands: the left operand and the right operand. An expression is evaluated performing the operation with the left and right operands. The resulting value is combined with the operand on the right of a second operator if it exists, and so on until it remains only one result value.
This order of evaluation may not always be exactly like this. Each operation may have a different level of priority. For instance, multiplications and divisions must be evaluated before additions and subtractions.
If the following operator has a greater priority than the current operator, then the whole expression on the right of the following operator it is evaluated before evaluating the current operation.
If it is necessary to assure that an operation between operands is evaluated before the following operation that has higher priority, use the group operand to enclose the sub-expression defined by the operands and operators that need to be evaluated first.
Metastorage filter expression operators are specified using tags with names that clearly identify the operation that they represent.
The supported operators are divided in several groups marked with the respective relative priority level in the front of the operator names.
- equalto (2)
- differentfrom (2)
- morethan (2)
- lessthan (2)
- moreorequalthan (2)
- lessorequalthan (2)
The comparison operations compare the left and right operand expressions. The result of comparison operations is a boolean value that indicates whether the comparison condition is true or false.
The comparison operators take left and right operands of the same type. Those can be of any of the supported types except boolean.
- plus (3)
- minus (3)
- multiplyby (4)
- divideby (4)
The arithmetic operators perform calculations with the operand expressions. The result of arithmetic operations is another value of the same type of the operands.
The arithmetic operators take left and right operands of the same type that must be either of type integer, float or decimal.
- and (1)
- or (1)
The logical operators perform boolean operations only with boolean the operand expressions. The result of logical operations is another boolean value.
The collection operators perform operations over a collection of scalar values or objects.
- in (5)
The in operator can check the presence of a given object in a collection of objects.
<object>
<name>article</name>
</object>
<in />
<collection>
<object>author</object>
<name>articles</name>
</collection>
not in (5)
The not in operator can check the absence of a given object in a collection of objects.
<object>
<name>article</name>
</object>
<not /> <in />
<collection>
<object>author</object>
<name>articles</name>
</collection>
Object declarations
In certain circumstances a filter expression may involve objects that need to be declared before they are used.
When it is necessary to make a previous declaration of an object to be used in a filter expression is associated to an object retrieval function, the object declaration may be done by adding an object section inside the function specific parameters section.
The parameters of an object declaration are the same of the object query join operands that may be used within filter expressions.
Schema setup class
The schema setup class is also a special purpose class that is meant to provide services to setup a data schema to store the objects in the persistence container.
For each component, there should be only one schema setup class. Like the data classes and the factory class, the schema setup class may also have customizable functions defined in a similar way.
The only currently supported schema setup class function types is installschema.
- installschema
A installschema type function does all that is necessary to install the data schema in the persistence container. The meaning of this action depends on the type of persistence container being used.
Currently the only type persistence container that is supported are SQL based relational databases. For this type of persistence container, this function installs the database schema.
The generated function is of type BOOLEAN. It returns true if it succeeded or false if there was an execution error. This function does not take any parameters.
Form classes
A data object form class is a type of class that may be generated to provide a user interface for creating or editing objects of the data classes defined for a component.
All form classes are defined in sections starting with the tag form. Alternatively, you may define individual forms in separate XML files. For that you need to use the tag includeform specifying the form definition file name as value. The contents of the form definition file are same as for forms defined within the component file. The root tag of the form definition files is also form.
- Form templates
Before describing how the form output is generated, it is important to understand the concepts of the template system that is used to build the form output that is presented to the user.
- Template clips
The form output is generated from data taken from template files. The template data is extracted from portions of the template files named clips.
A clip is a section from the template file that is within special delimiter marks. The clip start mark starts with the character { followed by the clip name and then the character }. The clip end mark is similar, except that it starts with the characters {/.
- Template marks
Inside a template clip, there may be other named marks that also start with the character { followed by the mark name and then the end character }.
Template clip marks serve as place holders that determine where it will be placed certain types of information that is evaluated by the template processing engine. For instance, template clip place holder marks are used to determine where each field of the form is going to appear within the template data.
- Conditional template sections
Templates may also have conditional sections. These sections are only processed depending on whether a specific mark is defined. If the conditional mark is not defined, the whole section defined by the conditional marks is skipped as if that template section was not there.
Conditional sections are delimited by two equal marks that start with {? followed by the conditional mark name and then the end character }.
- Input fields, labels and marks
Any type of elements may be inserted in the template place holder positions, but the most important type of elements is the form input fields.
Along with input fields, it may be inserted field labels and marks. Input labels are meant to tell the user what is in each field. Input marks are meant hint the user about whether the field is required, and so needs to be entered, or the field is invalid, and so it needs to be verified and eventually corrected.
The place holder mark name for input fields is the input name followed by the suffix input. For instance, if the input name is somefield, the place holder mark is {somefieldinput}.
Similarly, field label and mark place holder names should have the suffixes label and mark respectively, when they are used in the template.
- Template example
Here is an example of a template clip data section named form:
{form}
<p>{title}<br/>
{?errormessage}<b>{errormessage}</b><br/>{?errormessage}
{somefieldlabel}: {somefieldinput} {somefieldmark}<br/>
{submitinput}</p>
{/form}
Form expressions
Before describing the different aspects of the definition of a form, it is important to understand the concept of form expressions.
Form expressions define values that are evaluated when the form class and data structures are generated. These may represent things like variables of the classes or return values of functions that are called at run time to retrieve the values to be used in the forms.
Form expressions may be string values or boolean conditions. Each expression is specified by a tag with a unique name.
- String expressions
String expressions may be concatenated with other string expressions or interleaved with character data, forming a longer string expression.
Here is the list of the currently supported string expressions:
- errormessage
errormessage represents the message that describes the validation error associated to the first field of the form determined to be invalid. It is usually used to present to the user to explain why the form was not accepted. Error messages may be stored in an automatic form class variable defined in the validation error messages theme section.
Example:
<errormessage/>
formvariable
formvariable represents the value of string custom form variable from those specified in the theme definitions. A form variable expression takes as data argument the name of the variable that is meant to be represented.
Example:
<formvariable>images</formvariable>
formtext
formtext represents one message to present to the user. That message is eventually in an idiom set according to the current user locale.
Form text messages are defined by the application that uses the form class. These messages are stored in an hash array form class variable named text.
A form text expression takes as data argument the name of the text message that it represents. That name is used as index to retrieve the message from the text hash variable.
Example:
<formtext>title</formtext>
skinclip
skinclip represents a text data segment extracted from the template file defined in the form skin.
A skin clip expression takes as data argument the name of the skin template file clip that is meant to be extracted.
Example:
<skinclip>backgroundcolor</skinclip>
conditional
conditional represents a string expression that is evaluated at run time choosing between two possible expressions. The choice depends on the value of a boolean condition also evaluated at run time.
A conditional expression takes as arguments a boolean expression and two string expressions. The boolean expression may be specified within either the tags istrue or isfalse, depending on whether it is intended to evaluate if condition is true or false.
The string expressions are defined within the tags then and else. Each defines the values that will be used in case the condition is true or false. The expression correspondent to else case may be omitted if corresponds to an empty string.
Example:
<conditional>
<istrue><errormessageset/></istrue>
<then>Error: <errormessage /></then>
<else>No validation error</else>
</conditional>
objectvariable
objectvariable represents the value of text variable of a specified object.
A object variable expression takes as arguments another expression that identifies the object that is meant to be accessed and the name of the text variable to be retrieved at run time.
The object and the variable name are specified within the tags object and variable respectively.
Example:
<objectvariable>
<object><formvariable>parent</formvariable></object>
<variable>name</variable>
</objectvariable>
roleimage
roleimage represents the file name of an image of a given role from those specified in the theme definitions. A role image expression takes as data argument the name of the role of the image that is meant to be used.
Example:
<roleimage>cancel</roleimage>
Boolean expressions
Currently, boolean expressions may not combined with any other type of expressions or data.
Here is the list of the currently supported boolean expressions:
- errormessageset
errormessageset represents a condition that determines whether the error message variable was set due to an form field with an invalid value. The evaluation of this condition is done at run time by verifying if the error message variable is set to a non-empty string.
Example:
<errormessageset/>
verifyvariable
verifyvariable represents a condition that determines whether it is invalid a field of the form associated to the specified variable. A verify variable expression takes as data argument the name of the variable that is meant to be verified.
Example:
<verifyvariable>description</verifyvariable>
formvariableisnotnull
formvariableisnotnull represents a condition that determines whether it is not null (is defined) a custom form variable from those specified in the theme definitions. A form variable is not null expression takes as data argument the name of the variable that is meant to be checked.
Example:
<formvariableisnotnull>parent</formvariableisnotnull>
Form name
(Required) Each form must have a name. This the prefix of the name that is given to the form handling class that is generated.
Form type
(Required) Each form is of a type. Currently, only forms of type createobject are supported. See the section on form type specific parameters for more details on the supported form types.
Form theme definitions
Themes are groups of settings that define details of presentation and behavior of the forms that are generated.
Each form may have multiple theme definition sections. However, some theme settings may not be defined more than once in all the theme sections.
A form theme definition section starts with the tag theme.
You may also create individual theme definitions in separate XML files. For that you need to use the tag themefile specifying the theme definition file name as value, like in the following example. The contents of the theme definition file are same as for theme sections defined within the component file. The root tag of the theme definition files is also theme.
<themefile>newsflash.theme</themefile>
- Form variables
The generated form classes may have custom variables. These additional variables may be set by the applications to define values to be used by the form classes for customization purposes.
Form class variables should not be confused with the class variables of the data objects that the forms are intended to manipulate.
A form variable is defined in a theme section starting with the tag formvariable.
- Variable name
(Required) Each form variable must have a unique name.
- Variable type
(Required) Each form is of a type. The variable type is defined by the tag type except for object variables that have their types defined with the class tag.
- Variable default value
A form variable may have a default value. This is the initial value that the variable will have when the form class object is created. If this value is not defined, the variable will be undefined.
- Presentation template
Although the definition of the values to insert in the template place holders is done in a separate section named page, a theme may contain additional definitions for certain template place holder values.
Template definition sections start with the tag template.
- Template variables
Template variables define string expressions that are used in replacement of the template marks of specified names.
Template variables definition sections start with the tag variables.
- Template conditional variables
Template conditional variables define a condition boolean expression and one or two conditional string expressions values. These values are used in replacement of the template marks of specified names, depending on the value of the condition.
If there is a conditional section with the name of a given conditional variable, the content of the conditional section is only outputted when the condition is true.
Template conditional variables definition sections start with the tag conditional.
- Condition
The condition boolean expression may be specified within either the tags istrue or isfalse, depending on whether it is intended to evaluate if condition is true or false.
- Conditional values
The conditional string expression values have to be specified within the tags then and else, for defining the values either for when condition is true or false respectively.
The else value may be omitted. In that case, an empty string value is assumed as default.
- Automatic layout
The layout of the form elements may be designed manually by a developer or a presentation designer, but to gain time, there is also the possibility to have the form templates generated automatically from simple layout hints.
The definition of automatic layout hints is done in a theme section that starts with the tag layout.
- Layout type
(Required) Currently, the layout type is the only automatic layout hint that is supported.
The only type of layout that is supported for now is vertical. In this type of layout, the fields of the form are displayed vertical column of field rows. Each row has a form field with labels on the right side and field marks on the left, if any.
The type of automatic layout is specified with the tag type.
- Presentation skin template
Skins are special purpose templates that involve the body of the form when they are used. Skin templates are like other templates but they have a special mark that determines the place where the form template body is inserted.
The definition of skin templates is done in a theme section that starts with the tag skin.
- Skin template file
(Required) The definition of the skin template file is done with the tag file.
- Skin template clip
The definition of the skin template clip name is done with the tag clip. This definition is optional. The default clip name is form.
- Skin body template variable
The definition of the skin template body variable is done with the tag bodyvariable. This definition is optional. The default body variable name is body.
- Validation error messages
When a form is validated, there may be fields with invalid values. For each invalid field there is an associated error message that may be displayed, so the user can understand why the field value was not accepted as valid.
For that purpose, the generated form class may have a special variable that is used to store the error message associated to the first invalid field.
The definition of error message variables is done in a theme section that starts with the tag errormessage.
- Error message form class variable
(Required) The definition of the validation error message form class variable is done with the tag formvariable.
- Template conditional variable
An error message may be displayed when the form was submitted to the server and it has some invalid fields.
The error message template conditional variable specifies the name of the template mark that defines where the error message will appear. It also defines the name of conditional section of the template that will be displayed only when the validation error message is set.
The definition of the template conditional variable name is done with the tag templateconditional. When this definition is omitted, the error message is not displayed in the form.
- Field marks
To help the user navigation in the generated forms, each field may be displayed along with special marks that denote whether it is a field that requires user input, or it is a field that needs to be verified and corrected by the user, as it was submitted with invalid values.
Field marks are defined with template data that is processed when the form output is composed.
The definition of field marks is done in a theme section that starts with the tag marks.
- Required field mark
The required field mark data is usually displayed near by every field that needs to be entered by the user.
Usually this mark data is defined using a template clip. The definition of the required field mark data is done with the tag required.
- Invalid field verification mark
The invalid field verification mark data is usually displayed near by every field that did not pass the validation checks when the form was submitted. If an invalid field is also a required field, only the field verification mark is displayed.
Usually this mark data is also defined using a template clip. The definition of the field verification mark data is done with the tag verify.
- Presentation default styles and style sheet classes
Since the output of the form elements is only composed when the template is processed, there is no way to define the elements presentation style details directly in the form template clip. To overcome this limitation, the elements fields presentation details may be defined separately in theme sections.
Presentation styles may be defined either as individual style properties or style sheet classes defined according the CSS (Cascaded Style Sheets) standards.
Individual style properties are more appropriate to generate form output that is independent of the style sheets used in the whole page. Style sheet classes are more appropriate to generate more compact form output.
There may be different theme sections to define either style or style sheet classes. These sections start respectively with the tags style and class.
- Input styles
Currently, the only form elements that may have their presentation styles defined separately are the input fields.
The definition of input styles is done in a section that starts with the tag input.
Inside an input styles section it must be included the definition of the styles for each type of form input. Such definition should be in the form of a tag with the name of type of form input. That tag data should be the definition of the input style or name of the style sheet class. Currently, only text input fields are supported.
- Graphical images
A theme may specify use multiple graphical images to better compose the presentation of the generated forms. Each image file that is used must be declared so it is copied to the installation directory when the form classes are generated.
Each image file that is used must be declared in a individual theme section that starts with the tag image.
- Image file
(Required) The definition of the image file name is done with the tag file.
- Image role
Each declared file may have an optional role identification. This identification may serve to determine automatically the name of an image file included for a specific purpose. The image role is usually referenced when using roleimage string expressions.
Currently, the only supported image role is cancel. Images with this role are meant to be used in form canceling submit buttons.
The definition of the image role is done with the tag role.
- Images directory path
To use an image file in Web pages, it is necessary to specify its URL. Usually the URL consists of the image file name preceeded by the URL of its directory path. That may be either relative to the current page or to the current document root directory.
The URL of the images directory path may be hard-coded in the generated form classes, but usually it is more convenient to be defined at run time, for instance in a form class variable.
Regardless what may be more convenient for your application, you may define the image directory path URL in a theme section using the tag imagespath.
Pre-defined themes
To simplify the development, Metastorage already provides a few pre-defined themes that give you a quick choice between alternative options of look and feel for the generated forms.
Use the tag themename to specify the use a pre-defined theme in your form definitions. Currently there are only two predefined themes available: 9x and xp.
The list of pre-defined themes is in a file named themes.themes that is located in the themes sub-directory of the Metastorage installation directory.
To add more pre-defined themes, you need to add a new theme sections to themes.themes file starting with the tag theme.
Inside each new theme section there should be only two tags: name and file. name should be a unique name for the pre-defined theme. file should the path of the theme definitions file relatively to the themes directory. The new theme definitions file should follow rules for separate theme files. All the files that belong to a new pre-defined theme should be located in a sub-directory of the themes directory with the same name of the new theme.
Here is an example of a new pre-defined theme section named bold. It needs to be added to the themes.themes file to be usable by its name in form definitions.
<theme>
<name>bold</name>
<file>bold/bold.theme</file>
</theme>
Form pages
A form may present its fields in one or more Web pages. Currently, only single page forms are supported, but in future versions of Metastorage, it will be possible to split the fields of a form between multiple pages, giving to the user the ability to navigate between them.
The definition of the form page structure is done with the tag page.
- Page template
The layout of each form page is defined using individual templates. The definition of page form templates follows the same structure and has the same default values as the presentation templates defined the theme sections.
The only required parameter is the page template file for forms with custom layout definitions. In this case, the file parameter specifies the path of the file that contains the form template clip to be used. For forms with automatic layout, Metastorage generates a template file automatically, so it is not necessary to specify the page template file path.
Form type specific parameters
Currently, Metastorage only generates one type of form. However, it will support multiple types of form later, each one with a specific set of parameters.
The definition of the form type specific parameters is done in a section that starts with the tag parameters.
- createobject
The createobject type of forms are meant for creating new objects of a given class from user defined data.
- Form object
(Required) The object definition section specifies details associated with object of the class that is meant to be created.
The definition of the form object details is done in a section that starts with the tag object.
- Object class
(Required) The definition of the form object class is done with the tag class.
- Object variables
(Required) For each variable of the class of the object that is meant to be created, there must be a definition of the details that specify how the respective fields will be presented in the form.
The name of the template marks that correspond to each object variable form field is based on the variable name. For instance, if you have an object variable named description, the field mark for the input is {descriptioninput}, for the label is {descriptionlabel} and for the marks is {descriptionmark}.
The definition of the form object variables is done in sections that start with the tag variable.
- Variable name
(Required) The definition of the form object variable name is done using the tag name.
- Variable default value
A variable field may have a default value set by the application at run time. This can be achieved by specifying a form class variable that should be used to specify the default value. The form class variable has necessarily to be of a compatible type.
(Required) The definition of the form variable default value is done using the tag defaultvalueformvariable.
- Variable input label
Form fields may have optional labels that are displayed near them. The label text is a string expression, possibly defined in the current locale idiom. Label text strings should already contain any HTML markup that may be necessary for instance to underscore the field access key.
The definition of the form variable label text is done using the tag label.
- Variable input access key
Form fields may be activated using access keys. These are keyboard shortcut keys that are meant to improve the Web pages usability. Form field access keys are usually defined in concordance with their labels.
The definition of the form fields access keys is done using the tag accesskey.
- Variable alternative value display
Under specific circumstances, certain types of variables may not be defined by the user, entering their values in form fields. That is the case of variables that are class objects.
In that case, their default values need to be specified by the means of form variables. In their place, the forms may display an alternative value. That alternative value is defined as a string expression.
The definition of the form variables alternative display values is done using the tag display.
- Variable input vertical position
When the form layout is defined automatically, the default order of presentation of the fields is defined according to the order of definition of form field variables.
The default vertical position of a form fields may be overridden using the tag verticalposition.
- Default error message
An error message is displayed when the user enters a value in the form input that does not represent a valid value of the associated variable type.
The definition of the form variable default error message is done using the tag errormessage.
- Validation error messages
Before storing a newly created object from the form values entered by the user, the code of the generated form class calls the function of the just created object to validate its variable values.
The validate function returns error codes that are integer numbers. However, to generate an error message that is suitable to present to the user, it is necessary to map each of the possible error codes into meaningful error message strings.
The definition of each of the possible error code messages is done in sections that start with the tag error.
- Error code
(Required) The definition of the error code number is done using the tag code.
- Error message
(Required) The definition of the error message string expression is done using the tag message.
- Form submit and cancel buttons
Create object forms always have at least a submit button. They may also have an optional cancel button, if defined. Both types of buttons may appear in the form either as normal submit buttons, or image input fields, or in both forms.
Either the submit or cancel buttons may have the same set of possible parameters that defined how they appear in the form. The definition of both types of buttons is done in sections that start with the tags submit and cancel respectively.
- Button text value
The definition of the button text value string expression is done using the tag value.
- Button label
The definition of the button label string expression is done using the tag label.
- Button access key
The definition of the button access key string expression is done using the tag accesskey.
- Image button
The definition of the button image file string expression is done using the tag image.
- Image button directory path
The definition of the button image file directory string expression is done using the tag imagepath.
- Image button access key
The definition of the image button access key string expression is done using the tag imageaccesskey.
- Image button label
The definition of the image button label string expression is done using the tag imagelabel.
- Button input field name
The base name for the form template marks used to place submit or cancel fields are by default submit and cancel respectively. For image buttons, append the suffix image to the respective submit button names.
However, if by coincidence these names are already used by an object variable, you should use the tag field to override the default names.
Report classes
A report class is a type of class that may be generated by Metastorage to perform operations that require retrieving significant amounts of data from the persistent objects.
This kind of classes can be used for instance to generate reports to present to the users of an application, or to perform any other kind of bulk processing of the persistent objects data.
A report class may have multiple functions defined by the component designer to execute report queries on data objects of one or more classes.
Each report query may satisfy a filter criteria, also defined by the component designer, that determines from which objects of the involved classes the report information should be retrieved.
The report functions return information in rows of data, like the results of SQL queries. The columns of the retrieved data rows are defined by expressions that can use only part of the variables of the classes of the objects involved in the filter search criteria.
Since the report class functions do not have to retrieve the information of the whole data object, using report classes is a more efficient solution for executing operations that only need to retrieve part of the data from persistent objects, but do not need to change them.
All report classes are defined in sections starting with the tag report. Alternatively, you may define individual report classes in separate XML files. For that you need to use the tag includereport specifying the report definition file name as value. The contents of the report definition file are same as for reports defined within the component file. The root tag of the form definition files is also report.
- Report name
(Required) Each report class must have a name. This the same name that will be given to the class that is going to generated from the report definition.
- Report queries
(Required) A report should define one ore more a queries that will be executed by the report class functions. Each query is defined in a section that starts with the tag query.
- Query name
(Required) Each query must have a unique name among all queries of the same report.
- Query object class declarations
(Required) A query may retrieve information from objects of one or more classes. All the objects of the classes that are accessed by a query must be explicitly declared. The declaration of each object should be done individually in a section named object. The object declaration section consists of the definition of the object name, class and eventual external component name, just like for search filter object declarations.
- Query filter expression
Each query may have associated an optional filter expression to define a search condition that the objects of the declared classes should match. The query search condition is defined within a section named filter. If this search condition is omitted, all the objects of the classes declared for this query are considered. The search condition has the same name syntax as the object search filter expressions.
- Query columns
(Required) When a report query is executed, a variable number of rows of data may be returned as result. Each row is made of columns that define by expression values. These expressions are defined within sections named column.
- Column name
(Required) Every report column must have an unique name among all the columns of a query.
- Column value
(Required) The value of a column is defined by an expression that may combine the values of the variables of objects of the classes that are queried. The syntax of a column value expression is the same as the object search filter expressions, except that instead of defining boolean expressions, the columns may define expressions of other data types, like integer, text, date, etc..
- Query distinct rows
A report query may return rows with duplicated values. The distinct option may be used to make it return only rows with unique combination of values in all columns. This option does not take any values.
- Report sorting order
A report query may return the results sorted according to predefined criteria. The sorting criteria is detailed within sections named order.
- Sorting order columns
The report query sort criteria consists of a list of query columns that specify by which values the query results should be sorted. Each column that defines the sorting criteria should be declared in a section named column. The priority of sorting is defined by the order of appearance of the columns within the order section.
- Order column name
For each column involved in the sorting criteria it must be declared its name.
- Ascending and descending order
The report query results may be sorted according to the values of a column in ascending or ascending order. The ascending order is the default. To set the column order explicitly to ascending or descending order, the order column section must contain an empty tag named ascending or descending respectively.
- Report row grouping
A report query may return results grouping rows that have the same values on certain columns. Such columns are defined within sections named group.
- Row group object variables
The report query group columns are defined by object variables. When grouping is applied to a query, the query columns must use only expressions that involve these object variables or employ row group functions.
- Report functions
(Required) Each report class should define one or more functions that will be generated to execute the report queries and extract the result information. Each report function should be defined within a section named function.
- Function name
(Required) Each report function must have a name. This is the same name that will be given to each function member in the generated report class.
- Function type
(Required) Each report class function is of a specified type defined according to the function purpose. Currently the types of report functions that are supported are: custom, openreport, getreportdata, getallreportdata, getreportfield, getreportrow and getreportsql.
- custom
custom type functions may be used to implement customizations of a report class using arbitrary code written by the component designer in the target language.
The report classes custom functions follow the same definition rules as of data class custom functions.
- openreport
A openreport type function executes a given query leaving it open to subsequentlty retrieve its results with a getreportdata type function.
This type of function takes as required parameter the query name. Optionally it may also take the parameters first, limit and count.
The generated function return type is BOOLEAN. It returns true if the query was executed successfully or false if there was an execution error.
- getreportdata
A getreportdata type function retrieves rows of data resulting from a query executed previously using an openreport type function.
This type of function takes as required parameters the query name, the data argument that returns by reference an array with result row data, and the endofdata argument that returns a boolean also by reference that tells when all rows of result data were retrieved.
The generated function return type is BOOLEAN. It returns true if the query result row data was retrieved successfully or false if there was a retrieval error.
- getallreportdata
getallnamedreportdata
A getallreportdata type function executes a given query and retrieves all the result rows into a bidimensional array.
A getallnamedreportdata type function executes the same query but it returns an array of associative arrays for the result rows. The result rows associative arrays have as indexes the names of the respective query columns.
This type of function takes as required parameters the query name and the data argument that returns by reference an array with data from all the result rows. Optionally it may also take the parameters first, limit and count.
The generated function return type is BOOLEAN. It returns true if the query was executed and the data from the result rows was retrieved successfully, or false if there was an error.
- getreportfield
A getreportfield type function executes a given query and retrieves only the value of the first column of the first result row, even if there are more columns or rows in the query results.
This type of function takes as required parameters the query name and the field argument that returns by reference the first field value of the first result row. The type of the field argument depends on the query first column type. Optionally it may also take the parameters first, limit and count.
The generated function return type is BOOLEAN. It returns true if the query was executed and the field value was retrieved successfully, or false if the query results did not contain any rows or there was another kind of error.
- getreportrow
getnamedreportrow
A getreportrow type function executes a given query and retrieves only the first of the result rows into an array, even if there are more rows in the query results.
A getnamedreportrow type function executes the same query but it returns an associative array for the result row. The result row associative array has as indexes the names of the respective query columns.
This type of function takes as required parameters the query name and the data argument that returns by reference an array with data from the first result row. Optionally it may also take the parameters first, limit and count.
The generated function return type is BOOLEAN. It returns true if the query was executed and the data from the first result row was retrieved successfully, or false if the query results did not contain any rows or there was another kind of error.
- getreportsql
A getreportsql type function returns one or more parts of an SQL SELECT statement that would be used to execute the specified report query.
This may be useful to execute a report query without using any of the classes generated by Metastorage.
It may return the condition to be used with the SQL WHERE, the involved tables list and the columns list.
This type of function takes as required parameters the query name. At least one of these parameters must be specified to define the arguments of the SQL SELECT statement parts that should be returned: condition, tables and columns.
The generated function does not have a return value.
- Custom function arguments
(Optional) Custom function arguments are additional arguments that can be passed to a function at run time to configure details its behavior.
Custom arguments are defined separately in sections that start with the tag argument. The argument details are defined inside the respective section. The report function custom argument parameters are the same as the data class function custom arguments.
- Common report function parameters
(Required depending on function type) Each type of function may need additional parameters that define the details of the scope of the report function. Some types of parameters are common to multiple kinds of functions.
- query
The query parameter defines the name of the query to be executed.
- first
The first parameter specifies an integer expression that defines the first row of the query results that should be returned. The expression should have the same name syntax as the object search filter expressions, except that the expression type must be an integer.
- limit
The limit parameter specifies an integer expression that defines a limit for the number of query results that should be returned. The expression should have the same name syntax as the object search filter expressions, except that the expression type must be an integer.
- count
The count parameter specifies a special function argument that should return by reference the total number rows that satisfy the query filter condition, including any result rows that may not be returned by the query when using the first and limit parameters.
The count parameter should be defined with a section that starts with the count tag. It must include a parameter named argument that should specify the name of the function argument that returns the results row count integer value by reference.
- data
The data parameter specifies a special function argument that should return by reference an array with the data of the query result rows.
The data parameter should be defined with a section that starts with the data tag. It must include a parameter named argument that should specify the name of the function argument that returns the query results data by reference.
- field
The field parameter specifies a special function argument that should return by reference the value of the first column of the first row of the query results.
The field parameter should be defined with a section that starts with the field tag. It must include a parameter named argument that should specify the name of the function argument that returns the field value by reference.
- endofdata
The endofdata parameter specifies a special function argument that should return by reference a boolean flag that tells whether all the result rows were already retrieved.
The end of data parameter should be defined with a section that starts with the endofdata tag. It must include a parameter named argument that should specify the name of the function argument that returns the end of query result data flag value by reference.
- condition
The condition parameter specifies a special function argument that should return by reference a string with an SQL WHERE clause that is used as condition to return only the result rows that satisfy the query filter expression.
The condition parameter should be defined with a section that starts with the condition tag. It must include a parameter named argument that should specify the name of the function argument that returns the SQL query condition string value by reference.
- tables
The tables parameter specifies a special function argument that should return by reference a string with a comma separated list of tables that are used to define from which tables the report query results should be retrieved.
The tables parameter should be defined with a section that starts with the tables tag. It must include a parameter named argument that should specify the name of the function argument that returns the table list declaration string value by reference.
- columns
The columns parameter specifies a special function argument that should return by reference a string with a comma separated list of columns that define the SQL expressions of the columns of the report query results.
The columns parameter should be defined with a section that starts with the columns tag. It must include a parameter named argument that should specify the name of the function argument that returns the column list declaration string value by reference.
Example of component definition
Here follows an example of the definition of a component for an eventual content management system.
<?xml version="1.0" encoding="iso-8859-1"?>
<component>
<name>cms</name>
<description>Content management system component</description>
<component>
<name>accounts</name>
<file>accounts.component</file>
</component>
<class>
<id>1</id>
<name>article</name>
<variable>
<name>title</name>
<type>text</type>
<initialvalue>[no title]</initialvalue>
<length>64</length>
</variable>
<variable>
<name>lead</name>
<type>text</type>
<multiline>1</multiline>
</variable>
<variable>
<name>body</name>
<type>text</type>
<multiline>1</multiline>
</variable>
<variable>
<name>created</name>
<type>timestamp</type>
<autocreate>localtime</autocreate>
</variable>
<variable>
<name>updated</name>
<type>timestamp</type>
<autoupdate>localtime</autoupdate>
</variable>
<variable>
<name>author</name>
<class>writer</class>
</variable>
<collection>
<name>categories</name>
<class>category</class>
<reference>articles</reference>
</collection>
<validation>
<type>notempty</type>
<errorcode>1</errorcode>
<parameters>
<variable>title</variable>
</parameters>
</validation>
<validation>
<type>unique</type>
<errorcode>2</errorcode>
<parameters>
<variable>title</variable>
</parameters>
</validation>
<validation>
<type>notempty</type>
<errorcode>3</errorcode>
<parameters>
<variable>lead</variable>
</parameters>
</validation>
<validation>
<type>notempty</type>
<errorcode>4</errorcode>
<parameters>
<variable>body</variable>
</parameters>
</validation>
<function>
<name>validatearticle</name>
<type>validate</type>
<parameters>
<errorcode>
<argument>errorcode</argument>
</errorcode>
</parameters>
</function>
<function>
<name>getauthor</name>
<type>getreference</type>
<parameters>
<variable>author</variable>
</parameters>
</function>
<function>
<name>setauthor</name>
<type>setreference</type>
<parameters>
<variable>author</variable>
<reference>
<argument>author</argument>
</reference>
</parameters>
</function>
<function>
<name>persistarticle</name>
<type>persist</type>
</function>
<function>
<name>deletearticle</name>
<type>delete</type>
</function>
<function>
<name>copyarticle</name>
<type>cloneobject</type>
</function>
<function>
<name>validateandsave</name>
<type>custom</type>
<parameters>
<return>
<type>boolean</type>
</return>
<do><![CDATA[
if( !$this->validatearticle($errorcode) )
return false;
if( $errorcode != 0 )
{
$this->factory->error = 'the article object is not valid (error code: '.$errorcode.')';
return false;
}
return $this->persistarticle();
]]></do>
</parameters>
</function>
<index>
<name>article_title_author_index</name>
<unique>0</unique>
<variable>
<name>title</name>
<sorting>ascending</sorting>
</variable>
<variable>
<name>author</name>
</variable>
</index>
</class>
<class>
<name>writer</name>
<variable>
<name>alias</name>
<type>text</type>
</variable>
<variable>
<name>user_account</name>
<class>user_account</class>
<component>accounts</component>
</variable>
<variable>
<name>picture</name>
<type>largedata</type>
<binary>1</binary>
<optional>1</optional>
</variable>
<variable>
<name>picture_type</name>
<type>text</type>
<optional>1</optional>
<length>32</length>
</variable>
<variable>
<name>lastarticle</name>
<class>writer</class>
<optional>1</optional>
</variable>
<collection>
<name>articles</name>
<class>article</class>
<reference>author</reference>
</collection>
<validation>
<type>unique</type>
<errorcode>1</errorcode>
<parameters>
<variable>alias</variable>
</parameters>
</validation>
<function>
<name>getarticles</name>
<type>getcollection</type>
<parameters>
<collection>articles</collection>
</parameters>
</function>
<function>
<name>persistwriter</name>
<type>persist</type>
</function>
<function>
<name>addarticle</name>
<type>addtocollection</type>
<parameters>
<collection>articles</collection>
<object>
<argument>article</argument>
</object>
</parameters>
</function>
<function>
<name>setpicture</name>
<type>setlargedata</type>
<parameters>
<variable>picture</variable>
<data>
<argument>picture</argument>
</data>
</parameters>
</function>
<function>
<name>setpicturefromfile</name>
<type>setlargedata</type>
<parameters>
<variable>picture</variable>
<file>
<argument>file_name</argument>
</file>
</parameters>
</function>
<function>
<name>undefinepicture</name>
<type>setlargedata</type>
<parameters>
<variable>picture</variable>
<undefined />
</parameters>
</function>
<function>
<name>getpicture</name>
<type>getlargedata</type>
<parameters>
<variable>picture</variable>
<data>
<argument>picture</argument>
</data>
<undefined>
<argument>undefined</argument>
</undefined>
</parameters>
</function>
<function>
<name>getpictureintofile</name>
<type>getlargedata</type>
<parameters>
<variable>picture</variable>
<file>
<argument>file_name</argument>
</file>
<undefined>
<argument>undefined</argument>
</undefined>
</parameters>
</function>
<function>
<name>outputpicture</name>
<type>getlargedata</type>
<parameters>
<variable>picture</variable>
<output />
<undefined>
<argument>undefined</argument>
</undefined>
</parameters>
</function>
</class>
<class>
<name>category</name>
<variable>
<name>name</name>
<type>text</type>
</variable>
<variable>
<name>description</name>
<type>text</type>
</variable>
<collection>
<name>articles</name>
<class>article</class>
<reference>categories</reference>
</collection>
<function>
<name>addarticle</name>
<type>addtocollection</type>
<parameters>
<collection>articles</collection>
<object>
<argument>article</argument>
</object>
</parameters>
</function>
<function>
<name>removearticle</name>
<type>removefromcollection</type>
<parameters>
<collection>articles</collection>
<object>
<argument>article</argument>
</object>
</parameters>
</function>
<function>
<name>getarticlesbytitleandauthor</name>
<type>getcollection</type>
<argument>
<name>title</name>
<type>text</type>
</argument>
<argument>
<name>author</name>
<type>text</type>
</argument>
<parameters>
<collection>articles</collection>
<object>
<name>author</name>
<class>writer</class>
</object>
<filter>
<variable>
<name>title</name>
<object>article</object>
</variable>
<equalto />
<argument>title</argument>
<and />
<object>
<name>article</name>
</object>
<in />
<collection>
<object>author</object>
<name>articles</name>
</collection>
<object>
<name>author</name>
</object>
<equalto />
<variable>
<object>article</object>
<name>author</name>
</variable>
<and />
<variable>
<object>author</object>
<name>alias</name>
</variable>
<equalto />
<argument>author</argument>
</filter>
</parameters>
</function>
<function>
<name>getarticlesofauthoruser</name>
<type>getcollection</type>
<argument>
<name>author</name>
<class>user_account</class>
<component>accounts</component>
<oid />
</argument>
<parameters>
<collection>articles</collection>
<filter>
<object>
<name>author</name>
<class>writer</class>
</object>
<equalto />
<variable>
<object>article</object>
<name>author</name>
</variable>
<and />
<variable>
<object>author</object>
<name>user_account</name>
<oid/>
</variable>
<equalto />
<argument>author</argument>
</filter>
</parameters>
</function>
</class>
<factory>
<function>
<name>createarticle</name>
<type>createobject</type>
<parameters>
<class>article</class>
</parameters>
</function>
<function>
<name>getallarticles</name>
<type>getallobjects</type>
<parameters>
<class>article</class>
</parameters>
</function>
<function>
<name>getarticle</name>
<type>getobject</type>
<parameters>
<class>article</class>
<id>
<argument>article</argument>
</id>
</parameters>
</function>
<function>
<name>createwriter</name>
<type>createobject</type>
<parameters>
<class>writer</class>
</parameters>
</function>
<function>
<name>getwriter</name>
<type>getobject</type>
<parameters>
<class>writer</class>
<id>
<argument>writer</argument>
</id>
</parameters>
</function>
<function>
<name>getallwriters</name>
<type>getallobjects</type>
<parameters>
<class>writer</class>
</parameters>
</function>
<function>
<name>getarticlesincategory</name>
<type>getallobjects</type>
<argument>
<name>category</name>
<class>category</class>
<oid />
</argument>
<parameters>
<class>article</class>
<filter>
<object>
<name>category</name>
<class>category</class>
</object>
<equalto />
<argument>category</argument>
<and />
<object>
<name>article</name>
</object>
<in />
<collection>
<object>category</object>
<name>articles</name>
</collection>
</filter>
</parameters>
</function>
<function>
<name>getarticlesnotincategory</name>
<type>getallobjects</type>
<argument>
<name>category</name>
<class>category</class>
<oid />
</argument>
<parameters>
<class>article</class>
<filter>
<object>
<name>category</name>
<class>category</class>
</object>
<equalto />
<argument>category</argument>
<and />
<object>
<name>article</name>
</object>
<not /> <in />
<collection>
<object>category</object>
<name>articles</name>
</collection>
</filter>
</parameters>
</function>
<function>
<name>getarticlesofwriter</name>
<type>getallobjects</type>
<argument>
<name>writer</name>
<class>writer</class>
<oid />
</argument>
<parameters>
<class>article</class>
<filter>
<object>
<name>writer</name>
<class>writer</class>
</object>
<equalto />
<argument>writer</argument>
<and />
<object>
<name>article</name>
</object>
<in />
<collection>
<object>writer</object>
<name>articles</name>
</collection>
</filter>
</parameters>
</function>
<function>
<name>getarticlesnotofwriter</name>
<type>getallobjects</type>
<argument>
<name>writer</name>
<class>writer</class>
<oid />
</argument>
<parameters>
<class>article</class>
<filter>
<object>
<name>writer</name>
<class>writer</class>
</object>
<equalto />
<argument>writer</argument>
<and />
<object>
<name>article</name>
</object>
<not /> <in />
<collection>
<object>writer</object>
<name>articles</name>
</collection>
</filter>
</parameters>
</function>
<function>
<name>begin</name>
<type>starttransaction</type>
</function>
<function>
<name>end</name>
<type>finishtransaction</type>
<parameters>
<commit>argument</commit>
<argument>
<name>commit</name>
</argument>
</parameters>
</function>
</factory>
<schema>
<function>
<name>installschema</name>
<type>installschema</type>
</function>
</schema>
<includeform>createarticle.form</includeform>
<includereport>articles.report</includereport>
</component>
Example of form definition
Here follows an example of the definition of a form for submitting a new article to the example content management system.
Form definitions are usually much simpler than in this example when using predefined themes. These already define suitable form specification detail values that do not need to be redefined as in the example that follows.
<?xml version="1.0" encoding="iso-8859-1"?>
<form>
<name>createarticle</name>
<type>createobject</type>
<theme>
<formvariable>
<name>images</name>
<type>STRING</type>
<value>images/</value>
</formvariable>
<formvariable>
<name>author</name>
<class>writer</class>
</formvariable>
<template>
<variables>
<title>New article submission</title>
</variables>
<conditional>
<errormessage>
<istrue><errormessageset/></istrue>
<then>Validation error: <errormessage/></then>
</errormessage>
</conditional>
</template>
<layout>
<type>vertical</type>
</layout>
<skin>
<file>themes/9x/form.html</file>
</skin>
<errormessage>
<formvariable>errormessage</formvariable>
<templateconditional>errormessage</templateconditional>
</errormessage>
<marks>
<required><skinclip>formrequiredmark</skinclip></required>
<verify><skinclip>formverifymark</skinclip></verify>
</marks>
<style>
<input>
<text>border-style: solid ;
border-width: 1px ;
border-color: #CCCCCC</text>
</input>
</style>
<image>
<file>themes/9x/close.gif</file>
<role>cancel</role>
</image>
<imagespath><formvariable>images</formvariable></imagespath>
</theme>
<themename>9x</themename>
<page>
<template>
<file>createarticleform.html</file>
</template>
</page>
<parameters>
<object>
<class>article</class>
<variable>
<name>title</name>
<label><![CDATA[<u>T</u>itle]]></label>
<accesskey>T</accesskey>
<verticalposition>2</verticalposition>
<errormessage>It was not entered a valid name.</errormessage>
</variable>
<variable>
<name>lead</name>
<label><![CDATA[<u>L</u>ead]]></label>
<accesskey>L</accesskey>
<verticalposition>3</verticalposition>
</variable>
<variable>
<name>body</name>
<label><![CDATA[<u>B</u>ody]]></label>
<accesskey>B</accesskey>
<verticalposition>4</verticalposition>
</variable>
<variable>
<name>author</name>
<label>Author</label>
<defaultvalueformvariable>author</defaultvalueformvariable>
<verticalposition>1</verticalposition>
<display><objectvariable>
<formvariable>author</formvariable>
<variable>name</variable>
</objectvariable></display>
</variable>
<error>
<code>1</code>
<message>It was specified an empty article title.</message>
</error>
<error>
<code>2</code>
<message>It was specified an empty article lead text.</message>
</error>
<error>
<code>3</code>
<message>There is already an article with the same title.</message>
</error>
<error>
<code>4</code>
<message>It was specified an empty article body text.</message>
</error>
</object>
<submit>
<value>Submit article</value>
<label>Use this button to </label>
<accesskey>S</accesskey>
</submit>
<cancel>
<value>Cancel</value>
<image><roleimage>cancel</roleimage></image>
<imagepath><formvariable>images</formvariable></imagepath>
</cancel>
</parameters>
</form>
Example of report definition
Here follows an example of the definition of a report for extracting a listing of the latest articles published with the example content management system.
Report class definitions are usually more complex than in this example which just defines one report query and one function to execute it and retrieve the report results.
<?xml version="1.0" encoding="iso-8859-1"?>
<report>
<name>articles</name>
<query>
<name>latestarticles</name>
<object>
<name>article</name>
<class>article</class>
</object>
<filter>
<variable>
<name>created</name>
<object>article</object>
</variable>
<moreorequalthan />
<argument>startdate</argument>
</filter>
<column>
<name>title</name>
<value><variable>
<name>title</name>
<object>article</object>
</variable></value>
</column>
<column>
<name>lead</name>
<value><variable>
<name>lead</name>
<object>article</object>
</variable></value>
</column>
<column>
<name>date</name>
<value><variable>
<name>created</name>
<object>article</object>
</variable></value>
</column>
<distinct />
<order>
<column>
<name>date</name>
<descending />
</column>
</order>
</query>
<function>
<name>getlatestarticles</name>
<type>getallreportdata</type>
<argument>
<name>startdate</name>
<type>timestamp</type>
</argument>
<parameters>
<query>latestarticles</query>
<data>
<argument>articles</argument>
</data>
<first>
<integer>0</integer>
</first>
<limit>
<integer>10</integer>
</limit>
<count>
<argument>total</argument>
</count>
</parameters>
</function>
<query>
<name>authorarticles</name>
<object>
<name>article</name>
<class>article</class>
</object>
<column>
<name>author</name>
<value><variable>
<name>author</name>
<object>article</object>
<oid />
</variable></value>
</column>
<column>
<name>count</name>
<value><count>
<variable>author</variable>
<object>article</object>
</count></value>
</column>
<group>
<variable>author</variable>
<object>article</object>
</group>
</query>
<function>
<name>getauthorarticles</name>
<type>getallreportdata</type>
<parameters>
<query>authorarticles</query>
<data>
<argument>articles</argument>
</data>
</parameters>
</function>
</report>
- Generating component code
Metastorage provides two ways of generating component code: via the metastorage operating system shell command or via the Web interface.
- Metastorage shell command
The Metastorage command consists of a PHP script named metastorage that generates all the files necessary to run applications based on the component classes.
The metastorage script should be run from your operating system command line shell. It takes as argument the path of a component definition file in the CPML format.
The metastorage script may also take as first argument the switch -clean to request that all files generated in a previous build be deleted.
If the php executable program may be found in the default installation directory (/usr/local/bin/php), you may run the metastorage script using a command line like:
metastorage my.component
Otherwise, you may have to specify the php executable program path using a command line like:
/path/to/php -q metastorage my.component
where my.component is the component file that you want to build.
You can compile components from a different directory than the one where the metastorage command is installed. Just use its absolute or relative path from your current directory to the Metastorage directory.
In this case make sure that PHP is executed with the -C switch option, as in the following example, to tell PHP to not change the current directory to metastorage command path:
/path/to/php -C -q /path/to/metastorage my.component
- Command line options
The metastorage command line program provides a few options that can be used to control several aspects of its operation. These options are specified using special command line switches that usually start with the character -.
When you do not know or not remember the name and syntax of the metastorage command line options, you can use just the --help switch to make the command print the usage help message:
/path/to/php -C -q /path/to/metastorage --help
usage: /path/to/metastorage [-h] [-i] [-g] [-c] component
-h, --help usage help
-i, --install installation directory path
-g, --graph UML class diagram graph generation directory path
-c, --clean, -clean remove previously generated files
component path of the component definition file
Here follows a brief explanation of the available options:
- --help
Tell the metastorage command to only present the usage help message.
- --install
Specify the path of the directory where Metastorage will store the generated component runtime classes and script files. If this option is missing, the files will be generated in a sub-directory of the current directory named install.
- --graph
Specify the path of the directory where Metastorage will copy the generated UML diagram graph file in the .dot format. If this option is missing, the graph file will not be copied.
- --clean
Tell Metastorage to remove the files created during a previous compilation of a given component file using the same options.
Metastorage Web interface
The Metastorage Web interface is an experimental Web application written in PHP named WebStorage. It is meant to provide a Web based user interface to the process of generating component code calling the Metastorage compiler.
It is an alternative to the Metastorage shell command meant for those that for some reason cannot run PHP from the command line or would prefer to use a graphical user interface as it may be more intuitive.
This is an experimental application that provides access to files available in the Web server computer disk. Therefore, this application is not really meant to run in a public Web server. It is rather a tool meant to be run in a private Web server running in the developer machine or in a development server accessible in the local network.
Since Metastorage needs to create the files being generated, PHP must not run in safe mode. The operating system user under which it runs the Web server must have sufficient permissions to access to the Metastorage installation directories.
To start this application, just make the directory applications/webstorage/web/ accessible inside your local Web server document tree. Then point your browser to the index.php of that directory. If PHP is correctly installed in your Web server, you will see the WebStorage welcome page.
The welcome page leads the user to the compile page. This page exhibits a window with some compiler setup parameters, like the path of the component file to be compiled. This file can be set using the Change link.
The user may also change the installation directory where generated files will be created, as well specify a directory on which the UML class diagram .dot will be generated if he wants.
The user may specify the component file in the component browsing page. This page shows a directory browser that lets the user navigate on the file system and look for the component file that he wants to be compiled.
The files with the name extension .component appear with a Compile link in the front of their listing line entries. Using this link leads the user back to the compiler setup page.
Once the file of the component to compile is set, the compiler setup window displays the buttons Compile and Clean.
The Compile button starts the compiler execution. This makes a new window appear displaying Metastorage compiler output, which is the same output generated by the metastorage shell command.
If the compilation was successful, another window is displayed with the compilation results and a listing of the files that were generated.
If there was an error, the compilation results window displays the error returned by Metastorage, as well the file name, line number, column number and the position in bytes of the origin of the error that made Metastorage fail.
If you want to clean up the files generated by Metastorage, just use the Clean button of the compiler setup window. That will also make the compiler output window display the clean up process progress messages.
Generated file structure
During the component build process, several files are generated, mostly in two directories: install and work. The number and name of the generated files found in these directories depend on your component definition.
- Installation files
The install sub-directory contains files in the final target language that you will use in your applications. Currently, the Metastorage application only supports PHP, so you only find PHP classes and scripts in this directory.
By default this sub-directory is created in the current directory. The path of the installation directory may be changed directly when using Metastorage Web interface or using the --install option when using Metastorage from the command line.
The generated code uses the Metabase API. In the future versions Metastorage may support other database access APIs. For now, all the necessary Metabase files to use it with MySQL are copied to a sub-directory named metabase. Since Metabase provides a database independent API, if you want to use other databases, you just need to copy the respective driver class files to the metabase sub-directory.
The PHP code generated by Metastorage is self-contained. This means that it does not depend on any other libraries besides the generated classes or other than Metabase. So, you may copy the generated class files to wherever it is more convenient to your application.
The files listed here result from the build of the example component defined in the projects/cms/cms.component file.
- cms.php
This the component factory class. It contains a class named cmsclass.
- article.php, writer.php and category.php
These are the data classes. They contain the classes articleclass, writerclass and categoryclass respectively.
- cmsschema.php
This is the schema setup class. It contains a class named cmsschemaclass.
- cms.schema
This is the database schema definition file ready to install by Metabase.
To help you get started with the database installation, it is also generated a set of scripts in the setup sub-directory:
- install.php
This is a script that uses the cmsschemaclass to install your database.
- global_options.php
This is a script that defines an associative array with option names and their default values. These options are used by the install.php script. May either change the default option values but since this file is overwritten when the component is regenerated, you may prefer to create a script named local_options.php with code to redefine the option values set in the global_options.php script.
- UML class diagram
Besides the installation files, Metastorage may also create a a file that describes an Entity-Relationship graph of the component class diagram using UML notation. The graph file is generated in the DOT language format. This format is used by the Graphviz software package from AT&T research labs.
By default, the graph file is generated in an intermediate work file. If you want to generate this file in given directory, you may specify it directly in Metastorage Web interface or using the --graph command line option. The file name is always the name of the component followed by the .dot extension.
The DOT file can be rendered in many common image formats using Graphviz tools to generate a graph image like in this example rendered from the generated file cms.dot.
- Work directory
The work directory contains intermediate files that are generated by the compiler during the build process. It is located in the Metastorage installation directory.
In previous releases of Metastorage, it used to generate intermediate files in three directories: files, generated and cache. Currently these files are generated in sub-directories of the work directory.
The generated intermediate files are not needed after each compilation. Therefore, they may be removed to save disk space.
Only the cache sub-directory has files that may be reused in subsequent compilations. If the cache files are removed, Metastorage will regenerate them in the next compilation. However, if the cache files are kept between compilations, there may be significant gains in the compilation speed.
Using the generated code
Once you have compiled your component classes with the procedure described before, you are ready to start using the generated code to build your applications.
- Schema installation class
Before starting to run an application that uses the generated code, the first step that you need to take is to install the schema that will hold the information of the objects to be stored persistently.
In the case that the type of persistent storage container is a SQL based database, which is currently the only type that is supported, you need to install a database schema. It consists of tables, fields, indexes and sequence number generators.
Since Metastorage currently uses Metabase to interface with SQL based databases, the schema installation task is greatly simplified.
Metastorage generates a schema installation class that uses Metabase schema manager class to install or update a database schema.
Metastorage also generates a schema description file that has the name extension .schema. This file defines the tables, fields, indexes and sequences schema objects to be installed.
The schema file is in a XML format described in Metabase documentation. Since the schema installation class takes care of the instalation steps, it is not necessary to understand the schema definition file.
All it is necessary to install or update a database schema is to create an object of the schema installation class, set the necessary configuration variables and call the installschema function described below.
The schema installation class name starts with the name of the generated component followed by the suffix schemaclass.
- Variables
- error
(string) This variable stores a string that corresponds to the message about the last error that occurred. See also the description of the error variable of the factory class.
- connection
(string) This variable must be initialized with a connection string URL. See also the description of the connection variable of the factory class.
- includepath
(string) This variable is meant to specify the Metabase installation directory relative to the current script directory. See also the description of the includepath variable of the factory class.
- schemapath
(string) This variable is meant to specify the directory where the schema definition file is located.
Set this variable to the relative or absolute path of schema file directory.
- databasename
(string) This variable is meant to specify the name of the database within which will be created the tables and sequences that will hold the information of the persistent class objects. Different component classes may be stored in the same database as long as the classes have unique names among all the components.
- createdatabase
(boolean) This variable is meant to tell Metabase schema manager whether it should create the database in the first time the schema is installed.
If you are installing multiple components in the same database, only the first component installation may create a database.
Keep in mind that for some types of databases it is not possible to create a new database from programming scripts. See Metabase documentation to learn which types of databases may be created from scripts.
- Functions
The schema installation class only has one function.
- installschema
(boolean) This function is meant to install a schema definition. It calls Metabase schema manager class to process the schema definition file.
If the schema is being installed for the first time, it will call the respective database driver classes to execute the necessary functions or SQL queries to create the tables, indexes or sequences.
If the installation is successful, it will copy the schema definition file to another file in the same directory with the same name appended with suffix .installed. The schema file copy is used to let Metabase schema manager keep track of the last version of the schema that was installed.
If the schema is being updated after a previous installation, Metabase schema manager will compare the new version of the schema with the previously installed. It will only apply the schema changes without disturbing any data that was added since the first time the schema was installed or the last time the schema was updated.
If the schema installation or updating succeeds, this function returns true. Otherwise it returns false, setting the error variable with the relevant error message string.
- Object factory class
The factory is a central class meant to manage the life cycle of the data object classes managed by an application. It may create objects from scratch or retrieve them from persistent storage.
Although data objects may be instantiated directly creating new data class objects, you should always do it using the factory class. The reason for this is that the factory class keeps track of each data object that is created in memory, preventing that the same object be created more than once.
Besides the functions specified in the component definition, the factory class also has public functions to initialize and finalize the resources to access to the persistent container, currently just the database connection. All the accesses to the factory and data object classes should happen between the calls to these two functions.
The database access connection is controlled using some of the public factory class functions.
The factory class also provides package private functions and variables to assist the data classes. Obviously these functions and variables may not be accessed from outside of the package classes. In the future these private functions may be subject of changes without guarantee of backwards compatibility.
- Variables
- error
(string) This variable stores a string that corresponds to the message about the last error that occurred. It provides useful information to let developers to understand why an operation executed by the class has failed.
All the types of generated classes have error variables. They are used for cumulative error handling. This means that if a function may fail due to some error, an error message is stored in the error variable and the function returns a false boolean value to signal the failure.
If the error variable was already set when the function is called, it returns immediately doing nothing, as if the error occurred in that function. This is meant to reduce the need to check for success conditions on every class function that may fail in error.
- connection
(string) This variable must be initialized with a connection string URL. It identifies the type of database to use and other connection details. See Metabase documentation for a more detailed explanation about connection string parameters.
Examples:
type://user:pass@host/database?Opt1=val1&Opt2=val2
mysql://root:@localhost/cms?Port=/var/lib/mysql/mysql.sock
includepath
(string) This variable is meant to specify the Metabase installation directory relative to the current script directory, so Metabase can find the database driver classes that it needs.
Set this variable to the relative or absolute path of Metabase installation directory.
debug
(boolean) This variable is meant to tell Metabase to capture all database access activity information to assist in eventual debugging process the debugging.
debugoutput
(string) This variable is meant to store the debugging information captured by Metabase when the debug variable is set to true. The captured output will only be retrieved and stored in this variable when the finalize function is called.
database
(integer) This variable stores the database access handle that is set by the factory class initialize function.
If an application script already has a database access handle, set eventually during the initialization of another component factory class, this variable may be set before calling the initialize function, and so the database access handle will be reused. In this case the variables connection, includepath, debug, debugoutput will be ignored.
Functions
- initialize
(boolean) This function sets a connection with the database. Keep in mind that Metabase connection setup function may not establish a database connection immediately. The connection may only be established when the first query is executed.
This is meant to avoid needless overhead of establishing a database connection in scripts that end up not needing it. As a consequence, database connection errors may only be returned when the first effective database access happens.
If specified, the database access log capture is started in this function, even if the database connection is not established immediately.
If this function succeeds, it returns true. Otherwise it returns false setting the error variable with a relevant error message.
- finalize
(boolean) This function is meant to return any resources allocated during the access to the data objects. It closes the database connection, if it was opened.
If the class variable debug was set to true, this function also retrieves the debugging output that may have been captured and stores it in the debugoutput variable.
Data object classes
The data object classes are made mostly of functions and variables that correspond to those declared in component definition.
The life time of a data class object starts by calling a createobject type function of the factory class. After setting its data variable values, it should be called a persist type function of the data object class to store it in the persistence storage container.
If you are creating a data object from values defined by the user, taken for instance in forms, it is recommend to call a function of type validate before calling the persist function. This is meant to assure that the object data satisfy all of its class validation rules.
An object that was previously stored may be retrieved using for instance factory class functions of type getobject or getallobjects.
Currently, the class variables are public and so they may be accessed and changed directly by the applications. In the future Metastorage will provide other data access options that will make the data object variables private. Their values will only be accessible via special purpose setter and getter functions.
Besides the declared class variables, Metastorage automatically defines a special variable named id that is meant to store unique identifier integer values. These values are generated automatically when each object is created and stored for the first time.
Class variables that store references to other objects are represented by variables that store the primary key values of the referenced objects. Undefined object variables indicate that the reference does not point to any object. null object values represent undefined object relationships.
Object variables should not be accessed directly. Instead, use functions of the types getreference and setreference to access object variables.
Forms handling classes
The generated forms handling classes follow the Model-View-Controller (MVC) aggregate design pattern to provide a Web interface to let the users interact with the information of the data objects.
Each forms handling class has three functions that are named initialize, process and output. They manage the three aspects of the pattern, respectively the access to the model data, the controller of the submitted forms and the view of the data in the forms.
The flow of usage starts by creating one object of a forms handling class. Some class variables may need to be setup before using the object. Then the initialize function should be called to setup some private variables in preparation to process the forms. Some data objects may be accessed at this point to retrieve any data that may be necessary.
Once the object is initialized, it should be called the process function to validate and process the form. This function may set the class variables processed or canceled, depending on whether the form is being presented to the user for the first time or how the form was submitted.
If the user did not yet submit the form, or it has some invalid data and the form was not canceled, it should be called the output function to present the form to the user.
- Variables
The form handling classes may have either custom variables or base variables meant for specific purposes.
- error
(string) This variable stores a string that corresponds to the message about the last error that occurred. See also the description of the error variable of the factory class.
- processed
(string) This variable stores a condition result determined by the initialize function that indicates whether the form was submitted and processed successfully.
- canceled
(string) This variable stores a condition result determined by the initialize function that indicates whether the user submitted the form using the cancel button. This variable is only declared in the class if the form has a cancel button.
- text
(hash) This variable is an associative array that should be used to store text messages meant to be used in the composition of the form output using formtext string expressions.
- Form type specific variables
The generated form handling classes may have variables that are specific to the type of form.
- createobject forms
The form classes of this type only have one specific variable that is meant to store the created object after the form is submitted and processed successfully. The name of the variable if the same name of the class of the object that was created.
- Functions
- initialize
(boolean) This function initializes the form private variables in preparation to process the form. It takes as argument a reference to a factory class object that was previously initialized.
Since it may access certain data objects to retrieve data that may be needed, this function may fail due to an error. In that case, it sets the error variable and returned false. Otherwise it returns true.
- process
(boolean) This function processes a form. If the form was not yet submitted by the user, this function does not do anything. If the cancel button was used, this function just sets the canceled variable to true.
If the form is being submitted by the user, this function validates the form field values. If the form has some invalid values, this function does not do anything other than keeping track of the fields that are not valid to eventually mark them differently in the form output.
If all fields are valid, this function executes the corresponding action to the type of form being used. If all goes well, this function sets the processed variable to true and returns true. Otherwise, it sets the error variable and returns false.
- output
(string) This form generates the form output to present it to the user. If the class variables processed or canceled are set to true, this function returns an empty string. Otherwise it returns a string that results from the composition of the form output.
Report classes
The generated report classes are self-contained and independent of any other classes generated during a Metastorage project compilation. This means that they can be used without the need for any data or factory classes.
- Variables and functions
Besides the actual functions that are declared in the report definitions, Metastorage also generates a set of public variables and functions that are meant for configuration, initialization and finalization of the operations executed by the class.
Such class public class variables and functions are the same that are generated for object factory classes, despite the two kinds of classes distinct.
Before starting using a report class, an application must at least initialize the connection or database variables and then call the initialize function. Once the application has finished the use of the report class, it must call the finalize function.
|