Working with the CMLabs CoreLibrary

 

 

In this document you will find the following sections

 

+        Programming with the CoreLibrary

+        Working with Strings

+        Working with Collections

+        Sorting with Collections

+        Working with XML

+        Working with Time

+        Working with Timers

+        Working with Files

+        Working with Queues

+        Working with Threads

+        Working with Networking

+        Working with DLLs and Shared Objects

+        CVML Example

+        Creating third party (your own) CoreLibrary objects

 

Programming with the CoreLibrary

 

To start using the CoreLibrary you will need to do three things:

 

  1. Include the CoreLibrary binary archive into your project when you link
  2. Include the path to the header files
  3. Include one or more header files in your source code

 

How to do this will depend on your operating system and development environment. In most Unix based systems you will do this by editing the Makefile and in Microsoft Visual Studio for Windows you have to edit the project properties. Please see the documentation of your environment for including 1 and 2 above.

 

For point number 3 above you simply need to include the relevant header files into your own source code files. For example, if you wish to use the Dictionary or ObjectCollection classes you will need to include a line at the top saying

 

            #include "Dictionaries.h"

 

and if you wish to use JStrings and JTime you say

 

            #include "JString.h"

            #include "JTime.h"

 

After this you can now use any object in the CoreLibrary anywhere in your code.

 

The following will provide examples of how to work with some of the more common objects. Remember that you can look at the full Doxygen documentation of all the classes and source files as well.

 

Working with Strings

 

A JString is most often referenced by value and is easily declares such as

 

            JString str = "mystring";

 

Almost every function taking a JString as a parameter expects a value, not a pointer, but some do pass a reference.

 

The JString class has a very large zoo of functionality commonly associated with Strings, such as searching for substrings, splitting and merging, replacing, you name it. It really is easier to just look at the JString Doxygen documentation, but here are a few examples.

 

      JString text = "My test string";

      Collection cols = text.split(" ");

 

The Collection cols now contains three entries, namely ‘My’, ‘test’ and ‘string’ – see later how to work with Collections. The same can be accomplished with

 

      cols = text.splitOnWhiteSpaces();

 

and more splitting routines exist, such as

 

      Dictionary dict = text.splitCommandLine();

 

You can extend the String with

 

      text += " my extra text";

 

and you can extract substrings with

 

      JString text2 = text.substring(2, 8);

 

You can also replace text inside the String with

 

      text.replace("my", "your");

 

and most functions have case insensitive variations, such as

 

      text.replaceIgnoreCase("my", "your");

 

Lastly, you can create JStrings much like printf statements with

 

      int i = 4;

      text = JString::format("Hello %d World\n", i);

 

Please have a look at the extensive list of methods in the Doxygen documentation.

 

Working with Collections

 

There are several types of Collections, some completely text-based such as Collection and Dictionary, others object-based such as ObjectCollection and ObjectDictionary and even time-based such as TimeSeries.

 

The Collection is a simple collection of JStrings, which you use like this

 

      Collection col;

      col.add("Test 1");

      col.add("Test 2");

      JString text1 = col.get(0);

      JString text2 = col.get(1);

 

You can ask how many extries any Collection has with

 

      int c = col.getCount();

 

and use this in loops such as

 

      JString text;

      for (int n=0; n<col.getCount(); n++) {

            text = col.get(n);

            if (text.length() > 0) {

                  ...

            }

      }

 

A Dictionary work similarly, but here you have named entries

 

      Dictionary dict;

      dict.put("My Entry 1", "Test 1");

      dict.put("My Entry 2", "Test 2");

      JString text1 = dict.get(0); // or

      text1 = dict.get("My Entry 1");

      JString text2 = dict.get(1); // or

      text2 = dict.get("My Entry 2");

 

and in loops you do

 

      JString key, value;

      for (int n=0; n<dict.getCount(); n++) {

            key = dict.getKey(n);

            value = dict.get(n);

      }

 

When using ObjectCollections and ObjectDictionary, the entries are pointers to Objects, which form the base for every single object in the CoreLibrary including JStrings. This means that you can add any object to a Collection, for example

 

      ObjectDictionary dict;

      dict.put("My Entry 1", new JTime());

      dict.put("My Entry 2", new JTime());

      JTime* t1 = dict.get(0); // or

      t1 = dict.get("My Entry 1");

      JTime* t2 = dict.get(1); // or

      t2 = dict.get("My Entry 2");

  

      JString key;

      Object* value;

      JTime* t;

      for (int n=0; n<dict.getCount(); n++) {

            key = dict.getKey(n);

            value = dict.get(n); // or

            t = (JTime*) dict.get(n);

            if (t != NULL) {

                  ...

            }

      }

 

