Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

edm::DataBucket Class Reference

Codify and simplify access to a ROOT TFolder. More...

#include <DataBucket.h>

List of all members.

Public Member Functions

 DataBucket (TFolder *top)
template<class T> int Get (const char *path, std::vector< const T * > &v) const
template<class T> T * Put (const T &obj, const char *path, int opt=kFixedSizeObject)
template<class T> int PutArray (const T *obj, const char *path, int nObj, int opt=kFixedSizeObject)
template<class T> int PutVector (const std::vector< T * > &obj, const char *path, int opt=kFixedSizeObject)
void List (const char *opt="*") const
TFolder * GetFolder (const char *path) const
TClonesArray * GetClonesArray (const char *path) const
TObjArray * GetObjArray (const char *path) const
void ClearArrays (const TFolder *f=0)
TFolder * MakeFolder (const char *path)
int MakeFolders (const char *dirs[])
int Remove (const char *path)

Static Public Attributes

const int kFixedSizeObject = 0x01
 Obsolete for ROOT>5.00.

const int kVariableSizeObject = 0x02
 Obsolete for ROOT>5.00.


Private Member Functions

TClonesArray * NewClonesArray (const char *path, const char *classname)
TObjArray * NewObjArray (const char *path, const char *classname)

Static Private Member Functions

void ListFolder (const TFolder &f, int lvl)
void Split (const char *path, char *dir, char *base)

Private Attributes

TFolder * fTopFolder
 Folder managed. Not owned.


Detailed Description

Codify and simplify access to a ROOT TFolder.

Definition at line 22 of file DataBucket.h.


Constructor & Destructor Documentation

edm::DataBucket::DataBucket TFolder *  top  ) 
 

Construct a data bucket to manage the folder "top"

Parameters:
top : the ROOT TFolder to manage

Definition at line 23 of file DataBucket.cxx.

00023 : fTopFolder(f) { }


Member Function Documentation

void edm::DataBucket::ClearArrays const TFolder *  f = 0  ) 
 

Clear the contents of all containers located at and below a folder.

Parameters:
f : pointer of folder to clear
ClearArrays will clear all arrays located at the level of folder f and will recursively descend into all subfolders of f and clear those also.

Definition at line 262 of file DataBucket.cxx.

References fTopFolder.

Referenced by edm::Event::Clear(), and edm::EventHandle::~EventHandle().

00263 {
00264   if (f==0) f = fTopFolder;
00265   TCollection* folders = f->GetListOfFolders();
00266   TIterator*   iter    = folders->MakeIterator();
00267   TObject*      obj  = 0;
00268   TFolder*      fobj = 0;
00269   TClonesArray* carr = 0;
00270   TObjArray*    oarr = 0;
00271   while ( (obj=iter->Next())!=0) {
00272     fobj = dynamic_cast<TFolder*>(obj);
00273     carr = dynamic_cast<TClonesArray*>(obj);
00274     oarr = dynamic_cast<TObjArray*>(obj);
00275     if (fobj) this->ClearArrays(fobj);
00276     if (carr) carr->Clear("C");
00277     if (oarr) oarr->Clear("C");
00278   }
00279   delete iter;
00280 }

template<class T>
int edm::DataBucket::Get const char *  path,
std::vector< const T * > &  v
const
 

Retrieve all objects located in the bucket at a specific path.

Parameters:
path : Unix-style path name to collection
v : On return, vector of pointers to the objects retrieved
Returns:
Count of number of objects correctly read
Exceptions:
edm::Exception if path is invalid or type-conversion from objects found to requested objects fails
The path variable can be used in one of two ways: either to 1) specify a folder name where the collection may be found or 2) to specify the collection exactly.

In the first case, the collection name is guessed based on the class name. So for example, a Get(path,v) where v is a vector of objects of type 'DataObject' and "path=./directory" will retrieve the collection "./directory/DataObjects". This is the simplest method if no casting of object type is required. A code fragement for this example follows:

