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