The concept of class metadata was explained in the section called “Class metadata”. Basically, class metadata can be specified on three levels:
At a global level using
getDefaultsMeta()
from theMetadataHolder
class.At a type level.
At an attribute level.
Metadata specified at a higher level are inherited by objects at lower levels. So if we specify metadata for a type, all attributes of that type inherit such information. If we specify the same metadata on an attribute, the type level metadata are overridden by the attribute level metadata. Global level metadata are inherited by both, the type and attribute objects.
The Type
and Attribute
classes inherit from the MetadataHolder
. MetadataHolder::getDefaultsMeta()
returns a MetadataHolder
instance directly. MetadataHolder
provides its descendants with a dictionary-like interface as illustrated by the following example:
// Global level metadata (all objects will use ADT mapping) MetadataHolder& globalMeta = MetadataHolder::getDefaultsMeta(); globalMeta["db.mapping.type"].setStringValue("object"); // Type level metadata // The ADT type associated with the Person class // will be named Person_type. (The default is tPerson). const Type& t = TypeDesc<Person>::getType(); t["db.type"].setStringValue("Person_type"); // Attribute level metadata // The Person::name attribute will be a 20 characters long string t.getAttribute("name")["db.type.length"].setIntValue(20);
Class metadata can be set in three different ways at the program start-up. First way is to set metadata in the static method iopcInit()
which can be defined for each reflection-capable type. It is used to set type level and attribute level metadata for the class in which it is defined. iopcInit()
methods are invoked during the iopcMeta library initialisation (IopcMeta::Init()
). To demonstrate how iopcInit
is used, we modify the Person
class definition:
class Person : public iopc::OidObject { public: EString name; EShort age; static void iopcInit(iopc::Type& t) { t["db.type"].setStringValue("Person_type"); t.getAttribute("name")["db.type.length"].setIntValue(20); } };
Second way is to implement the MetadataInitializer
interface declared in iopcmeta. It defines only one method - initializeMetadata()
- into which we can insert the metadata initialization code. In this case, the global level values can also be set. The instance of the MetadataInitializer
implementation is then passed as first parameter to any of the iopcmeta, iopcdb or iopclib initialisation methods:
class OurMetadataInitializer : public MetadataInitializer { public: virtual void initializeMetadata(); }; ... void OurMetadataInitializer::initializeMetadata() { // metadata initialisation code // same as in the first example of this section } ... int main(int argc, char *argv[]) { Iopc::init(new OurMetadataInitializer(), LogWriter::ERROR ... ); ... }
The OurMetadataLoader
instance is deallocated during the library initialization after its initializeMetadata()
method is called. During the initialization phase, iopcInit()
methods are invoked before MetadataInitializer::initializeMetadata()
, so the metadata changes done in MetadataInitializer::initializeMetadata()
may overwrite metadata created in the iopcInit()
methods.
iopcmeta contains a MetadataInitializer
implementation named TextFileMetadataLoader
which represents the last way how we can set the class metadata. Its constructor takes a single parameter, a filename (may include a path as well) of a configuration file, which contains a description of metadata assignments. The class is used in the same way as the OurMetadataInitializer
in previous example. When its initializeMetadata()
method is invoked, it loads the configuration file and sets all global level, type level and attribute level metadata according to it.
The format of the configuration file is illustrated by the following example:
# global level defaults[db.mapping.type];string;object # type level ::Person[db.type];string;Person_type # attribute level ::Person::name[db.type.length];int;20
TextFileMetadataLoader
skips all empty lines or lines starting with the '#' character. The file is case-sensitive. Lines describing the metadata contain three semicolon delimited values. First value specifies the location and code of the metadata to be set. It uses the same format as metadata in queries (see the section called “Querying”). Second column denotes the data type of the metadata value. Only string
, int
and bool
values are allowed. Last column contains the metadata value. Boolean values can be either true
or false
.
One additional class based on the MetadataHolder
container class is the GlobalSettings
class. Its global instance is defined in iopccommon/globalSettings.h
. Although it is based on the MetadataHolder
class, it does not contain any class metadata. It is used to provide application-wide settings. The settings can be specified using the following line in the TextFileMetadataLoader
configuration file:
# global settings globals[db.oracle.oidSequence.name];string;MyOIDSeq