0000 std::vector<const DataObject*> v; 0001 dataBucket.Get("./directory",v");

In the second case, the user specifies the collection name exactly. In this case, the objects located in the data bucket are case to the type specified by the user. So, for example, if a class DataObject is derived from a base class DataObjectBase one may attempt a Get(path,v) where v is a collection of pointers to DataObjectBase objects and the path might be something like "./directory/DataObjects". In this case, the DataObjects retreived will be cast to their base class DataObjectBase. If the cast fails, an exception is thrown. A code fragment for this example follows:

0000 std::vector<const DataObjectBase*> v; 0001 dataBucket.Get("./directory/DataObjects",v);

Definition at line 104 of file DataBucket.h.

References edm::COLLECTION_NOT_FOUND, GetClonesArray(), GetObjArray(), and edm::TYPE_MISMATCH.

Referenced by main(), and testRead().

00105 {
00106   // Try to find exactly the collection names by
00107   // user. eg. "./dir/Objects" If that fails, assume the user gave
00108   // only a directory. Guess the collection name based on the class
00109   // type
00110   static std::string newPath;
00111   register int i;
00112   
00113   TObjArray* c = 0;
00114   if (c==0) c = this->GetClonesArray(path);
00115   if (c==0) c = this->GetObjArray(path);
00116   if (c==0) {
00117     T tmpItem;
00118     newPath = path;
00119     newPath += "/";
00120     newPath += tmpItem.ClassName();
00121     newPath += "s"; // Object array names are plural.
00122   
00123     if (c==0) c = this->GetClonesArray(newPath.c_str());
00124     if (c==0) c = this->GetObjArray(newPath.c_str());
00125   }
00126   // By this time we need to have a collection in order to proceed
00127   if (c==0) {
00128     // std::cerr << "edm::DataBucket: Couldn't locate collection at "
00129     //           << path << std::endl;
00130     throw Exception(__FILE__,__LINE__,COLLECTION_NOT_FOUND);
00131     return 0;
00132   }
00133   
00134   const T* cc = 0;
00135   int nitem     = c->GetEntries();
00136   int ntot_size = nitem+v.size();
00137   if (ntot_size>(int)v.capacity()) v.reserve(ntot_size);
00138   for (i = 0; i<nitem; ++i) {
00139     cc = dynamic_cast<const T*>(c->At(i));
00140     if (cc==0) {
00141       std::cerr << "Failed to convert objects in array "
00142                 << c->GetName() << " to requested type.";
00143       throw Exception(__FILE__,__LINE__,TYPE_MISMATCH);
00144       assert(cc);
00145     }
00146     v.push_back(cc);
00147   }
00148 
00149   return nitem;
00150 }

TClonesArray * edm::DataBucket::GetClonesArray const char *  path  )  const
 

Return the object array stored at 'path'

Parameters:
path : Location where to look for collection
Returns:
Pointer to TClonesArray if one is found. 0 on failure.

Definition at line 99 of file DataBucket.cxx.

References GetFolder(), and Split().

Referenced by Get(), Put(), PutArray(), and PutVector().

00100 {
00101   char pathto  [strlen(path)+1];
00102   char objarray[strlen(path)+1];
00103   edm::DataBucket::Split(path, pathto, objarray);
00104   
00105   TFolder* f = this->GetFolder(pathto);
00106   if (f==0) return 0;
00107   
00108   TObject* obj = f->FindObjectAny(objarray);
00109   TClonesArray* carray = dynamic_cast<TClonesArray*>(obj);
00110 
00111   return carray;
00112 }

TFolder * edm::DataBucket::GetFolder const char *  path  )  const
 

Return the TFolder stored at 'path'

Parameters:
path : Location where to look for collection
Returns:
Pointer to TFolder if one is found. 0 on failure.

Definition at line 143 of file DataBucket.cxx.

References fTopFolder, and Split().

Referenced by GetClonesArray(), GetObjArray(), MakeFolder(), NewClonesArray(), and NewObjArray().

00144 {
00145   // Check if we've been asked for the top folder
00146   if (strcmp("",  path) == 0) { return fTopFolder; }
00147   if (strcmp(".", path) == 0) { return fTopFolder; }
00148   if (strcmp("/", path) == 0) { return fTopFolder; }
00149 
00150   char pathto[strlen(path)+1];
00151   char folder[strlen(path)+1];
00152   edm::DataBucket::Split(path, pathto, folder);
00153   
00154   TFolder* f1 = this->GetFolder(pathto);
00155   if (f1==0) return 0;
00156   
00157   TFolder* f2 = dynamic_cast<TFolder*>(f1->FindObjectAny(folder));
00158   return f2;
00159 }