This includes other collections, which means that you can build a hierarchy of collections of collections of ...

 

Sorting with Collections

 

All collections can now sort, either by key or by value (as of CoreLibrary version 1.0). All four collection types mentioned above (Collection, Dictionary, ObjectCollection, ObjectDictionary) do not sort by default, and they will keep the order in which you added entries. You can resort them at any time and any number of times by using

 

            bool sortKeys();

            bool sortValues();

            bool sortKeysReverse();

            bool sortValuesReverse();

 

If you want to sort your entries automatically you should use one of the sorting versions of the four collections:

 

                        SortedCollection              sorts by string value

            SortedObjectCollection        sorts by object value

            SortedDictionary              sorts by string key

            SortedObjectDictionary        sorts by object key

 

If you wish to change an existing non-sorting collection to a sorting collection you can set

 

            myCollection.sorting = { SORTNONE | SORTBYKEY | SORTBYVALUE | SORTREVERSE };

 

Lastly, you may wish to change the way you retrieve from collections (both sorted and unsorted). For example, if you have a Dictionary with 20000 entries and you know that you normally look for entries near the bottom of the list rather than the top, you can set

 

            myCollection.sorting |= FINDREVERSE

 

so every get(N) or get(String) will start looking from the bottom up rather than top down.

 

Working with XML

 

When you have an Object you can convert it to XML by calling the method

 

      JTime t;

      JString xml = t.toXML();

 

and later instantiate another object by

 

      JTime t2(xml); // or

      t2.fromXML(xml);

  

Usually, when you receive XML either across the network or from a file, you would normally parse the XML into a tree and work with the nodes directly. To parse XML from either a file or a string in memory you would do

 

      XMLParser* parser = new XMLParser();

      parser->parseURL(filename); // or

      parser->parseXML(xml);

 

and when you were done traversing the tree you would

 

      delete(parser);

 

The methods parseXML() and parseURL() return true if the parsing went well or false if an error occurred. In the case of errors you can ask for more information by

 

      JString desc = parser->verifyXML(xml);

 

which will tell you exactly which line and which tag it had a problem with including more information on what the problem was.

 

If the parsing went well you would obtain the root node in the tree with

 

      XMLNode* rootnode = parser->getRootNode();

 

Every time one obtains a pointer to a node one should check whether that is NULL as a precaution. This includes every example below.

 

Each XMLNode has a tag (the name), zero or more parameters (xxx=”yyy”) and zero or more child nodes. The tag name can be found by

 

      JString tagname = rootnode->getTag();

 

and the non-XML text content of the node can be obtained with

 

      JString content = rootnode->getText();

 

All content XML will be present in the form of child nodes, but if one wished to force the issue one can get the full text content even if it is XML by

 

      JString content = rootnode->getTextContent();

 

If you know that a node has a child tag called ‘mytag’ you can get it with

 

      XMLNode* node = rootnode->getChildNode("mytag");

 

and if you just with to get all of the nodes you ask for the collection

 

      //! Get the pointer to the children - don't delete it - and check for NULL!

      ObjectCollection* children = rootnode->getChildTags();

 

which you can then iterate through

 

      XMLNode* node;

      for (int n=0; n<children->getCount(); n++) {

            node = (XMLNode*) children->get(n);

            if (node != NULL) {

                  if (node->getTag().equalsIgnoreCase("mytag")) {

                        ...

                  }

            }

      }

 

To check if a tag has a parameter called myparm=”” you can do

 

      JString parm;

      if (node->hasAttribute("myparam")) {

            parm = node->getAttribute("myparam");

            ...

      }

 

and if you want them all in a dictionary

 

      Dictionary params = node->getAttributes();

  

      JString param, value;

      for (int n=0; n<params.getCount(); n++) {

            param = params.getKey(n);

            value = params.get(n);

            ...

      }

 

You don’t have to worry about deleting nodes or parameters at all as they are always pointers to nodes inside the parser, but remember not to delete your parser until you are done using all the nodes. This is especially important if you instantiate your parser with

 

      XMLParser parser;

      parser.parseXML(xml);

 

as it will be deleted when out of scope.

 

Working with Time

 

The JTime object always work with the highest possible precision of the operating system, which for Windows and Linux means 1 microsecond resolution. This means that you can do extremely accurate timings such as

 

      JTime t1;

      // Do something

      JTime t2;

  

      long dif = t2 - t1;

 

