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
|