TObjArray * edm::DataBucket::GetObjArray const char *  path  )  const
 

Return the object array stored at 'path'

Parameters:
path : Location where to look for collection
Returns:
Pointer to TObjArray if one is found. 0 on failure.

Definition at line 121 of file DataBucket.cxx.

References GetFolder(), and Split().

Referenced by Get(), Put(), PutArray(), and PutVector().

00122 {
00123   char pathto[strlen(path)];
00124   char objarray[strlen(path)];
00125   edm::DataBucket::Split(path, pathto, objarray);
00126   
00127   TFolder* f = this->GetFolder(pathto);
00128   if (f==0) return 0;
00129   
00130   TObject* obj = f->FindObjectAny(objarray);
00131   TObjArray* oarray = dynamic_cast<TObjArray*>(obj);
00132 
00133   return oarray;
00134 }

void edm::DataBucket::List const char *  opt = "*"  )  const
 

List the entire contents of the data bucket on the screen.

Parameters:
opt : Unused place holder for options.

Definition at line 246 of file DataBucket.cxx.

References fTopFolder, and ListFolder().

Referenced by main(), edm::Event::Print(), testMakeOne(), and testWrite().

00247 {
00248   if (!opt) std::cout << "Listing folder with no option." << std::endl;
00249   edm::DataBucket::ListFolder(*fTopFolder, 1);
00250 }

void edm::DataBucket::ListFolder const TFolder &  f,
int  lvl
[static, private]
 

Print the contents of the folder to the screen

Parameters:
f : the Folder who's contents to print
lvl : depth of this folder. Used for indentation level.

Definition at line 211 of file DataBucket.cxx.

Referenced by List().

00212 {
00213   std::string pad;
00214   for (int i=0; i<lvl-1; ++i) pad += "  ";
00215   if (lvl>0) pad += "|-";
00216   pad += "* ";
00217 
00218   TCollection* folders = f.GetListOfFolders();
00219   TIterator* iter = folders->MakeIterator();
00220   TObject*   obj  = 0;
00221   TFolder*   fobj = 0;
00222   TObjArray* aobj = 0;
00223   while ( (obj=iter->Next())!=0) {
00224     fobj = dynamic_cast<TFolder*>(obj);
00225     aobj = dynamic_cast<TObjArray*>(obj);
00226     if (fobj) {
00227       std::string pad2(pad);
00228       pad2 += fobj->GetName();
00229       std::cout << pad2 << "\n";
00230       edm::DataBucket::ListFolder(*fobj, lvl+1);
00231     }
00232     if (aobj) {
00233       std::cout << pad << 
00234         obj->GetName() << "[" << aobj->GetEntries() << "]\n";
00235     }
00236   }
00237   delete iter;
00238 }

TFolder * edm::DataBucket::MakeFolder const char *  path  ) 
 

Create ad configure a new folder at the absolute path name "path".

Parameters:
path : location of new folder
Returns:
Pointer to new folder. 0 on failure
Exceptions:
none 

Definition at line 34 of file DataBucket.cxx.

References GetFolder(), and Split().

Referenced by main(), MakeFolders(), testMakeOne(), and testWrite().

00035 { 
00036   if (!path) return 0;
00037   
00038   TFolder* f = 0;
00039   f = this->GetFolder(path);
00040   if (f!=0) return f;
00041   
00042   char pathto[strlen(path)+1];
00043   char newdir[strlen(path)+1];
00044   edm::DataBucket::Split(path, pathto, newdir);
00045   
00046   // Find the mother folder and add the daughter
00047   f = this->GetFolder(pathto);
00048   if (f==0) return 0;
00049   f->SetOwner(kTRUE);
00050   TFolder* newFolder = f->AddFolder(newdir, newdir, 0);
00051   newFolder->SetOwner(kTRUE);
00052   
00053   return newFolder;
00054 }

int edm::DataBucket::MakeFolders const char *  dirs[]  ) 
 

Make a collection of folders all at once given a list of names.

Parameters:
dirs : list of directories to be made. End with '0'
Returns:
Number of folders successfully created.
Exceptions:
none Example:
0000 const char dirs={"./A","./A/B1","./A/B2",0}; 0001 dataBucket.MakeFolders(dirs);

