// $Id: mmdb_mmcif_.h $ // ================================================================= // // CCP4 Coordinate Library: support of coordinate-related // functionality in protein crystallography applications. // // Copyright (C) Eugene Krissinel 2000-2013. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License version 3, modified in accordance with the provisions // of the license to address the requirements of UK law. // // You should have received a copy of the modified GNU Lesser // General Public License along with this library. If not, copies // may be downloaded from http://www.ccp4.ac.uk/ccp4license.php // // This program 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 Lesser General Public License for more details. // // ================================================================= // // 12.09.13 <-- Date of Last Modification. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ----------------------------------------------------------------- // // **** Module : MMDB_MMCIF // ~~~~~~~~~ // **** Project : MacroMolecular Data Base (MMDB) // ~~~~~~~~~ // **** Classes : mmdb::mmcif::Category ( mmCIF category ) // ~~~~~~~~~ mmdb::mmcif::Struct ( mmCIF structure ) // mmdb::mmcif::Loop ( mmCIF loop ) // mmdb::mmcif::Data ( mmCIF data block ) // mmdb::mmcif::File ( mmCIF file ) // // (C) E. Krissinel 2000-2013 // // ================================================================= // #ifndef __MMDB_MMCIF__ #define __MMDB_MMCIF__ #include "mmdb_io_stream.h" namespace mmdb { namespace mmcif { // ====================== Category ========================== enum MMCIF_ITEM { MMCIF_Category = 0, MMCIF_Struct = 1, MMCIF_Loop = 2, MMCIF_Data = 3 }; DefineClass(Category); DefineStreamFunctions(Category); /// \brief mmcif::Category is a base class for mmcif::Struct and /// mmcif::Loop, implementations of mmCIF's "structure" and /// "loop" categories. /*! This class is not instantiated independently in any applications, however, it provides a few public functions which work for both mmcif::Struct and mmcif::Loop. All data in mmCIF hierarchy is addressed using construct "category.tag" plus row number (>=0) for loops. Category names should always start from underscore, while tags normally start with a letter, e.g. "_barrel.id". See general principles of working with mmCIF files and mmCIF hierarchies in Section \"\ref mmcif_handler\". */ class Category : public io::Stream { friend class Data; public : /// \brief Basic constructor. Category (); /// \brief Constructor that assigns category name. /// \param[in] N category name (must start with underscore). Category ( cpstr N ); /// \brief Constructor for MMDB data streaming functions. Category ( io::RPStream Object ); /// \brief Destructor. ~Category(); /// \brief Returns category name. /// \return NULL if name was not set /// \return pointer to character string if name was set inline pstr GetCategoryName() { return name; } /// \brief Sets category name. /// \param N new category name void SetCategoryName ( cpstr N ); /// \brief Returns category type. /// This function may be used when retrieving categories /// (structures and loops) from data blocks (mmcif::Data). /// \return MMCIF_Category for mmcif::Category /// \return MMCIF_Struct for mmcif::Struct /// \return MMCIF_Loop for mmcif::Loop virtual MMCIF_ITEM GetCategoryID() { return MMCIF_Category; } /// \brief Virtual function for writing category's content /// into mmCIF file. /// Default implementation does nothing. virtual void WriteMMCIF ( io::RFile ) {} /// \brief Virtual function for optimizig data structures. /// Optimized data structures take less RAM and their indexes /// are sorted for quicker access. Sorting is done automatically /// as new data is added to the category. If the /// category is edited (fields/data removed), it may need /// optimization and re-sorting for efficiency.\n\n /// The sorting preserves the order of actual appearance of /// tags in mmCIF file. If a category is created /// programmatically, the order of tags in mmCIF file will be /// the same as order of adding them to the category. virtual void Optimize(); /// \brief Sorts category's data for quicker access. /// The sorting is essentially re-indexing of data for quicker /// access. It does not change the order of data in both mmCIF /// hierarchy and mmCIF file. E.g., if tag "serial_no" was 2nd /// one in given category before sorting, it will remain on 2nd /// place after it, therefore no change in tag number passed /// to functions in mmcif::Struct, mmcif::Loop and mmcif::Data. void Sort(); /// \brief Returns serial number of a tag in the category. /// \param[in] ttag tag (or name of a field in category) /// \return \b >=0 : the tag is in given position /// \return \b <0 : the tag was not found, but it could be /// inserted before tag with (-rc-1)th index, where /// 'rc' is the return. int GetTagNo ( cpstr ttag ); /// \brief Adds a tag to the category. /// Adding a tag in mmcif::Category does not reserve any /// placeholder for the corresponding value. All tags get /// automatically sorted (reindexed) for quicker access. /// Tags will appear in mmCIF file in order of their addition /// to the category. /// \param[in] ttag tag to be added. /// \return \b >=0 the tag is already in the category, and return /// is its serial number. No changes to the category /// is done /// \return \b <0 the tag was added to the list of tags, and /// return is minus total number of tags in the /// category. int AddTag ( cpstr ttag ); /// \brief Returns the total number of tags in the category int GetNofTags() { return nTags; } /// \brief Returns tag with the specified serial number. /// The tags are enumerated as 0..GetNofTags()-1. /// \param tagNo tag's serial number /// \return \b NULL: tagNo is outside the range /// of 0..GetNofTags()-1 /// \return \b not \b NULL: tag in tagNo'th position pstr GetTag ( int tagNo ); // 0..nTags-1 /// \brief Prints list of tags to stdout. /// Both sorted and unsorted tags are printed to standard /// output. This function may be used for debugging. void PrintTags(); /// \brief Returns true if all tags from the list are found /// in the category. /// The size of the list of tags may be less than the number /// of tags in the category, and order of tags is disregarded. /// \param[in] tagList list of tags to be checked for presence /// in the category. The list must end with NULL /// pointer, or your program will crash. /// \return \b true if all tags from the list were found in the /// category /// \return \b false if one or more tags from the list were not /// found in the category. /// /// Example: /// \code /// cpstr tagList[] = {"id","type","date",NULL}; /// mmcif::Struct cifStruct; /// if (cifStruct.CheckTags(tagList)) /// printf ( " all tags are found in category %s\n", /// cifStruct.GetCategoryName() ); /// \endcode /// This function is useful for finding categories in /// "dirty cifs", where category name is not given. bool CheckTags ( cpstr * tagList ); /// \brief Deep copy of categories. /// Deep copy duplicates all data and memory allocations, /// producing a genuine clone of the original. Only deep copy /// should be used for copying MMDB objects, a mere assignment /// operator will fail you. /// \param[in] Category a pointer to mmcif::Category, the content of /// which is copied into 'this' category. virtual void Copy ( PCategory Category ); /// \brief MMDB stream writer. void write ( io::RFile f ); /// \brief MMDB stream reader. void read ( io::RFile f ); protected: int nTags; pstr name; psvector tag; ivector index; int nAllocTags; void InitCategory (); virtual void FreeMemory (); void ExpandTags ( int nTagsNew ); void PutCategoryName ( cpstr newName ); }; // ====================== Struct ============================ DefineClass(Struct); DefineStreamFunctions(Struct); /// \brief Constants used to specify mmCIF's \"data not given\" and /// \"data not available\" data types. extern const int CIF_NODATA_DOT; extern const int CIF_NODATA_QUESTION; extern cpstr CIF_NODATA_DOT_FIELD; extern cpstr CIF_NODATA_QUESTION_FIELD; /// \brief mmcif::Struct represents mmCIF's \"structure\" category, /// where data follows directly the corresponding tag. /*! mmCIF's \"structure\" category has the following form: \code _structure_name.tag0 value0 _structure_name.tag1 value1 ........... _structure_name.tagN valueN \endcode mmcif::Struct represents this construct by keeping category name (\"_structure_name\") and associated lists of tags (\"tag0,tag1...tagN\") and their values (\"value0,value1...valueN\"). The structure is created automatically when an mmCIF file is read, or it may be created programatically and then pushed into file. Access to data is provided via tags. Internally, all values are kept as character fields, and it is only on the retrieval stage that they are converted to other data types (integers, floats or strings). If conversion is not possible, an error code is returned by the corresponding functions, which should be checked by the application. See general principles of working with mmCIF files and mmCIF hierarchies, as well as some code samples, in Section \"\ref mmcif_handler\". */ class Struct : public Category { public : /// \brief Basic constructor Struct (); /// \brief Constructor that assigns structure name. /// \param[in] N structure name (must start with underscore). Struct ( cpstr N ); /// \brief Constructor for MMDB data streaming functions Struct ( io::RPStream Object ); /// \brief Destructor ~Struct(); /// \brief Adds field to the structure. /// \param[in] F field value /// \param[in] T tag name /// \param[in] Concatenate flag to concatenate existing field /// with the value of \b F. If tag \b T is already in /// the structure and \b Concatenate=true, then /// value of \b F is appended to the existing field. /// Otherwise, the field is replaced with the value /// of \b F void AddField ( cpstr F, cpstr T, bool Concatenate=false ); /// \brief Returns category type \b MMCIF_Struct. MMCIF_ITEM GetCategoryID() { return MMCIF_Struct; } /// \brief Optimizes structure for RAM and data access speed. /// Optimized data structures take less RAM and their indexes /// are sorted for quicker access. Sorting is done automatically /// as new data is added to the category. If the structure /// is edited (fields/data removed), it may need /// optimization and re-sorting for efficiency.\n\n /// The sorting preserves the order of actual appearance of /// tags in mmCIF file. If a structure is created /// programmatically, the order of tags in mmCIF file will be /// the same as order of adding them to the structure. void Optimize(); /// \brief Returns value of field corresponding to tag in the /// specified position. /// Tag positions are defined by the order of their appearance in /// mmCIF file (if structure was read from a file), or by the /// order of their addition to the structure (if structure was /// created programmatically). Tags are numbered as /// 0...GetNofTags()-1. /// \param[in] tagNo tag number (position in the structure) /// \return \b NULL: tag does not exist /// \return \b CIF_NODATA_DOT_FIELD the field contains /// \"data not given\" value /// \return \b CIF_NODATA_QUESTION_FIELD the field contains /// \"data not available\" value /// \return \b not \b NULL: string value of the field pstr GetField ( int tagNo ); // 0..nTags-1 /// \brief Fetches value, corresponding to the given tag, as /// a string /// \param[out] S pointer to string, which will point to newly /// allocated character string, containing value /// associated with tag \b TName. If tag or value /// is not found, or if value corresponds to /// mmCIF's \"data not given\" or /// \"data not available\", \b S returns NULL. /// \param[in] TName character string with tag name /// \param[in] Remove flag to remove the tag and its value from /// structure after it is read. /// \return \b CIFRC_NoTag: tag is not found /// \return \b CIFRC_NoField: value is not found /// \return \b CIFRC_Ok: success. If \b S returns NULL, then /// the value corresponds to either /// \"data not available\" or /// \"data not given\". /// \remarks If \b S!=NULL at time of call, the function will /// try to dispose the string it points on. This allows a slick /// re-use of the same pointer in consequitive calls. This also /// means that one should never pass unallocated pointer to /// this function. Safe use assumes the following patern: /// \code /// mmcif::Struct mmCIFStruct; /// pstr S; // this is merely "char *S" /// int rc; /// /// S = NULL; // null pointer before first use /// rc = mmCIFStruct.GetString ( S,"id" ); /// if (rc) CreateCopy ( S,"*** data not found" ); /// if (!S) CreateCopy ( S,"*** data not given or not available" ); /// printf ( " rc=%i, S='%s'\n",rc,S ); /// /// rc = mmCIFStruct.GetString ( S,"property" ); /// if (rc) CreateCopy ( S,"*** data not found" ); /// if (!S) CreateCopy ( S,"*** data not given or not available" ); /// printf ( " rc=%i, S='%s'\n",rc,S ); /// /// // etc etc etc /// /// delete[] S; // application is responsible for final /// // disposal of memory /// \endcode int GetString ( pstr & S, cpstr TName, bool Remove=false ); /// \brief Returns pointer to value associated with given tag. /// \param[in] TName character string with tag name /// \param[out] RC return code: /// \arg \b CIFRC_NoTag: tag is not found /// \arg \b CIFRC_NoField: value is not found /// \arg \b CIFRC_Ok: success. If function returns NULL, then /// the value corresponds to either /// \"data not available\" or /// \"data not given\". /// \return \b NULL: either tag or value is not found, or the /// value is \"data not available\" or \"data not given\". /// Read return code \b RC in order to interpret NULL return. /// \return \b not \b NULL: pointer (\c char \c *) to value /// associated with \b TName. /// \remarks Never try to dispose memory pointed by the return /// value, or your program will crash. pstr GetString ( cpstr TName, int & RC ); // NULL if TName // is not there /// \brief Deletes field associated with given tag. /// \param[in] TName character string with tag name /// \return \b >=0: field deleted /// \return \b <0: either field or tag is not found int DeleteField ( cpstr TName ); // <0 the field was not there /// \brief Fetches value, corresponding to the given tag, as /// a real number. /// \param[out] R reference to real number to accept the value. /// In case of failure, \b R returns zero. /// \param[in] TName character string with tag name /// \param[in] Remove flag to remove the tag and its value from /// structure after it is read. /// \return \b CIFRC_NoTag: tag is not found /// \return \b CIFRC_NoField: field is not found /// \return \b CIFRC_WrongFormat: value is not a real or integer /// number. /// \return \b CIFRC_NoData: value is either /// \"data not available\" or /// \"data not given\". /// \return \b CIFRC_Ok: success. int GetReal ( realtype & R, cpstr TName, bool Remove=false ); /// \brief Fetches value, corresponding to the given tag, as /// an integer number. /// \param[out] I reference to integer number to accept the /// value. In case of failure, \b I returns zero, except /// when value is \"data not available\" or /// \"data not given\", when I returns \c MinInt4. /// \param[in] TName character string with tag name /// \param[in] Remove flag to remove the tag and its value from /// structure after it is read. /// \return \arg \b CIFRC_NoTag: tag is not found /// \return \b CIFRC_NoField: field is not found /// \return \b CIFRC_WrongFormat: value is not an integer number. /// \return \b CIFRC_NoData: value is either /// \"data not available\" or /// \"data not given\". /// \return \b CIFRC_Ok: success. int GetInteger ( int & I, cpstr TName, bool Remove=false ); /// \brief Sets string value for given tag. /// \param[in] S character string with value to be set. /// If \b S==NULL, the \"data not given\" value /// will be set. If \b S==\"\" (empty string), the /// \"data not available\" value is stored. /// \param[in] TName character string with tag name. If tag /// is not found, it will be added to the structure. /// \param[in] NonBlankOnly flag to treat white-space-only /// strings: /// \arg \b false: set as is /// \arg \b true: set \"data not available\" value instead. void PutString ( cpstr S, cpstr TName, bool NonBlankOnly=false ); /// \brief Sets current date in format YYYY-MM-DD as a value /// for given tag. /// \param[in] T character string with tag name. If tag /// is not found, it will be added to the structure. void PutDate ( cpstr T ); /// \brief Sets \"data not given\" or \"data not available\" /// values for given tag. /// \param[in] NoDataType can be either /// \arg \b CIF_NODATA_DOT for \"data not given\" /// \arg \b CIF_NODATA_QUESTION for \"data not available\" /// \param[in] T character string with tag name. If tag /// is not found, it will be added to the structure. void PutNoData ( int NoDataType, cpstr T ); /// \brief Sets float-point value for given tag. /// \param[in] R real number with value to be set. /// \param[in] TName character string with tag name. If tag /// is not found, it will be added to the structure. /// \param[in] prec float-point precision; g-format with given /// precision will be used void PutReal ( realtype R, cpstr TName, int prec=8 ); /// \brief Sets float-point value for given tag. /// \param[in] R real number with value to be set. /// \param[in] TName character string with tag name. If tag /// is not found, it will be added to the structure. /// \param[in] format format string to convert \b R. void PutReal ( realtype R, cpstr TName, cpstr format ); /// \brief Sets integer value for given tag. /// \param[in] I integer number with value to be set. /// \param[in] TName character string with tag name. If tag /// is not found, it will be added to the structure. void PutInteger ( int I, cpstr TName ); /// \brief Writes structure data in mmCIF format into file. /// \param[in] FName character string with file name. /// \param[in] gzipMode flag to controll compression of files: /// \arg \b GZM_NONE: do not compress /// \arg \b GZM_CHECK: check file name suffix and compress /// (or not) accordingly /// \arg \b GZM_ENFORCE_GZIP: force gzip compression despite /// suffix /// \arg \b GZM_ENFORCE_COMPRESS: force using compress despite /// suffix /// \return \b true: success /// \return \b false: can not open file for writing. /// \remarks This function does not create a valid mmCIF file /// as \"data_XXX\" record will be missing. It may be used for /// debugging though. bool WriteMMCIFStruct ( cpstr FName, io::GZ_MODE gzipMode=io::GZM_CHECK ); /// \brief Writes structure into given file. /// \param f reference to MMDB's file class. The file should be /// opened and closed by application. /// \remarks There is a very limited use of this function on /// application level. It is primarily used by mmcif::Data class. void WriteMMCIF ( io::RFile f ); /// \brief Deep copy of structures. /// Deep copy duplicates all data and memory allocations, /// producing a genuine clone of the original. Only deep copy /// should be used for copying MMDB objects, a mere assignment /// operator will fail you. /// \param[in] Struct a pointer to mmcif::Struct, the content of /// which is copied into 'this' structure. void Copy ( PCategory Struct ); /// \brief MMDB stream writer. void write ( io::RFile f ); /// \brief MMDB stream reader. void read ( io::RFile f ); protected: psvector field; void InitStruct(); void FreeMemory(); }; // ====================== Loop ============================== DefineClass(Loop); DefineStreamFunctions(Loop); /// \brief mmcif::Loop represents mmCIF's \"loop\" category, which keeps /// rows of data values associated with tags. /*! mmCIF's \"loop\" category has the following form: \code loop_ _loop_name.tag0 value0 _loop_name.tag1 value1 ........... _loop_name.tagN valueN value00 value10 ... valueN0 value01 value11 ... valueN1 ........... value0M value1M ... valueNM \endcode mmcif::Loop represents this construct by keeping category name (\"_loop_name\") and associated lists of tags (\"tag0,tag1...tagN\") and data vectors (\"[value00...value0M],[value10...value1M]...[valueN0...valueNM]\"). The loop object is created automatically when an mmCIF file is read, or it may be created programatically and then pushed into file. Access to data is provided via tags and data indexes. Internally, all values are kept as character fields, and it is only on the retrieval stage that they are converted to other data types (integers, floats or strings). If conversion is not possible, an error code is returned by the corresponding functions, which should be checked by the application. The following code gives an example of creating mmCIF loop category and populating it with data: \code mmcif::Loop loop; char S[100]; int i; // Specify loop name: loop.SetCategoryName ( "_sample_loop" ); // Create loop structure, i.e., list of tags first: loop.AddLoopTag ( "id" ); loop.AddLoopTag ( "name" ); loop.AddLoopTag ( "value" ); // Now populate it with data. This my be done in 2 ways. // Here is how you write loop data in stream fashion, // value-after-value: for (i=0;i<3;i++) { loop.AddInteger ( i ); sprintf ( S,"1st_way-%i",i ); loop.AddString ( S ); loop.AddReal ( 2.5*(i+1) ); } // Here is how you populate loop data using direct-access // functions: for (i=3;i<6;i++) { loop.PutReal ( 2.5*(i+1),"value",i ); loop.PutInteger ( i,"id" ); sprintf ( S,"2nd way: %i",i ); loop.PutString ( S,"name" ); } loop.WriteMMCIFLoop ( "sample_loop.cif" ); \endcode The resulting file \b sample_loop.cif will contain: \code loop_ _sample_loop.id _sample_loop.name _sample_loop.value 0 1st_way-0 2.5 1 1st_way-1 5.0 2 1st_way-2 7.5 3 "2nd way: 3" 10.0 4 "2nd way: 4" 12.5 5 "2nd way: 5" 15.0 \endcode See general principles of working with mmCIF files and mmCIF hierarchies, as well as some code samples, in Section \"\ref mmcif_handler\". */ class Loop : public Category { friend class Data; public : /// \brief Basic constructor Loop (); /// \brief Constructor that assigns structure name. /// \param[in] N structure name (must start with underscore). Loop ( cpstr N ); /// \brief Constructor for MMDB data streaming functions Loop ( io::RPStream Object ); /// \brief Destructor ~Loop(); /// \brief Adds tag to the loop. /// The tag is appended to the list of existing tags. The order /// of tags cannot be changed. /// \param[in] T tag name /// \param[in] Remove flag to remove all fields in the loop. void AddLoopTag ( cpstr T, bool Remove=true ); /// \brief Sets string value at current loop position. /// When \b mmcif::Loop::Add[Data] functions use internal loop /// pointer. When category is created or cleared (by using /// \b mmcif::Loop::AddLoopTag ( T,true )) the pointer is set to /// 0th row and 0th column (tag). After each call to /// \b mmcif::Loop::Add[Data] function, internal pointer advances /// to next column (tag), and wraps over to next row, 0th tag, /// if list of tags is exhausted. Any remaining fields in last /// row will be populated with \"data not given\" value. /// \param[in] S character string with value to be set. /// If \b S==NULL, the \"data not given\" value /// will be set. If \b S==\"\" (empty string), the /// \"data not available\" value is stored. /// \param[in] NonBlankOnly flag to treat white-space-only /// strings: /// \arg \b false: set as is /// \arg \b true: set \"data not available\" value instead. void AddString ( cpstr S, bool NonBlankOnly=false ); /// \brief Sets \"data not given\" or \"data not available\" at /// current loop position. /// When \b mmcif::Loop::Add[Data] functions use internal loop /// pointer. When category is created or cleared (by using /// \b mmcif::Loop::AddLoopTag ( T,true )) the pointer is set to /// 0th row and 0th column (tag). After each call to /// \b mmcif::Loop::Add[Data] function, internal pointer advances /// to next column (tag), and wraps over to next row, 0th tag, /// if list of tags is exhausted. Any remaining fields in last /// row will be populated with \"data not given\" value. /// \param[in] NoDataType integer key specifying which type of /// data absence should be set as a value: /// \arg \b CIF_NODATA_DOT for \"data not given\" /// \arg \b CIF_NODATA_QUESTION for \"data not available\" void AddNoData ( int NoDataType ); /// \brief Sets float-point value at current loop position. /// When \b mmcif::Loop::Add[Data] functions use internal loop /// pointer. When category is created or cleared (by using /// \b mmcif::Loop::AddLoopTag ( T,true )) the pointer is set to /// 0th row and 0th column (tag). After each call to /// \b mmcif::Loop::Add[Data] function, internal pointer advances /// to next column (tag), and wraps over to next row, 0th tag, /// if list of tags is exhausted. Any remaining fields in last /// row will be populated with \"data not given\" value. /// \param[in] R real number with value to be set. /// \param[in] prec float-point precision; g-format with given /// precision will be used void AddReal ( realtype R, int prec=8 ); /// \brief Sets float-point value at current loop position in /// given format. /// When \b mmcif::Loop::Add[Data] functions use internal loop /// pointer. When category is created or cleared (by using /// \b mmcif::Loop::AddLoopTag ( T,true )) the pointer is set to /// 0th row and 0th column (tag). After each call to /// \b mmcif::Loop::Add[Data] function, internal pointer advances /// to next column (tag), and wraps over to next row, 0th tag, /// if list of tags is exhausted. Any remaining fields in last /// row will be populated with \"data not given\" value. /// \brief Sets float-point value for given tag. /// \param[in] R real number with value to be set. /// \param[in] format format string to convert \b R. void AddReal ( realtype R, cpstr format ); /// \brief Sets integer value at current loop position in given /// format. /// When \b mmcif::Loop::Add[Data] functions use internal loop /// pointer. When category is created or cleared (by using /// \b mmcif::Loop::AddLoopTag ( T,true )) the pointer is set to /// 0th row and 0th column (tag). After each call to /// \b mmcif::Loop::Add[Data] function, internal pointer advances /// to next column (tag), and wraps over to next row, 0th tag, /// if list of tags is exhausted. Any remaining fields in last /// row will be populated with \"data not given\" value. /// \param[in] I integer number with value to be set. void AddInteger ( int I ); /// \brief Returns current length of the loop (i.e. the number /// of rows). /// \return number of data rows in the loop. int GetLoopLength() { return nRows; } /// \brief Returns string pointer on the field corresponding to /// tag in the specified position, in the specified row. /// Tag positions are defined by the order of their appearance in /// mmCIF file (if loop was read from a file), or by the /// order of their addition to the loop (if loop was /// created programmatically). /// \param[in] rowNo row number (0...GetLoopLength()-1) /// \param[in] tagNo tag number (0...GetNofTags()-1) /// \return \b NULL: tag or row do not exist /// \return \b CIF_NODATA_DOT_FIELD the field contains /// \"data not given\" value /// \return \b CIF_NODATA_QUESTION_FIELD the field contains /// \"data not available\" value /// \return \b not \b NULL: string value of the field /// \remarks Never try to dispose memory pointed by the return /// value, or your program will crash. pstr GetField ( int rowNo, int tagNo ); /// \brief Fetches value, corresponding to the given tag, in /// the given row, as a string /// \param[out] S pointer to string, which will point to newly /// allocated character string, containing value /// associated with tag \b TName and row \b nrow. /// If tag, row or value /// is not found, or if value corresponds to /// mmCIF's \"data not given\" or /// \"data not available\", \b S returns NULL. /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \param[in] Remove flag to remove the field from /// structure after it is read. /// \return \b CIFRC_NoTag: tag is not found /// \return \b CIFRC_WrongIndex: row is not found /// \return \b CIFRC_NoField: value is not found /// \return \b CIFRC_Ok: success. If \b S returns NULL, then /// the value corresponds to either /// \"data not available\" or /// \"data not given\". /// \remarks If \b S!=NULL at time of call, the function will /// try to dispose the string it points on. This allows a slick /// re-use of the same pointer in consequitive calls. This also /// means that one should never pass unallocated pointer to /// this function. Safe use assumes the following patern: /// \code /// mmcif::Loop mmCIFLoop; /// pstr S; // this is merely "char *S" /// int rc; /// /// S = NULL; // null pointer before first use /// rc = mmCIFLoop.GetString ( S,"id",1 ); /// if (rc) CreateCopy ( S,"*** data not found" ); /// if (!S) CreateCopy ( S,"*** data not given or not available" ); /// printf ( " rc=%i, S='%s'\n",rc,S ); /// /// rc = mmCIFLoop.GetString ( S,"property",0 ); /// if (rc) CreateCopy ( S,"*** data not found" ); /// if (!S) CreateCopy ( S,"*** data not given or not available" ); /// printf ( " rc=%i, S='%s'\n",rc,S ); /// /// // etc etc etc /// /// delete[] S; // application is responsible for final /// // disposal of memory /// \endcode int GetString ( pstr & S, cpstr TName, int nrow, bool Remove=false ); /// \brief Returns pointer to value associated with given tag, /// in the given row of the loop. /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \param[out] RC return code: /// \arg \b CIFRC_NoTag: tag is not found /// \arg \b CIFRC_WrongIndex: row is not found /// \arg \b CIFRC_NoField: value is not found /// \arg \b CIFRC_Ok: success. If function returns NULL, then /// the value corresponds to either /// \"data not available\" or /// \"data not given\". /// \return \b NULL: either tag, row or value is not found, or the /// value is \"data not available\" or \"data not given\". /// Read return code \b RC in order to interpret NULL return. /// \return \b not \b NULL: pointer (\c char \c *) to value /// associated with \b TName. /// \remarks Never try to dispose memory pointed by the return /// value, or your program will crash. pstr GetString ( cpstr TName, int nrow, int & RC ); /// \brief Copies value, associated with given tag, /// in the given row, into specified buffer. /// Terminating NULL character is appended. /// \param[out] buf character string to accept the value /// \param[in] maxlength maximum number of bytes to copy /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \param[out] RC return code: /// \arg \b CIFRC_NoTag: tag is not found /// \arg \b CIFRC_WrongIndex: row is not found /// \arg \b CIFRC_NoField: value is not found /// \arg \b CIFRC_Ok: success. /// \remarks Destination string \b buf is not modified if /// \b RC!=CIFRC_Ok . void CopyString ( pstr buf, int maxlength, cpstr TName, int nrow, int & RC ); /// \brief Deletes field associated with given tag in /// the given row. /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \return \b >=0: field deleted /// \return \b <0: either field or tag is not found int DeleteField ( cpstr TName, int nrow ); /// \brief Deletes all fields in given row. /// \param[in] nrow row number (0...GetLoopLength()-1) /// \return \b CIFRC_Ok: fields deleted /// \return \b CIFRC_WrongIndex: row not found /// \remarks Note that this function delets just the fields, but /// not the row. If you wish the row to be deleted, call /// mmcif::Loop::Optimize() function after this one. int DeleteRow ( int nrow ); /// \brief Fetches value, corresponding to the given tag, /// in the given row, as a real number. /// \param[out] R reference to real number to accept the value. /// In case of failure, \b R returns zero. /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \param[in] Remove flag to remove the field from /// the loop after it is read. /// \return \b CIFRC_NoTag: tag is not found /// \return \b CIFRC_WrongIndex: row not found /// \return \b CIFRC_NoField: field is not found /// \return \b CIFRC_WrongFormat: value is not a real or integer /// number. /// \return \b CIFRC_NoData: value is either /// \"data not available\" or /// \"data not given\". /// \return \b CIFRC_Ok: success. int GetReal ( realtype & R, cpstr TName, int nrow, bool Remove=false ); /// \brief Copies value, associated with given tag, /// in the given row, into specified destination as /// a real number. /// \param[out] R reference to real number to accept the value /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \param[out] RC return code: /// \arg \b CIFRC_NoTag: tag is not found /// \arg \b CIFRC_WrongIndex: row is not found /// \arg \b CIFRC_NoField: value is not found /// \arg \b CIFRC_Ok: success. /// \remarks Destination \b R is set 0 if \b RC!=CIFRC_Ok. void CopyReal ( realtype & R, cpstr TName, int nrow, int & RC ); /// \brief Copies value, associated with given tag, /// in the given row, into specified destination as /// an integer number. /// \param[out] I reference to integer number to accept the value /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \param[out] RC return code: /// \arg \b CIFRC_NoTag: tag is not found /// \arg \b CIFRC_WrongIndex: row is not found /// \arg \b CIFRC_NoField: value is not found /// \arg \b CIFRC_Ok: success. /// \remarks Destination \b I is set 0 if \b RC!=CIFRC_Ok. void CopyInteger ( int & I, cpstr TName, int nrow, int & RC ); /// \brief Fetches value, corresponding to the given tag, /// in the given row, as an integer number. /// \param[out] I reference to integer number to accept the value. /// In case of failure, \b R returns zero. /// \param[in] TName character string with tag name /// \param[in] nrow row number (0...GetLoopLength()-1) /// \param[in] Remove flag to remove the field from /// the loop after it is read. /// \return \b CIFRC_NoTag: tag is not found /// \return \b CIFRC_WrongIndex: row not found /// \return \b CIFRC_NoField: field is not found /// \return \b CIFRC_WrongFormat: value is not a real or integer /// number. /// \return \b CIFRC_NoData: value is either /// \"data not available\" or /// \"data not given\". /// \return \b CIFRC_Ok: success. int GetInteger ( int & I, cpstr TName, int nrow, bool Remove=false ); /// \brief Fetches set of values, corresponding to the given /// tag, in the given range of rows, as a vector of /// strings. /// \param[out] S reference to string vector to accept /// the values. if \b S==NULL , the vector will be /// allocated with starting index of \b i1. /// \param[in] TName character string with tag name /// \param[in] i1 minimum row number to fetch, the actual /// index will be calculated as \b max(0,min(i1,i2)) /// \param[in] i2 maximum row number to fetch, the actual /// index will be calculated as /// \b min(GetLoopLength()-1,max(i1,i2)) /// \param[in] Remove flag to remove fetched fields from /// the loop after they are read. /// \return \b CIFRC_NoTag: tag is not found /// \return \b CIFRC_WrongIndex: invalid range of rows /// \return \b CIFRC_Ok: success. /// /// For safe use, \b S should be pre-allocated by calling /// process. Only elements \b S[i1] to \b S[i2] will contain /// fetched data, others remain untouched. The calling /// process is responsible for the disposal of \b S. Example: /// \code /// mmcif::Loop loop; /// psvector S; // equivalent to char **S /// int i,i1,i2,rc,n; /// /// // ... get loop data /// /// n = loop.GetLoopLength(); /// i1 = 5; i2 = n - 5; // could be wrong! /// /// // allocate vector of strings /// GetVectorMemory ( S,n,0 ); // "0" for starting index /// for (i=0;i