LCOV - code coverage report
Current view: top level - ioutils/fits - fitsHeader.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 100.0 % 2 2
Test Date: 2026-02-19 16:58:26 Functions: 100.0 % 1 1

            Line data    Source code
       1              : /** \file fitsHeader.hpp
       2              :  * \brief Declares and defines a class to work with a FITS header
       3              :  * \ingroup fits_processing_files
       4              :  *
       5              :  */
       6              : 
       7              : //***********************************************************************//
       8              : // Copyright 2015-2025 Jared R. Males (jaredmales@gmail.com)
       9              : //
      10              : // This file is part of mxlib.
      11              : //
      12              : // mxlib is free software: you can redistribute it and/or modify
      13              : // it under the terms of the GNU General Public License as published by
      14              : // the Free Software Foundation, either version 3 of the License, or
      15              : // (at your option) any later version.
      16              : //
      17              : // mxlib is distributed in the hope that it will be useful,
      18              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      19              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      20              : // GNU General Public License for more details.
      21              : //
      22              : // You should have received a copy of the GNU General Public License
      23              : // along with mxlib.  If not, see <http://www.gnu.org/licenses/>.
      24              : //***********************************************************************//
      25              : 
      26              : #ifndef ioutils_fits__fitsHeader_hpp
      27              : #define ioutils_fits__fitsHeader_hpp
      28              : 
      29              : #include <list>
      30              : #include <unordered_map>
      31              : #include <iostream>
      32              : #include <vector>
      33              : #include <optional>
      34              : 
      35              : #include "fitsHeaderCard.hpp"
      36              : 
      37              : namespace mx
      38              : {
      39              : namespace fits
      40              : {
      41              : 
      42              : /// Class to manage a FITS file metadata header and provide fast access to the cards by keyword
      43              : /** Manages tasks such as insertion (avoiding duplicates), and keyword lookup.
      44              :  *
      45              :  * The \ref fitsHeaderCard "cards" are stored in a `std::list` to preserve order, but
      46              :  * a `std::unordered_multimap` is used to provide fast keyword lookup into the list.
      47              :  *
      48              :  * \tparam verboseT sets the error reporting \ref mx::verbose "verbosity"
      49              :  *
      50              :  * \ingroup fits_processing
      51              :  */
      52              : template <class verboseT = verbose::d>
      53              : class fitsHeader
      54              : {
      55              :   public:
      56              :     /// The list type
      57              :     /** We use a list,  rather than forward_list, so that append (insert at end) is constant time.
      58              :      *
      59              :      */
      60              :     typedef std::list<fitsHeaderCard<verboseT>> cardListT;
      61              : 
      62              :     /// The iterator type for the cards list
      63              :     typedef cardListT::iterator headerIteratorT;
      64              : 
      65              :     /// The map type
      66              :     /** Use unordered_multimap to allow multiple HISTORY and COMMENT properly, but be as efficient as possible.
      67              :      */
      68              :     typedef std::unordered_multimap<std::string, headerIteratorT> cardMapT;
      69              : 
      70              :     /// The value type for the card map
      71              :     typedef cardMapT::value_type cardMapValueT;
      72              : 
      73              :     /// The iterator type for the card map
      74              :     typedef cardMapT::iterator mapIteratorT;
      75              : 
      76              :   protected:
      77              :     /// The storage for the FITS header cards
      78              :     cardListT m_cardList;
      79              : 
      80              :     /// This multimap allows for fast lookup by keyword.
      81              :     cardMapT m_cardMap;
      82              : 
      83              :     /// Card with empty keyword used as an error sentinel.
      84              :     mutable fitsHeaderCard<verboseT> m_emptyCard;
      85              : 
      86              :   public:
      87              :     /// Default c'tor
      88              :     fitsHeader();
      89              : 
      90              :     /// Copy constructor
      91              :     fitsHeader( const fitsHeader &head /**< The fitsHeader to copy */ );
      92              : 
      93              :     /// Destructor
      94              :     ~fitsHeader();
      95              : 
      96              :     /// Assignment operator
      97              :     fitsHeader &operator=( const fitsHeader &head /**< The fitsHeader to copy */ );
      98              : 
      99              :     /// Get iterator to the beginning of the cards list
     100              :     headerIteratorT begin();
     101              : 
     102              :     /// Get iterator to the end of the cards list
     103              :     headerIteratorT end();
     104              : 
     105              :     /// Get iterator pointing to a specific element
     106              :     headerIteratorT iterator( const std::string &keyword /**< The keyword to look up*/ );
     107              : 
     108              :     /// Test whether the header is empty.
     109              :     bool empty();
     110              : 
     111              :     /// Get number of cards currently stored in the header.
     112              :     size_t size();
     113              : 
     114              :     /// Clear all cards from the header
     115              :     error_t clear();
     116              : 
     117              :     /// Get number of cards with a given keyword
     118              :     /** Returns the result of the count() method of the header map.
     119              :      *
     120              :      * \retval the number of cards with  keyword.
     121              :      */
     122              :     size_t count( const std::string &keyword /**< [in] the keyword to look up*/ );
     123              : 
     124              :     /// Erase card by keyword
     125              :     /** This can not be used to erase COMMENT or HISTORY cards.
     126              :      *
     127              :      */
     128              :     error_t erase( const std::string &keyword /**< [in] the keyword of the card to delete*/ );
     129              : 
     130              :     /// Erase card by iterator
     131              :     /** This handles COMMENT and HISTORY cards, deleting only the one pointed to by it
     132              :      *  using all the contents of the card (not just the keyword)
     133              :      */
     134              :     error_t erase( headerIteratorT it /**< [in] iterator pointing to the card to delete. */ );
     135              : 
     136              :     /// Erase the standard entries at the top of the header
     137              :     /** Erases each entry down to BSCALE.  This is useful for appending
     138              :      * a header from a previous image to a newly created file. Also erases boilerplate comments,
     139              :      * such as for long string.
     140              :      */
     141              :     error_t eraseStandardTop();
     142              : 
     143              :     /// Append a fitsHeaderCard to the end of the header
     144              :     /**
     145              :      */
     146              :     error_t append( const fitsHeaderCard<verboseT> &card /**< [in] the card to append*/ );
     147              : 
     148              :     /// Append a string card to the end of the header, from the three components of a card.
     149              :     /**
     150              :      */
     151              :     error_t append( const std::string &k, /**< [in] the keyword of the new card*/
     152              :                     const char *v,        /**< [in] the value of the new card*/
     153              :                     const std::string &c  /**< [in] the comment of the new card*/
     154              :     );
     155              : 
     156              :     /// Append a card to the end of the header, from the three components of a card.
     157              :     /**
     158              :      * \tparam typeT is the data type of the value
     159              :      *
     160              :      */
     161              :     template <typename typeT>
     162              :     error_t append( const std::string &k, /**< [in] the keyword of the new card*/
     163              :                     const typeT &v,       /**< [in] the value of the new card*/
     164              :                     const std::string &c  /**< [in] the comment of the new card*/
     165              :     );
     166              : 
     167              :     /// Append a card to the end of the header, from the components of a card with no comment.
     168              :     /**
     169              :      * \tparam typeT is the data type of the value
     170              :      *
     171              :      */
     172              :     template <typename typeT>
     173              :     error_t append( const std::string &k, /**< [in] the keyword of the new card*/
     174              :                     const typeT &v        /**< [in] the value of the new card*/
     175              :     );
     176              : 
     177              :     /// Append a card to the end of the header, with just a keyword.
     178              :     /** Appends a headerCard with unknownType
     179              :      *
     180              :      */
     181              :     error_t append( const std::string &k /**< [in] the keyword of the new card*/ );
     182              : 
     183              :     /// Append a fitsHeader to the end of the header
     184              :     /**
     185              :      */
     186              :     error_t append( fitsHeader &head /**< [in]  the fitsHeader to append*/ );
     187              : 
     188              :     /// Insert a card before another card.
     189              :     /**
     190              :      */
     191              :     error_t insert_before( headerIteratorT it,           /**< [in] iterator pointing to the
     192              :                                                                    element before which to insert*/
     193              :                            fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
     194              :     );
     195              : 
     196              :     /// Insert a card before another card, specifying the card by its components.
     197              :     /**
     198              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     199              :      *
     200              :      */
     201              :     template <typename typeT>
     202              :     error_t insert_before( headerIteratorT it,   /**< [in] iterator pointing to the element before which to insert*/
     203              :                            const std::string &k, /**< [in] the keyword of the new card*/
     204              :                            typeT v,              /**< [in] the value of the new card*/
     205              :                            const std::string &c  /**< [in] the comment of the new card*/
     206              :     );
     207              : 
     208              :     /// Insert a card before another card, specifying the card by its components.
     209              :     /**
     210              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     211              :      *
     212              :      */
     213              :     template <typename typeT>
     214              :     error_t insert_before( headerIteratorT it,   /**< [in] iterator pointing to the element before which to insert*/
     215              :                            const std::string &k, /**< [in] the keyword of the new card*/
     216              :                            typeT v               /**< [in] the value of the new card*/
     217              :     );
     218              : 
     219              :     /// Insert a card after another card.
     220              :     /**
     221              :      */
     222              :     error_t insert_after( headerIteratorT it,           /**< [in] iterator pointing to the element
     223              :                                                                   after which to insert*/
     224              :                           fitsHeaderCard<verboseT> card /**< [in] the card to insert*/
     225              :     );
     226              : 
     227              :     /// Insert a card after another card, specifying the card by its components.
     228              :     /**
     229              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     230              :      *
     231              :      */
     232              :     template <typename typeT>
     233              :     error_t insert_after( headerIteratorT it,   /**< [in] iterator pointing to the element after which to insert*/
     234              :                           const std::string &k, /**< [in] the keyword of the new card*/
     235              :                           typeT v,              /**< [in] the value of the new card*/
     236              :                           const std::string &c  /**< [in] the comment of the new card*/
     237              :     );
     238              : 
     239              :     /// Insert a card after another card, specifying the card by its components.
     240              :     /**
     241              :      * \tparam typeT is the type of the value, which is converted to string for insertion
     242              :      *
     243              :      */
     244              :     template <typename typeT>
     245              :     error_t insert_after( headerIteratorT it,   /**< [in] interator pointing to the element after which to insert*/
     246              :                           const std::string &k, /**< [in] the keyword of the new card*/
     247              :                           typeT v               /**< [in] the value of the new card*/
     248              :     );
     249              : 
     250              :     /// Card access by keyword operator
     251              :     /** Looks up the card by its keyword, and returns a reference to it.
     252              :      *
     253              :      * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
     254              :      * \returns on error: a fitsHeaderCard& to a card with an empty keyword
     255              :      */
     256              :     fitsHeaderCard<verboseT> &operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ );
     257              : 
     258              :     /// Card access by keyword operator (const version)
     259              :     /** Looks up the card by its keyword, and returns a reference to it.
     260              :      *
     261              :      * \returns on success: fitsHeaderCard& reference to the \ref fitsHeaderCard
     262              :      * \returns on error: a fitsHeaderCard& to a card with an empty keyword
     263              :      */
     264              :     const fitsHeaderCard<verboseT> &
     265              :     operator[]( const std::string &keyword /**< [in] the header keyword to look up*/ ) const;
     266              : 
     267              : }; // fitsHeader
     268              : 
     269              : template <class verboseT>
     270              : fitsHeader<verboseT>::fitsHeader()
     271              : {
     272              : }
     273              : 
     274              : template <class verboseT>
     275              : fitsHeader<verboseT>::fitsHeader( const fitsHeader &head )
     276              : {
     277              :     operator=( head );
     278              : }
     279              : 
     280              : template <class verboseT>
     281              : fitsHeader<verboseT>::~fitsHeader()
     282              : {
     283              :     clear();
     284              : }
     285              : 
     286              : template <class verboseT>
     287              : fitsHeader<verboseT> &fitsHeader<verboseT>::operator=( const fitsHeader &head )
     288              : {
     289              :     m_cardList = head.m_cardList;
     290              : 
     291              :     headerIteratorT it = m_cardList.begin();
     292              : 
     293              :     m_cardMap.clear();
     294              :     while( it != m_cardList.end() )
     295              :     {
     296              :         m_cardMap.insert( cardMapValueT( it->keyword(), it ) );
     297              :         ++it;
     298              :     }
     299              : 
     300              :     return *this;
     301              : }
     302              : 
     303              : template <class verboseT>
     304              : fitsHeader<verboseT>::headerIteratorT fitsHeader<verboseT>::begin()
     305              : {
     306              :     return m_cardList.begin();
     307              : }
     308              : 
     309              : template <class verboseT>
     310              : fitsHeader<verboseT>::headerIteratorT fitsHeader<verboseT>::end()
     311              : {
     312              :     return m_cardList.end();
     313              : }
     314              : 
     315              : template <class verboseT>
     316              : fitsHeader<verboseT>::headerIteratorT fitsHeader<verboseT>::iterator( const std::string &keyword )
     317              : {
     318              :     return m_cardMap.find( keyword )->second;
     319              : }
     320              : 
     321              : template <class verboseT>
     322              : bool fitsHeader<verboseT>::empty()
     323              : {
     324              :     return m_cardList.empty();
     325              : }
     326              : 
     327              : template <class verboseT>
     328              : size_t fitsHeader<verboseT>::size()
     329              : {
     330              :     return m_cardList.size();
     331              : }
     332              : 
     333              : template <class verboseT>
     334              : error_t fitsHeader<verboseT>::clear()
     335              : {
     336              :     m_cardList.clear();
     337              :     m_cardMap.clear();
     338              : 
     339              :     return error_t::noerror;
     340              : }
     341              : 
     342              : template <class verboseT>
     343              : size_t fitsHeader<verboseT>::count( const std::string &keyword )
     344              : {
     345              :     return m_cardMap.count( keyword );
     346              : }
     347              : 
     348              : template <class verboseT>
     349              : error_t fitsHeader<verboseT>::erase( const std::string &keyword )
     350              : {
     351              :     if( keyword == "COMMENT" )
     352              :     {
     353              :         return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase COMMENT by keyword" );
     354              :     }
     355              : 
     356              :     if( keyword == "HISTORY" )
     357              :     {
     358              :         return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "can't erase HISTORY by keyword" );
     359              :     }
     360              : 
     361              :     auto mit = m_cardMap.find( keyword );
     362              :     if( mit == m_cardMap.end() )
     363              :     {
     364              :         return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in map:" + keyword );
     365              :     }
     366              : 
     367              :     headerIteratorT it = mit->second;
     368              : 
     369              :     size_t nrmv;
     370              :     try
     371              :     {
     372              :         nrmv = m_cardMap.erase( keyword );
     373              :     }
     374              :     catch( const std::exception &e )
     375              :     {
     376              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     377              :                                                 "exception thrown erasing " + keyword + ": " + e.what() );
     378              :         // clang-format off
     379              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     380              :             return error_t::std_exception;
     381              :         #else
     382              :             throw;
     383              :         #endif
     384              :         // clang-format on
     385              :     }
     386              : 
     387              :     if( nrmv != 1 )
     388              :     {
     389              :         return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found:" + keyword );
     390              :     }
     391              : 
     392              :     if( it == m_cardList.end() )
     393              :     {
     394              :         return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in list:" + keyword );
     395              :     }
     396              : 
     397              :     try
     398              :     {
     399              :         m_cardList.erase( it ); // this doesn't seem to actually throw but just in case
     400              :                                 // unclear how to actually test for an error though...
     401              :     }
     402              :     catch( const std::exception &e )
     403              :     {
     404              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     405              :                                                 "exception thrown erasing " + keyword + ": " + e.what() );
     406              :         // clang-format off
     407              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     408              :             return error_t::std_exception;
     409              :         #else
     410              :             throw;
     411              :         #endif
     412              :         // clang-format on
     413              :     }
     414              : 
     415              :     return error_t::noerror;
     416              : }
     417              : 
     418              : template <class verboseT>
     419              : error_t fitsHeader<verboseT>::erase( headerIteratorT it )
     420              : {
     421              :     if( it == m_cardList.end() )
     422              :     {
     423              :         return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "invalid list iterator" );
     424              :     }
     425              : 
     426              :     std::string keyword = it->keyword();
     427              : 
     428              :     mapIteratorT mit = m_cardMap.find( keyword );
     429              : 
     430              :     if( mit == m_cardMap.end() )
     431              :     {
     432              :         return internal::mxlib_error_report<verboseT>( error_t::notfound, "keyword not found in map:" + keyword );
     433              :     }
     434              : 
     435              :     if( keyword == "COMMENT" || keyword == "HISTORY" )
     436              :     {
     437              :         while( mit->second->keyword() == keyword && it->comment() != mit->second->comment() )
     438              :         {
     439              :             ++mit;
     440              :         }
     441              : 
     442              :         if( mit == m_cardMap.end() )
     443              :         {
     444              :             return internal::mxlib_error_report<verboseT>( error_t::notfound,
     445              :                                                            keyword + " with matching comment not found" );
     446              :         }
     447              :     }
     448              : 
     449              :     m_cardMap.erase( mit ); // does not throw
     450              : 
     451              :     try
     452              :     {
     453              :         m_cardList.erase( it ); // this doesn't seem to actually throw but just in case
     454              :                                 // unclear how to actually test for an error though...
     455              :     }
     456              :     catch( const std::exception &e )
     457              :     {
     458              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     459              :                                                 "exception thrown erasing " + keyword + ": " + e.what() );
     460              :         // clang-format off
     461              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     462              :             return error_t::std_exception;
     463              :         #else
     464              :             throw;
     465              :         #endif
     466              :         // clang-format on
     467              :     }
     468              : 
     469              :     return error_t::noerror;
     470              : }
     471              : 
     472              : template <class verboseT>
     473              : error_t fitsHeader<verboseT>::eraseStandardTop()
     474              : {
     475              : 
     476              :     headerIteratorT it = begin(), nit;
     477              : 
     478              :     int n = 0;
     479              :     while( it != end() )
     480              :     {
     481              :         nit = it;
     482              :         ++nit;
     483              :         if( it->keyword() == "SIMPLE" || it->keyword() == "BITPIX" || it->keyword() == "NAXIS" ||
     484              :             it->keyword() == "NAXIS1" || it->keyword() == "NAXIS2" || it->keyword() == "NAXIS3" ||
     485              :             it->keyword() == "EXTEND" || it->keyword() == "BZERO" || it->keyword() == "BSCALE" ||
     486              :             it->keyword() == "LONGSTRN" )
     487              :         {
     488              :             mxlib_error_check( erase( it ) );
     489              :         }
     490              : 
     491              :         if( it->keyword() == "COMMENT" )
     492              :         {
     493              :             if( it->comment().find( "FITS (Flexible Image" ) != std::string::npos )
     494              :             {
     495              :                 mxlib_error_check( erase( it ) );
     496              :             }
     497              :             else if( it->comment().find( "and Astrophysics'" ) != std::string::npos )
     498              :             {
     499              :                 mxlib_error_check( erase( it ) );
     500              :             }
     501              :         }
     502              : 
     503              :         if( nit == end() )
     504              :         {
     505              :             break;
     506              :         }
     507              :         it = nit;
     508              :         ++n;
     509              :     }
     510              : 
     511              :     return error_t::noerror;
     512              : }
     513              : 
     514              : template <class verboseT>
     515              : error_t fitsHeader<verboseT>::append( const fitsHeaderCard<verboseT> &card )
     516              : {
     517              :     if( card.keyword() == "CONTINUE" )
     518              :     {
     519              :         headerIteratorT backIt = m_cardList.end();
     520              :         --backIt;
     521              :         return backIt->appendContinue( card );
     522              :     }
     523              : 
     524              :     // First check if duplicate key
     525              :     if( m_cardMap.count( card.keyword() ) > 0 )
     526              :     {
     527              :         if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
     528              :         {
     529              :             return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
     530              :                                                            "attempt to duplicate keyword " + card.keyword() );
     531              :         }
     532              :     }
     533              : 
     534              :     headerIteratorT insertedIt;
     535              : 
     536              :     // Now insert in list
     537              :     try
     538              :     {
     539              :         m_cardList.push_back( card );
     540              :         insertedIt = m_cardList.end();
     541              :         --insertedIt;
     542              :     }
     543              :     catch( const std::bad_alloc &e )
     544              :     {
     545              :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     546              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     547              :         // clang-format off
     548              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     549              :             return error_t::std_bad_alloc;
     550              :         #else
     551              :             throw;
     552              :         #endif
     553              :         // clang-format on
     554              :     }
     555              :     catch( const std::exception &e )
     556              :     {
     557              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     558              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     559              :         // clang-format off
     560              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     561              :             return error_t::std_exception;
     562              :         #else
     563              :             throw;
     564              :         #endif
     565              :         // clang-format on
     566              :     }
     567              : 
     568              :     // Then add to the Map.
     569              :     try
     570              :     {
     571              :         auto insres = m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
     572              : 
     573              :         if( insres == m_cardMap.end() )
     574              :         {
     575              :             internal::mxlib_error_report<verboseT>( error_t::error, "inserting " + card.keyword() + " failed" );
     576              :         }
     577              :     }
     578              :     catch( const std::bad_alloc &e )
     579              :     {
     580              :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     581              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     582              :         // clang-format off
     583              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     584              :             return error_t::std_bad_alloc;
     585              :         #else
     586              :             throw;
     587              :         #endif
     588              :         // clang-format on
     589              :     }
     590              :     catch( const std::exception &e )
     591              :     {
     592              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     593              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     594              :         // clang-format off
     595              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     596              :             return error_t::std_exception;
     597              :         #else
     598              :             throw;
     599              :         #endif
     600              :         // clang-format on
     601              :     }
     602              : 
     603              :     return error_t::noerror;
     604              : }
     605              : 
     606              : template <class verboseT>
     607              : error_t fitsHeader<verboseT>::append( fitsHeader &head )
     608              : {
     609              :     headerIteratorT it;
     610              : 
     611              :     for( it = head.begin(); it != head.end(); ++it )
     612              :     {
     613              :         mxlib_error_check( append( *it ) );
     614              :     }
     615              : 
     616              :     return error_t::noerror;
     617              : }
     618              : 
     619              : template <class verboseT>
     620              : error_t fitsHeader<verboseT>::append( const std::string &k )
     621              : {
     622              :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k ) ) );
     623              : }
     624              : 
     625              : template <class verboseT>
     626              : error_t fitsHeader<verboseT>::append( const std::string &k, const char *v, const std::string &c )
     627              : {
     628              :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v, c ) ) );
     629              : }
     630              : 
     631              : template <class verboseT>
     632              : template <typename typeT>
     633           36 : error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v, const std::string &c )
     634              : {
     635           36 :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v, c ) ) );
     636              : }
     637              : 
     638              : template <class verboseT>
     639              : template <typename typeT>
     640              : error_t fitsHeader<verboseT>::append( const std::string &k, const typeT &v )
     641              : {
     642              :     mxlib_error_return( append( fitsHeaderCard<verboseT>( k, v ) ) );
     643              : }
     644              : 
     645              : template <class verboseT>
     646              : error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, fitsHeaderCard<verboseT> card )
     647              : {
     648              :     // First check if duplicate key
     649              :     if( m_cardMap.count( card.keyword() ) > 0 )
     650              :     {
     651              :         if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
     652              :         {
     653              :             return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
     654              :                                                            "duplicate keyword: " + card.keyword() );
     655              :         }
     656              :     }
     657              : 
     658              :     // Now insert in list
     659              :     headerIteratorT insertedIt;
     660              : 
     661              :     try
     662              :     {
     663              :         insertedIt = m_cardList.insert( it, card );
     664              :     }
     665              :     catch( const std::bad_alloc &e )
     666              :     {
     667              :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     668              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     669              :         // clang-format off
     670              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     671              :             return error_t::std_bad_alloc;
     672              :         #else
     673              :             throw;
     674              :         #endif
     675              :         // clang-format on
     676              :     }
     677              :     catch( const std::exception &e )
     678              :     {
     679              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     680              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     681              :         // clang-format off
     682              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     683              :             return error_t::std_exception;
     684              :         #else
     685              :             throw;
     686              :         #endif
     687              :         // clang-format on
     688              :     }
     689              : 
     690              :     // Then add to the Map.
     691              :     try
     692              :     {
     693              :         m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
     694              :     }
     695              :     catch( const std::bad_alloc &e )
     696              :     {
     697              :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     698              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     699              :         // clang-format off
     700              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     701              :             return error_t::std_bad_alloc;
     702              :         #else
     703              :             throw;
     704              :         #endif
     705              :         // clang-format on
     706              :     }
     707              :     catch( const std::exception &e )
     708              :     {
     709              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     710              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     711              :         // clang-format off
     712              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     713              :             return error_t::std_exception;
     714              :         #else
     715              :             throw;
     716              :         #endif
     717              :         // clang-format on
     718              :     }
     719              : 
     720              :     return error_t::noerror;
     721              : }
     722              : 
     723              : template <class verboseT>
     724              : template <typename typeT>
     725              : error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
     726              : {
     727              :     mxlib_error_return( insert_before( it, fitsHeaderCard<verboseT>( k, v, c ) ) );
     728              : }
     729              : 
     730              : template <class verboseT>
     731              : template <typename typeT>
     732              : error_t fitsHeader<verboseT>::insert_before( headerIteratorT it, const std::string &k, typeT v )
     733              : {
     734              :     mxlib_error_return( insert_before( it, fitsHeaderCard<verboseT>( k, v ) ) );
     735              : }
     736              : 
     737              : template <class verboseT>
     738              : template <typename typeT>
     739              : error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, const std::string &k, typeT v, const std::string &c )
     740              : {
     741              :     mxlib_error_return( insert_after( it, fitsHeaderCard<verboseT>( k, v, c ) ) );
     742              : }
     743              : 
     744              : template <class verboseT>
     745              : error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, fitsHeaderCard<verboseT> card )
     746              : {
     747              :     // First check if duplicate key
     748              :     if( m_cardMap.count( card.keyword() ) > 0 )
     749              :     {
     750              :         if( card.type() != fitsType<fitsCommentType>() && card.type() != fitsType<fitsHistoryType>() )
     751              :         {
     752              :             return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
     753              :                                                            "duplicate keyword: " + card.keyword() );
     754              :         }
     755              :     }
     756              : 
     757              :     // Now insert in list
     758              :     headerIteratorT insertedIt;
     759              : 
     760              :     try
     761              :     {
     762              :         insertedIt = m_cardList.insert( ++it, card );
     763              :     }
     764              :     catch( const std::bad_alloc &e )
     765              :     {
     766              :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     767              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     768              :         // clang-format off
     769              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     770              :             return error_t::std_bad_alloc;
     771              :         #else
     772              :             throw;
     773              :         #endif
     774              :         // clang-format on
     775              :     }
     776              :     catch( const std::exception &e )
     777              :     {
     778              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     779              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     780              :         // clang-format off
     781              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     782              :             return error_t::std_exception;
     783              :         #else
     784              :             throw;
     785              :         #endif
     786              :         // clang-format on
     787              :     }
     788              : 
     789              :     // Then add to the Map.
     790              :     try
     791              :     {
     792              :         m_cardMap.insert( cardMapValueT( card.keyword(), insertedIt ) );
     793              :     }
     794              :     catch( const std::bad_alloc &e )
     795              :     {
     796              :         internal::mxlib_error_report<verboseT>( error_t::std_bad_alloc,
     797              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     798              :         // clang-format off
     799              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS )
     800              :             return error_t::std_bad_alloc;
     801              :         #else
     802              :             throw;
     803              :         #endif
     804              :         // clang-format on
     805              :     }
     806              :     catch( const std::exception &e )
     807              :     {
     808              :         internal::mxlib_error_report<verboseT>( error_t::std_exception,
     809              :                                                 "inserting " + card.keyword() + " :" + e.what() );
     810              :         // clang-format off
     811              :         #if defined( MXLIB_CATCH_ALL_EXCEPTIONS ) || defined( MXLIB_CATCH_NONALLOC_EXCEPTIONS )
     812              :             return error_t::std_exception;
     813              :         #else
     814              :             throw;
     815              :         #endif
     816              :         // clang-format on
     817              :     }
     818              : 
     819              :     return error_t::noerror;
     820              : }
     821              : 
     822              : template <class verboseT>
     823              : template <typename typeT>
     824              : error_t fitsHeader<verboseT>::insert_after( headerIteratorT it, const std::string &k, typeT v )
     825              : {
     826              :     mxlib_error_return( insert_after( it, fitsHeaderCard<verboseT>( k, v ) ) );
     827              : }
     828              : 
     829              : template <class verboseT>
     830              : fitsHeaderCard<verboseT> &fitsHeader<verboseT>::operator[]( const std::string &keyword )
     831              : {
     832              :     headerIteratorT it;
     833              : 
     834              :     auto mit = m_cardMap.find( keyword );
     835              : 
     836              :     // If not found, append it.
     837              :     if( mit == m_cardMap.end() )
     838              :     {
     839              :         error_t errc = append( keyword );
     840              :         if( errc != error_t::noerror )
     841              :         {
     842              :             internal::mxlib_error_report<verboseT>( errc, "appending " + keyword );
     843              :             m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
     844              :             return m_emptyCard;
     845              :         }
     846              : 
     847              :         // have to do new search
     848              :         mit = m_cardMap.find( keyword );
     849              :         if( mit == m_cardMap.end() )
     850              :         {
     851              :             internal::mxlib_error_report<verboseT>( error_t::notfound, "after appending " + keyword );
     852              :             return m_emptyCard;
     853              :         }
     854              :     }
     855              : 
     856              :     it = mit->second;
     857              : 
     858              :     return *it;
     859              : }
     860              : 
     861              : template <class verboseT>
     862              : const fitsHeaderCard<verboseT> &fitsHeader<verboseT>::operator[]( const std::string &keyword ) const
     863              : {
     864              :     auto mit = m_cardMap.find( keyword );
     865              :     if( mit == m_cardMap.end() )
     866              :     {
     867              :         internal::mxlib_error_report<verboseT>( error_t::notfound, keyword + " not found" );
     868              :         m_emptyCard.keyword( "" ); // we have to reset this every time b/c someone could have changed it
     869              :         return m_emptyCard;
     870              :     }
     871              : 
     872              :     headerIteratorT it = mit->second;
     873              : 
     874              :     return *it;
     875              : }
     876              : 
     877              : /** \addtogroup fits_utils
     878              :  * @{
     879              :  */
     880              : 
     881              : /// Convert the values in a std::vector of \ref fitsHeader "fits headers" into a std::vector of values.
     882              : /** Resizes the vector of the appropriate type.
     883              :  *
     884              :  * \returns error_t::noerror on success. \p bad will be empty.
     885              :  * \returns error_t::error if the keyword fails to convert for any header.  \p bad will contain the indices of
     886              :  *          \p heads for which the extraction of a value for \p keyw failed
     887              :  *
     888              :  * \tparam dataT is the type of the header value
     889              :  * \tparam fitsHeaderT is the fitsHeader type
     890              :  *
     891              :  */
     892              : template <typename dataT, class fitsHeaderT>
     893              : error_t headersToValues( std::vector<dataT> &v,           /**< [out] will contain the converted values*/
     894              :                          std::vector<size_t> &bad,        /**<[out] will contain the indices of any
     895              :                                                                     headers that failed conversion */
     896              :                          std::vector<fitsHeaderT> &heads, /**< [in] contains the headers */
     897              :                          const std::string &keyw          /**< [in] contains the keyword designating which
     898              :                                                                     value to convert*/
     899              : )
     900              : {
     901              :     v.resize( heads.size() );
     902              :     bad.clear();
     903              : 
     904              :     for( size_t i = 0; i < heads.size(); ++i )
     905              :     {
     906              :         error_t errc;
     907              :         v[i] = heads[i][keyw].template value<dataT>( &errc ); // convertFromString<dataT>(heads[i][keyw].value);
     908              : 
     909              :         if( errc != error_t::noerror )
     910              :         {
     911              :             bad.push_back( i );
     912              :             v[i] = std::numeric_limits<dataT>::max();
     913              :         }
     914              :     }
     915              : 
     916              :     if( bad.size() > 0 )
     917              :     {
     918              :         return error_t::error;
     919              :     }
     920              :     else
     921              :     {
     922              :         return error_t::noerror;
     923              :     }
     924              : }
     925              : 
     926              : /// Write the status of a Git repository to HISTORY in a FITS header.
     927              : /**
     928              :  * \param [in,out] head the HISTORY cards will be appended to this header
     929              :  * \param [in] repoName the name of the repository
     930              :  * \param [in] sha1 is the SHA-1 hash string of the repository
     931              :  * \param [in] modified whether or not the repository has been modified after the
     932              :  *                      commit referred to by sha1
     933              :  */
     934              : template <class fitsHeaderT>
     935              : void fitsHeaderGitStatus( fitsHeaderT &head, const std::string &repoName, const char *sha1, int modified )
     936              : {
     937              :     std::string hist = "Git status for " + repoName + ":";
     938              :     head.append( "", fitsHistoryType(), hist );
     939              : 
     940              :     hist = "   sha1=";
     941              :     hist += sha1;
     942              :     if( modified )
     943              :         hist += ", modified";
     944              : 
     945              :     head.append( "", fitsHistoryType(), hist );
     946              : }
     947              : 
     948              : ///@}
     949              : 
     950              : extern template class fitsHeader<verbose::d>;
     951              : 
     952              : } // namespace fits
     953              : } // namespace mx
     954              : 
     955              : #endif // ioutils_fits__fitsHeader_hpp
        

Generated by: LCOV version 2.0-1