Definition at line 70 of file DataBucket.cxx.

References MakeFolder().

Referenced by main().

00071 {
00072   int n = 0;
00073   for (int i=0; dirs[i]!=0; ++i) {
00074     TFolder* f = this->MakeFolder(dirs[i]); 
00075     if (f!=0) ++n;
00076   }
00077   return n;
00078 }

TClonesArray * edm::DataBucket::NewClonesArray const char *  path,
const char *  classname
[private]
 

Create a new TClonesArray at a specific location

Parameters:
path : Folder where to create the array
classname : Name of objects to be stored in the array.
Returns:
Pointer to TClonesArray created. 0 otherwise.

Definition at line 169 of file DataBucket.cxx.

References GetFolder().

Referenced by Put(), PutArray(), and PutVector().

00171 {
00172   TFolder* f = this->GetFolder(path);
00173   if (f==0) return 0;
00174 
00175   TClonesArray* c = new TClonesArray(classname);
00176   f->Add(c);
00177 
00178   return c;
00179 }

TObjArray * edm::DataBucket::NewObjArray const char *  path,
const char *  classname
[private]
 

Create a new TObjArray at a specific location

Parameters:
path : Folder where to create the array
classname : Name of objects to be stored in the array.
Returns:
Pointer to TObjArray created. 0 otherwise.

Definition at line 189 of file DataBucket.cxx.

References GetFolder().

Referenced by Put(), PutArray(), and PutVector().

00191 {
00192   TFolder* f = this->GetFolder(path);
00193   if (f==0) return 0;
00194   std::string name;
00195   name  = classname;
00196   name += "s";
00197   TObjArray* c = new TObjArray(100,0);
00198   c->SetOwner();
00199   c->SetName(name.c_str());
00200   f->Add(c);
00201   return c;
00202 }

template<class T>
T * edm::DataBucket::Put const T &  obj,
const char *  path,
int  opt = kFixedSizeObject
 

Place a copy of an object into the data store

Parameters:
obj : Object to be stored
path : Location to place object
opt : Storage options. Obsolete in ROOT versions after (at least) 5.00
Returns:
Pointer to object created in the data store. 0 on failure.
Exceptions:
edm::Exception if requested location is invalid
"Put" places a copy of the object obj into the data bucket, appending it to the collection of object located at path. If no collection exists, a new one is made. So for example,
0000 DataObject obj; 0001 dataBucket.Put(obj,"./directory");
Will create a copy of the object "obj" and append it to the end of the collection "./directory/DataObjects".

The optional argument "opt" is used to specify whether the object is of fixed or variable size. This distinction was required by the implementation of TClonesArray in ROOT versions prior to 5.00. In modern ROOT versions this argument is obsolete.

Definition at line 178 of file DataBucket.h.

References GetClonesArray(), GetObjArray(), kFixedSizeObject, kVariableSizeObject, NewClonesArray(), NewObjArray(), and edm::NO_FOLDER_FOUND.

Referenced by main(), testMakeOne(), and testWrite().

00179 {
00180   static std::string collectName;
00181   static std::string fullPath;
00182   int indx;
00183 
00184   collectName = obj.ClassName();
00185   fullPath = path;
00186   fullPath += "/";
00187   fullPath += collectName;
00188   fullPath += "s";
00189   
00190   if (opt & kFixedSizeObject) {
00191     TClonesArray *c = this->GetClonesArray(fullPath.c_str());
00192     if (c == 0) {
00193       // No collection of this type of object. Make a new collection
00194       if ((c = this->NewClonesArray(path, collectName.c_str())) == 0) {
00195         std::cerr << "edm::DataBucket: no folder " << path << " found"
00196                   << std::endl;
00197         throw Exception(__FILE__,__LINE__,NO_FOLDER_FOUND);
00198       }
00199     }
00200     
00201     // Create new object of type T in event store. The "new with
00202     // placement" is a signature of the TClonesArray container
00203     assert(c!=0); // By this time we must have a home for the object
00204     indx = c->GetLast()+1;
00205     T* t = new ((*c)[indx]) T(obj);
00206     return t; // Add OK
00207   }
00208 
00209   if (opt & kVariableSizeObject) {
00210     TObjArray* c = this->GetObjArray(fullPath.c_str());
00211     if (c == 0) {
00212       // No collection of this type of object. Make a new collection
00213       if ((c = this->NewObjArray(path, collectName.c_str())) == 0) {
00214         std::cerr << "edm::DataBucket: no folder " << path << " found"
00215                   << std::endl;
00216         throw Exception(__FILE__,__LINE__,NO_FOLDER_FOUND);
00217       }
00218     }
00219     
00220     // Create new object of type T in event store.
00221     assert(c!=0); // By this time we must have a home for the object
00222     T* t = new T(obj);
00223     c->AddLast(t);
00224     return t; // Add OK
00225   }
00226 
00227   return 0;
00228 }