which will give you the millisecond difference between t2 and t1 or

 

      dif = t2.microDifference(t1);

 

which will return the microsecond difference. Internally the second is always used for differencing, meaning that the first is merely the second / 1000.

 

There is a number of ways to print a time

 

      //! Nice long date and time

      JString print();

      //! Only time part (hour, minute, second, ms)

      JString printTime();

      //! Only time part (hour, minute, second)

      JString printTimeSec();

      //! Only time part (hour, minute, second, ms)

      JString printTimeMS();

      //! Very short date print

      JString printDateShort();

      //! Sortable yyyymmdd

      JString printDateSortable();

      //! Nice long date

      JString printDateLong();

      //! Sortable date and time

      JString printStamp();

      //! HH:MM:SS.ms and days, months, years as needed

      JString printDifTime();

 

The latter is used when instantiating a time from a difference, such as

 

      JTime t3 = JTime(t2 - t1);

  

and will print a nicely formatted time which might be ‘2 ms’ or increasingly more information as needed up to the full format including years.

 

Lastly, when a JTime is created, it is always initialised to the time of creation. If you wish you can invalidate a JTime for later checking

 

      JTime t1;

      t1.setInvalid();

  

      if (t1.isValid()) {

            // it is not valid here

      }

  

      t1 = JTime();

  

      if (t1.isValid()) {

            // it is valid here

      }

 

 

Working with Timers

 

A Timer provides the ability to ask for a callback after a certain amount of time. To work with Timers one usually would create a TimerManager, which can create and manage any number of timers.

 

To initiate a timer callback one can use this TimerManager API

 

      //! Create a String Timer without callback

      JString createTimer(JString strData, long ms);

      //! Create an Object Timer without callback

      JString createTimer(Object* obj, long ms);

      //! Create a String Timer with a function callback

      JString createTimer(JString strData, long ms, void (*function)(JString id));

      //! Create an Object Timer with a function callback

      JString createTimer(Object* obj, long ms, void (*function)(Object* object));

      //! Create a String Timer with a TimerReceiver callback

      JString createTimer(JString strData, long ms, TimerReceiver* receiver);

      //! Create an Object Timer with a TimerObjectReceiver callback

      JString createTimer(Object* obj, long ms, TimerObjectReceiver* receiver);

 

Each of these functions give a String ID back which can be used to

 

      //! Cancel Timer

      bool cancel(JString id);

      //! Wait for Timer

      long waitFor(JString id);

      //! How long left of Timer

      long msLeft(JString id);

 

For the callback timers one has to provide a pointer to a function of the form

 

            void (*function)(JString id));

            void (*function)(Object* object));

 

or provide a pointer to a TimerReceiver or TimerObjectReceiver object.

 

Working with Files

 

The JFile object can work with files either with or without instantiation. If one is looking for a one-off file operation such as reading a whole file or writing a whole file, one can use the static JFile interfaces

 

      static JString readAFileASCII(const JString& name);

      static char* readAFileASCII(const JString& name, int &length);

      static char* readAFileBinary(const JString& name, int &length);

      static bool readAFileASCII(const JString& name, char* data, int &length);

      static bool readAFileBinary(const JString& name, char* data, int &length);

  

      static bool writeAFileASCII(const JString& name, const JString& text);

      static bool writeAFileASCII(const JString& name, char* str, int length);

      static bool writeAFileBinary(const JString& name, char* str, int length);

  

      static bool appendToAFileASCII(const JString& name, const JString& text);

      static bool appendToAFileASCII(const JString& name, char* str, int length);

  

      static Collection* getFilesInADir(const JString& name);

      static bool deleteAFile(const JString& name);

      static bool deleteADir(const JString& name, bool recursive);

  

      static unsigned long getAFileSize(const JString& name);

  

      static bool createADir(const JString& fname);

      static bool createADir(const JString& fname, bool recursive);

 

An example of the use of one of these could be reading a text file into a JString

 

      JString str = JFile::readAFileASCII("mydir/myfile");

 

In all file operations one can use / or \ interchangeably both under Windows and UNIX, as they will automatically be replaced with the appropriate file separator.

 

To work more with one particular file or directory, one can instantiate a JFile object with a name and use the standard API to either create, read, write or delete a file or directory, or one can query if the file/dir exists (bool exists()) and whether it is a file or a dir (bool isADir(), bool isAFile())

 

Working with Queues

 

