//# Record.h: A hierarchical collection of named fields of various types //# Copyright (C) 1995,1996,1997,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef CASA_RECORD_H #define CASA_RECORD_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class AipsIO; // // A hierarchical collection of named fields of various types // // // // // //
  • RecordDesc. //
  • RecordInterface. //
  • RecordFieldPtr. // // // ``Record'' is a widely used term in both programming languages and data // structures to denote an imhogeneous set of fields. An alternative would // have been to name it structure, which would have perhaps been // a clearer name for C++ programmers. // // // Class RecordInterface decribes // the fundamental properties of records. //
    // The Record class is a particular type of a record class. // The fields in Record may be of scalar type, array type, or a Record. // The types are chosen to be compatible with the native // types of the Table system, viz: Bool, uChar, Short, Int, uInt, float, // double, Complex, DComplex, String. // Arrays of all these types are also available. // Note that a Record is not a space-efficient way of storing small objects. //

    // The structure of a Record is defined by the // RecordDesc class. The structure of the Record can be defined at // construction time. It can thereafter be restructured. This has the // effect, however, that any existing RecordFieldPtr objects become // invalid (using the Notice classes). //
    // It is possible to add or remove fields once a Record is constructed. // However, this is not possible when the Record is constructed with a // fixed structure (i.e. with the fixedStructure flag set). //

    // A Record is an hierarchical structure, because it can have fields // containing Record's (as layed out in the RecordDesc). A subrecord // has a variable structure, when its RecordDesc is empty (i.e. contains // no fields). It is fixed when its RecordDesc contains fields. //

    // A Record may be assigned to another only if they conform; that is if their // fields have the identical type in the identical order. // The field names do not need to be identical however, only the types. // That is, the structure needs to be identical, but // not the labels. Note that field order is significant, // [ifield(type=Int),ffield(type=float)] // is not the same as [ffield(type=float),ifield(type=Int)] //
    // Conformance is checked recursively for fixed subrecords. That is, a // variable structured subrecord is not checked, because any record // can be assigned to it. A fixed structured subrecord has to // conform the corresponding subrecord in the source. //

    // Record uses copy-on-write semantics. This means that when a Record // is copied, only the pointer to the underlying RecordRep object is copied. // Only when the Record gets changed (i.e. when a non-const Record member // function is called), the RecordRep object is copied. // This results in a cheap copy behaviour. // // // Suppose we wanted to create a records that describe the favorite example // of the OO world - an employee: // // RecordDesc employeeDesc; // employeeDesc.addField ("name", TpString); // employeeDesc.addField ("salary", TpDouble); // // The above creates the description (structure) for some record objects. // // Record employeeA(employeeDesc); // Record employeeB(employeeDesc, False); // // And these two lines create Record objects which share this common structure. // The first Record has a fixed structure, the 2nd variable. // // RecordFieldPtr nameA(employeeA, 0); // RecordFieldPtr nameB(employeeB, 0); // RecordFieldPtr salaryA(employeeA, 1); // RecordFieldPtr salaryB(employeeB, "salary"); // // This shows how we can get access to the individual fields. The fields are // fundamentally identified by number, but the number can be looked up through // the use of the fieldNumber member function. // // nameA.define ("Tim"); // nameB.define ("Brian"); // salaryA.define (1.0e+8); // salaryB.define (1.0 / *salaryA); // // Once obtained, the fields are readily manipulated, as shown above. Note // that the field values are obtained through the dereference (*) // operator. This is to identify that the field objects are pointers // to the values in the underlying Record; that is // // salaryA = salaryB; // *salaryA = *salaryB; // // Do very different things; the first line is a pointer copy; salaryA and // salaryB now point to the same field in salaryB. The second line is a value // copy. // // Whole records can be copied as long as their structures are compatible, so // that employeeA = employeeB is a legal statement. However, if // the structure is changed, assignment is no longer possible, and all of the // field pointers are invalidated: // // employeeB.define ("age", (Int)40); // employeeA = employeeB; // exception - no longer conformant // // // // Collections of data with different types are frequently needed. // Record makes it possible to hold such data in a flexible way. // // //

  • A record reference class, which contains some fields from another // record, would likely be useful. This would be analagous to a // subarray sliced from an existing array. // class Record : public RecordInterface { friend class RecordRep; public: // Create a record with no fields. // The record has a variable structure. Record(); // Create a record with no fields. // The type determines if the record has a fixed or variable structure. // The callback function is called when a field is added to the Record. // That function can check the name and of data type of the new field // (for instance, the Table system uses it to ensure that table columns // and keywords have different names). explicit Record (RecordType type, CheckFieldFunction* = 0, const void* checkArgument = 0); // Create a record with the given description. If it is not possible to // create all fields (for example, if a field with an unsupported data // type is requested), an exception is thrown. // The type determines if the record has a fixed or variable structure. // All fields are checked by the field checking function (if defined) // (for instance, the Table system uses it to ensure that table columns // and keywords have different names). explicit Record (const RecordDesc& description, RecordType type = Fixed, CheckFieldFunction* = 0, const void* checkArgument = 0); // Create a copy of other using copy semantics. Record (const Record& other); // Create a Record from another type of record using copy semantics. // Subrecords are also converted to a Record. Record (const RecordInterface& other); // Copy the data in the other record to this record. // It can operate in 2 ways depending on the Record structure flag. //
      //
    • For variable structured records the existing fields are // thrown away and replaced by the new fields. // This means that RecordFieldPtr's using this record get invalidated. // Because copy-on-write semantics are used, this kind of // assignment is a very efficient operation. //
    • For fixed structured records the existing values are replaced // by the new values. This means that RecordFieldPtr's using this // record remain valid. // The structure of the other record has to conform this record // or this record has to be empty, otherwise an exception is thrown. // This assignment is less efficient, because it has to check the // conformance and because each value has to be copied. //
    // // Attributes like fixed structure flag and check function will not // be copied. // Record& operator= (const Record& other); // Release resources associated with this object. virtual ~Record(); // Make a copy of this object. virtual RecordInterface* clone() const; // Assign that RecordInterface object to this one. // Unlike operator= it copies all data in the derived // class. virtual void assign (const RecordInterface& that); // Get the comment for this field. virtual const String& comment (const RecordFieldId&) const; // Set the comment for this field. virtual void setComment (const RecordFieldId&, const String& comment); // Describes the current structure of this Record. const RecordDesc& description() const; // Change the structure of this Record to contain the fields in // newDescription. After calling restructure, description() == // newDescription. Any existing RecordFieldPtr objects are // invalidated (their isAttached() members return False) after // this call. //
    When the new description contains subrecords, those subrecords // will be restructured if recursive=True is given. // Otherwise the subrecord is a variable empty record. // Subrecords will be variable if their description is empty (i.e. does // not contain any field), otherwise they are fixed. The 2nd form of // the restructure function will overwrite those implicit // record types with the given record type. The new type will also // be given to this top record. //
    Restructuring is not possible and an exception is thrown // if the Record has a fixed structure. virtual void restructure (const RecordDesc& newDescription, Bool recursive = True); // Returns True if this and other have the same RecordDesc, other // than different names for the fields. That is, the number, type and the // order of the fields must be identical (recursively for fixed // structured sub-Records in this). // // thisRecord.conform(thatRecord) == True does not imply //
    thatRecord.conform(thisRecord) == True, because // a variable record in one conforms a fixed record in that, but // not vice-versa. //
    Bool conform (const Record& other) const; // How many fields does this structure have? A convenient synonym for // description().nfields(). virtual uInt nfields() const; // Get the field number from the field name. // -1 is returned if the field name is unknown. virtual Int fieldNumber (const String& fieldName) const; // Get the data type of this field. virtual DataType type (Int whichField) const; // Remove a field from the record. // // Removing a field means that the field number of the fields following // it will be decremented. Only the RecordFieldPtr's // pointing to the removed field will be invalidated. // void removeField (const RecordFieldId&); // Rename the given field. void renameField (const String& newName, const RecordFieldId&); // Define a value for the given field containing a subrecord. // When the field is unknown, it will be added to the record. // The second version is meant for any type of record (e.g. Record, // TableRecord, GlishRecord). It is converted to a Record using the // Record constructor taking a RecordInterface object. // void defineRecord (const RecordFieldId&, const Record& value, RecordType type = Variable); virtual void defineRecord (const RecordFieldId&, const RecordInterface& value, RecordType = Variable); // // Get the subrecord from the given field. // // The non-const version has a different name to prevent that the // copy-on-write mechanism makes a copy when not necessary. // // const Record& subRecord (const RecordFieldId&) const; Record& rwSubRecord (const RecordFieldId&); virtual const RecordInterface& asRecord (const RecordFieldId&) const; virtual RecordInterface& asrwRecord (const RecordFieldId&); // // Get or define the value as a ValueHolder. // This is useful to pass around a value of any supported type. // virtual ValueHolder asValueHolder (const RecordFieldId&) const; virtual void defineFromValueHolder (const RecordFieldId&, const ValueHolder&); // // Merge a field from another record into this record. // The DuplicatesFlag (as described in // RecordInterface) determines // what will be done in case the field name already exists. void mergeField (const Record& other, const RecordFieldId&, DuplicatesFlag = ThrowOnDuplicates); // Merge all fields from the other record into this record. // The DuplicatesFlag (as described in // RecordInterface) determines // what will be done in case a field name already exists. // An exception will be thrown if other is the same as this // (i.e. if merging the record itself). void merge (const Record& other, DuplicatesFlag = ThrowOnDuplicates); // Write the Record to an output stream. friend AipsIO& operator<< (AipsIO& os, const Record& rec); // Read the Record from an input stream. friend AipsIO& operator>> (AipsIO& os, Record& rec); // Write the Record to an output stream. // This is used to write a subrecord, whose description has // not been written. void putRecord (AipsIO& os) const; // Read the Record from an input stream. // This is used to read a subrecord, whose description has // not been read. void getRecord (AipsIO& os); // Put the data of a record. // This is used to write a subrecord, whose description has // already been written. void putData (AipsIO& os) const; // Read the data of a record. // This is used to read a subrecord, whose description has // already been read. void getData (AipsIO& os, uInt version); // Make a unique record representation // (to do copy-on-write in RecordFieldPtr). virtual void makeUnique(); // Print the contents of the record. // Only the first maxNrValues of an array will be printed. // A value < 0 means the entire array. virtual void print (std::ostream&, Int maxNrValues = 25, const String& indent="") const; protected: // Used by the RecordField classes to attach in a type-safe way to the // correct field. // virtual void* get_pointer (Int whichField, DataType type) const; virtual void* get_pointer (Int whichField, DataType type, const String& recordType) const; // // Return a const reference to the underlying RecordRep. const RecordRep& ref() const; // Return a non-const reference to the underlying RecordRep. // When needed, the RecordRep will be copied and all RecordField // objects will be notified. RecordRep& rwRef(); // Add a field to the record. virtual void addDataField (const String& name, DataType type, const IPosition& shape, Bool fixedShape, const void* value); // Define a value in the given field. virtual void defineDataField (Int whichField, DataType type, const void* value); private: // Get the description of this record. virtual RecordDesc getDescription() const; // Create Record as a subrecord. // When the description is empty, the record has a variable structure. // Otherwise it is fixed. // Record (RecordRep* parent, const RecordDesc& description); Record (RecordRep* parent, RecordType type); // // The Record representation. COWPtr rep_p; // The parent Record. RecordRep* parent_p; }; inline const RecordRep& Record::ref() const { return rep_p.ref(); } inline const RecordDesc& Record::description() const { return ref().description(); } inline Bool Record::conform (const Record& other) const { return ref().conform (other.ref()); } inline AipsIO& operator<< (AipsIO& os, const Record& rec) { rec.putRecord (os); return os; } inline void Record::putData (AipsIO& os) const { ref().putData (os); } inline AipsIO& operator>> (AipsIO& os, Record& rec) { rec.getRecord (os); return os; } inline void Record::getData (AipsIO& os, uInt version) { rwRef().getData (os, version); } } //# NAMESPACE CASACORE - END #endif