template<class T>
int edm::DataBucket::PutArray const T *  obj,
const char *  path,
int  nObj,
int  opt = kFixedSizeObject
 

Place an array of objects into the data store.

Parameters:
obj : Array of pointers to objects to be stored
path : Location in store where objects are to be placed
nObj : size of obj array
opt : Now obsolete
Returns:
Number of objects sucessfully stored
Exceptions:
edm::Exception 
See also:
Put()
This is simply a convenient and more efficient version of Put() above.

Definition at line 244 of file DataBucket.h.

References GetClonesArray(), GetObjArray(), kFixedSizeObject, kVariableSizeObject, NewClonesArray(), NewObjArray(), and edm::NO_FOLDER_FOUND.

00246 {
00247   static std::string collectName;
00248   static std::string fullPath;
00249   int indx;
00250   register int i;
00251 
00252   collectName = obj[0].ClassName();
00253   fullPath = path;
00254   fullPath += "/";
00255   fullPath += collectName;
00256   fullPath += "s";
00257   
00258   if (opt & kFixedSizeObject) {
00259     TClonesArray *c = this->GetClonesArray(fullPath.c_str());
00260     if (c == 0) {
00261       // No collection of this type of object. Make a new collection
00262       if ((c = this->NewClonesArray(path, collectName.c_str())) == 0) {
00263         std::cerr << "edm::DataBucket: no folder " << path << " found"
00264                   << std::endl;
00265         throw Exception(__FILE__,__LINE__,NO_FOLDER_FOUND);
00266       }
00267     }
00268     
00269     // Create new object of type T in event store. The "new with
00270     // placement" is a signature of the TClonesArray container
00271     assert(c != 0); // By this time we must have a home for the object
00272     indx = c->GetLast() + 1;
00273     const T* objPtr = obj;
00274     for (i = 0; i < nObj; ++i) {
00275       new ((*c)[indx++]) T(*objPtr++);
00276     }
00277     return indx; // Add OK
00278   }
00279 
00280   if (opt & kVariableSizeObject) {
00281     TObjArray* c = this->GetObjArray(fullPath.c_str());
00282     if (c == 0) {
00283       // No collection of this type of object. Make a new collection
00284       if ((c = this->NewObjArray(path, collectName.c_str())) == 0) {
00285         std::cerr << "edm::DataBucket: no folder " << path << " found"
00286                   << std::endl;
00287         throw Exception(__FILE__,__LINE__,NO_FOLDER_FOUND);
00288       }
00289     }
00290     
00291     // Create new object of type T in event store.
00292     assert(c!=0); // By this time we must have a home for the object
00293     for (i = 0; i < nObj; ++i) {
00294       c->AddLast(new T(obj[i]));
00295     }
00296     return c->GetLast(); // Add OK
00297   }
00298 
00299   return 0;
00300 }

template<class T>
int edm::DataBucket::PutVector const std::vector< T * > &  vec,
const char *  path,
int  opt = kFixedSizeObject
 

Place a vector of objects into the data store.

Parameters:
obj : vector of pointers to objects to be stored
path : Location in store where objects are to be placed
opt : Now obsolete
Returns:
Number of objects sucessfully stored
Exceptions:
edm::Exception 
See also:
Put()
This is simply a convenient and more efficient version of Put() above.

Definition at line 315 of file DataBucket.h.

References GetClonesArray(), GetObjArray(), kFixedSizeObject, kVariableSizeObject, NewClonesArray(), NewObjArray(), and edm::NO_FOLDER_FOUND.