A Queue is simply a first in, first out string or object queue, made safe for multithreading by semaphores and mutexes. One can add() a JString or an Object to a Queue and ObjectQueue, respectively, and one can

 

      JString waitForNewEntryID(long ms);

      bool waitForNewEntryToAppear(long ms);

or

       Object* waitForNewEntry(long ms);

      Object* waitForNewEntry(JString id, long ms);

      Object* retrieveEntry(JString id);

      Object* retrieveEntry(int pos);

      Object* viewEntry(JString id);

      Object* viewEntry(int pos);

 

and likewise for JString’s in the Queue.

 

A RequestQueue and ObjectRequestQueue are request managing objects, where one side can post a request

 

      JString enterRequest(Object* req);

 

get a request ID back and be able to wait for the other side to answer the request

 

            Object* waitForReply(JString id, long ms);

 

The other side can wait for incoming requests

 

      bool waitForNewRequestToAppear(int ms);

      JString waitForNewRequestID(int ms);

 

get the ID and the request itself

 

      Object* getRequest(JString id);

 

and post the answer back with the ID

 

      bool reply(JString id, Object* rep);

 

All of the above examples are equally valid for JStrings instead of Objects using the RequestQueue instead of the ObjectRequestQueue.

 

Working with Threads

 

Any Object can inherit from JThread and by merely implementing the function

 

      void run();

 

one can start, stop and interact with the thread using

 

      bool start();

      bool terminate();

      bool suspend();

      bool resume();

 

      int getPriority();

      bool setPriority(int pri);

 

One can ask

 

      bool isRunning();

      bool isThread();

 

and get statistics with

 

      //! get stat on local thread

      ThreadStat getLocalThreadStatistics();

      //! reset stat on local thread

      bool resetLocalThreadStatistics();

      //! print stat on local thread

      JString printLocalThreadStatistics();

     

      //! get stat on own (calling) thread

      ThreadStat getCallingThreadStatistics();

      //! reset stat on own (calling) thread

      bool resetCallingThreadStatistics();

      //! print stat on own (calling) thread

      JString printCallingThreadStatistics();

  

      ThreadStat getCurrentCPUUsage();

      ThreadStat getAverageCPUUsage();

  

Working with Networking

 

Networking usually has three components, namely a Server, a Client and a Protocol for transmitting data. For TCP networking, the Server will listen on a port to which the Client will connect and start speaking the Protocol.

 

The Network object is a server implementation, which can be asked to listen to a port with

 

      Network* network = new Network(receiver);

      network->addProtocol(new NetMessageProtocol());

      if (!network.init(1500))

            return false;

      network.start();

 

The receiver can be any object implementing the interface TCPReceiver, meaning they define the function

 

      Message* netObjectReceive(Message* msg, NetworkConnection* con);

 

whereto any received Messages are delivered.

 

A NetworkConnection can be created to connect to the Network with

 

      NetworkConnection* con = new NetworkConnection("myserver.net", 1500, new NetMessageProtocol(), NULL);

      if (!con->isConnected())

            return false;

 

      Message* msg = new Message("from", "to", "Hello");

      Message* returnMsg = con->sendReceiveObject(msg, 3000);

 

Any number of supported network protocols can be added to the Network server, including

 

            NetMessageProtocol, NetHTTPProtocol and NetTelnetProtocol

 

and new ones can easily be created.

 

An advanced callback connection can be created by opening a second connection and

 

      NetworkConnection* con = new NetworkConnection("myserver.net", 1500, new NetMessageProtocol(), NULL);

      con->initializeAsReceiver(receiver);

      if (!con->isConnected())

            return false;

 

This way you tell the Network not to try to connect back to you in case it wants to send you unsolicited messages, but to use this second connection for such purposes. This is very useful when connecting through firewalls or routers to private networks.

 

Working with DLLs and Shared Objects

 

The DynamicLoader can be used to load, manage and unload external libraries and their functions. If we have an external library called math.dll (Windows) or libmath.so (UNIX), one can load and unload the library with

 

      bool loadLibrary(JString libname);

      bool unloadLibrary(JString libname);

 

If the library contains the functions

 

      int mathOne(Object* obj);

      int mathTwo(Object* obj);

      int mathThree(Object* obj);

 

one can load the functions using

 

      DLLFunction getFunction(JString funcname);

 

and remember to cast it to the right format, such as

 

      typedef int (* MyFunction)(Object* obj);

           

      DynamicLoader loader;

      loader.loadLibrary("math");

      MyFunction func = (MyFunction*) loader.getFunction("mathOne");

 

One could also directly specify the library name in the function name

 

      DynamicLoader loader;

      MyFunction func = (MyFunction*) loader.getFunction("math::mathOne");

 

