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 the MetadataHolder 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