00317 {
00318   static std::string collectName;
00319   static std::string fullPath;
00320   int indx;
00321 
00322   if (vec.empty()) return 0;
00323 
00324   unsigned int vecSize = vec.size();
00325 
00326   // This doesn't seem to work for some reason...
00327   // std::vector<const T*>::const_iterator itr(vec.begin());
00328   // std::vector<const T*>::const_iterator itrEnd(vec.end());
00329 
00330   collectName = vec[0]->ClassName();
00331   fullPath = path;
00332   fullPath += "/";
00333   fullPath += collectName;
00334   fullPath += "s";
00335   
00336   if (opt & kFixedSizeObject) {
00337     TClonesArray *c = this->GetClonesArray(fullPath.c_str());
00338     if (c == 0) {
00339       // No collection of this type of object. Make a new collection
00340       if ((c = this->NewClonesArray(path, collectName.c_str())) == 0) {
00341         std::cerr << "edm::DataBucket: no folder " << path << " found"
00342                   << std::endl;
00343         throw Exception(__FILE__,__LINE__,NO_FOLDER_FOUND);
00344       }
00345     }
00346     
00347     // Create new object of type T in event store. The "new with
00348     // placement" is a signature of the TClonesArray container
00349     assert(c != 0); // By this time we must have a home for the object
00350     indx = c->GetLast() + 1;
00351     for (unsigned int i = 0; i < vecSize; ++i) {// ; itr != itrEnd; ++itr) {
00352       new ((*c)[indx++]) T(*vec[i]);
00353     }
00354     return indx; // Add OK
00355   }
00356 
00357   if (opt & kVariableSizeObject) {
00358     TObjArray* c = this->GetObjArray(fullPath.c_str());
00359     if (c == 0) {
00360       // No collection of this type of object. Make a new collection
00361       if ((c = this->NewObjArray(path, collectName.c_str())) == 0) {
00362         std::cerr << "edm::DataBucket: no folder " << path << " found"
00363                   << std::endl;
00364         throw Exception(__FILE__,__LINE__,NO_FOLDER_FOUND);
00365       }
00366     }
00367     
00368     // Create new object of type T in event store.
00369     assert(c!=0); // By this time we must have a home for the object
00370     for (unsigned int i = 0; i < vecSize; ++i) {//; itr != itrEnd; ++itr) 
00371       c->AddLast(new T(*vec[i]));
00372     }
00373     return c->GetLast(); // Add OK
00374   }
00375 
00376   return 0;
00377 }

int edm::DataBucket::Remove const char *  path  ) 
 

Remove the collection and/or folder indicated by path

Todo:
Not implemented.

Definition at line 86 of file DataBucket.cxx.

00087 {
00088   if   (path==0) return 0; 
00089   else  return 0;
00090 }

void edm::DataBucket::Split const char *  fullpath,
char *  pathto,
char *  obj
[static, private]
 

Split a complete path name into two pieces, the target and the path to the target: /full/path/to/target -> /full/path/to, target

Parameters:
fullpath : Input complete path
pathto : Path piece of the full path
obj : Target piece of the full path

Definition at line 291 of file DataBucket.cxx.

Referenced by GetClonesArray(), GetFolder(), GetObjArray(), and MakeFolder().

00292 {
00293   char cpy1[strlen(fullpath)+1];
00294   strcpy(cpy1,fullpath);
00295   strcpy(pathto, dirname(cpy1));
00296 
00297   char cpy2[strlen(fullpath)+1];
00298   strcpy(cpy2,fullpath);
00299   strcpy(obj, basename(cpy2));
00300 }


Member Data Documentation

TFolder* edm::DataBucket::fTopFolder [private]
 

Folder managed. Not owned.

Definition at line 59 of file DataBucket.h.

Referenced by ClearArrays(), GetFolder(), and List().

const int edm::DataBucket::kFixedSizeObject = 0x01 [static]
 

Obsolete for ROOT>5.00.

Definition at line 24 of file DataBucket.h.

Referenced by Put(), PutArray(), and PutVector().

const int edm::DataBucket::kVariableSizeObject = 0x02 [static]
 

Obsolete for ROOT>5.00.

Definition at line 25 of file DataBucket.h.

Referenced by Put(), PutArray(), and PutVector().


The documentation for this class was generated from the following files:
Generated on Thu Jul 24 12:01:16 2008 for NOvA Offline by doxygen 1.3.5