One can ask

 

      bool didErrorHappen();

      JString getLastErrorMessage();

 

 

CVML Example

 

CVML is part of the CAVIAR project and describes content in video sequences. An example of CVML is use can be found here.

 

To use CVML one needs to include the CVML header

 

            #include "thirdparty/CVML.h"

           

The XML can be loaded from a file

 

      JString xml = JFile::readAFileASCII(filename);

      if (xml.length() == 0) {

            printf("Could not read sequence file: %s\n", (char*) filename);

            return false;

      }

 

and parsed into a CVMLDataSet object

 

      XMLParser parser;

      if (!parser.parseXML(xml)) {

            printf("Error parsing sequence file: %s\n%s\n", (char*) filename, (char*) parser.verifyXML(xml));

            return false;

      }

  

      CVMLDataSet* newDataSet = new CVMLDataSet();

      if (!newDataSet->fromXML(parser.getRootNode())) {

            printf("Could not understand sequence file: %s\n", (char*) filename);

            return false;

      }

 

Each CVMLDataSet contains a number of CVMLFrames, which contain a number of CVMLObjects and CVMLGroups. A more detailed description of these classes can be found here.

 

 

Creating third party (your own) CoreLibrary objects

 

All CoreLibrary objects share a vast set of basic functionalities, and if you wish to create your own objects it would be to your advantage to create them as part of the CoreLibrary using the Third Party Extension mechanism. The most important benefit from this is that all CoreLibrary objects are automatically converted from and to XML when sent across the network. An example could be:

 

Sender:

            ObjectCollection* col = new ObjectCollection();

            col->add(new JTime());

            col->add(new JString("Hello"));

            col->add(new Bitmap(255, 122));

 

            Message* msg = new Message("Me", "You", "Hello World", col);

            msg = sendReceiveMsgTo("You", msg);

            if ( (msg != NULL) && (msg->type.equals("Hello Yourself")) ) {

                        ...

            }

 

Receiver:

 

            Message* msg = connection->receiveObject(msTimeout);

            if ( (msg != NULL) && (msg->type.equals("Hello World")) ) {

                        ObjectCollection* col = (ObjectCollection*) msg->getObject();

                        if (col != NULL) {

                                    // col now contains three objects: JTime, JString and Bitmap

                                    // all re-instantiated automatically

                        }

            }

 

You can extend the CoreLibrary with your own objects by putting them into the Third Party directories. There is one directory for the source files (/thirdparty) and one for the header files (/include/thirdparty).

 

This is how you create a new CoreLibrary object called MyObject in 9 easy steps:

 

1.      Create your header file MyObject.h and place it in /include/thirdparty

 

2.      MyObject.h should at least

 

          #include "Object.h"

 

          or any other CoreLibrary header

 

3.      Your class should inherit from

 

          class MyObject : public Object {}

 

4.      and has to implement this method:

 

            Object* clone() const;

 

5.      and to work with XML these methods

 

            JString toXML();

            bool fromXML(XMLNode* node);

 

6.      Then create your source file MyObject.cpp and place it in /thirdparty

 

7.      Now add this line to the file /thirdparty/AutoXML.cheader

 

          #include "thirdparty/MyObject.h"

 

8.      and add this to the file /thirdparty/AutoXML.code

 

            else if (tagName.equalsIgnoreCase("MyObject")) {

                        obj = (Object*) new MyObject();

                        obj->fromXML(rootNode);

            }

 

9.      Recompile the CoreLibrary

 

There are a few notes to this procedure

 

 

 

            else if (tagName.equalsIgnoreCase("MyObject")) {

                        obj = (Object*) new MyObject(rootNode);

            }

 

+        The clone() method should return a new instance of your object with exactly the same attributes and values. You should not, however, pass through pointers to objects, but rather make sure that this uses deep copying, so no memory is shared between the new and the old objects.

 

+        There are a number of additional methods which you can overwrite

 

1.    bool equals(const Object* o2) const;

2.    int compare(const Object* o2) const;

3.    JString print();

4.    JString toHTML();

 

1 is used to see if two objects are equal and should return true or false. If you do not overwrite this method the default way is to see if the objects have the same position in memory and are in fact the same instance.

 

2 is used when sorting objects, where you can return 0 for equal, 1 if this object is larger than the o2 and -1 if o2 is bigger. All collections support sorting, both by keys and values.

 

3 and 4 are used to create a custom String or HTML version of the object, for onscreen or in browser use. The default is to print the class name (“MyObject”);