-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathHeader.h
421 lines (381 loc) · 13.9 KB
/
Header.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
// File declaring the data structure that can mirror FITS headers.
#ifndef FHEADER_H
#define FHEADER_H
#include <sstream>
#include <list>
#include <algorithm>
#include "FitsTypes.h"
#include "Std.h"
/************************ Header ****************************
* Contains auxiliary information for Images. This includes:
* a list of COMMENT strings
* a list of HISTORY strings
* a list of other keyword-indexed records, a la FITS headers.
*
* Header records have a keyword string, a value, and optional comment
* and units strings. Individual records have a base class
* HdrRecordBase
* and the derived classes are
* HdrRecordNull (no value)
* HdrRecord<T> (value of type T).
* Header keywords are case-insensitive and at least for FITS are
* limited to 8 characters.
*
* Usually the client will not construct Headers, but always get
* them from Images. The most common methods will be:
* append("keyword",value,"comment")
* ...to append a new keyword/value pair to the header.
* replace("keyword",value,"comment")
* ...replaces old keyword header, or appends if no old one
* getValue("keyword", value)
* ...returns true & fills the value if keyword is in header,
* returns false if keyword is not already in header.
* addComment("comment")
* ...appends a new COMMENT record.
* addHistory("history") is a new HISTORY entry.
*
* Less frequently the client will use a list-oriented access to all
* the header records.
* There is an internal pointer to the header record list.
* It is manipulated by rewind(), atEnd(), incr(). Pointer to the
* "current" record is current(). find() moves the pointer to next
* record that matches a keyword.
* append() or insert() add new records at end or at current pointer.
* You can call either of these with a keyword,value pair, and the
* correct type for HdrRecord<T> will be inferred from the value.
* erase() gets rid of either a certain keyword, or the current record.
*
* clear() flushes all records, plus the HISTORY and COMMENT lists.
* size() is total number of records, HISTORY, and COMMENT entries.
*
* A copy or assignment of a Header is a deep copy (as long as
* all the header types T are). Header owns all the HdrRecords
* and will delete them upon deletion of hte Header.
*
* Each Header keeps an "isAltered"
* flag so one can note whether it is unchanged since its creation or
* last call to notAltered().
*
* You can't erase individual comments or history entries.
*****************************************************************/
namespace img {
using namespace std;
// Exception classes:
class HeaderError: public std::runtime_error {
public:
HeaderError(const string m=""):
std::runtime_error("img::Header Error: " + m) {}
};
class HeaderLockError: public HeaderError {
public:
HeaderLockError(const string m=""):
HeaderError("Write access to locked data " + m) {}
};
//////////////////////////////////////////////////////////////////////////
// Auxiliary information held for all images
//////////////////////////////////////////////////////////////////////////
// First the classes that are individual Header records:
string KeyFormat(const string input);
// Base class for all header entries
class HdrRecordBase {
public:
HdrRecordBase(const string _kw, const string _com="",
const string _un=""): keyword(KeyFormat(_kw)),
comment(_com),
units(_un) {}
virtual ~HdrRecordBase() {}
virtual HdrRecordBase* duplicate() const {
return new HdrRecordBase(*this);
}
bool matchesKey(const string _k) const {
return keyword==KeyFormat(_k);
}
string getComment() const {return comment;}
void setComment(const string _c) {comment=_c;}
string getUnits() const {return units;}
void setUnits(const string _u) {units=_u;}
string getKeyword() const {return keyword;}
void setKeyword(const string _k) {keyword=KeyFormat(_k);}
virtual void reset(const string _kw, const string _com="",
const string _un="") {
keyword=_kw; comment=_com; units=_un;
}
// Two functions needed for easy interface to CFITSIO:
virtual FITS::DataType dataType() const {return FITS::Tnull;}
// return (void *) pointer to the value, if any
virtual void* voidPtr() {return 0;}
virtual const void* voidPtr() const {return 0;}
// Set value for this entry from string; return true on failure
virtual bool setValueString(const string _v) {return false;}
virtual string getValueString() const {return "";}
string writeCard() const;
protected:
string keyword;
string comment;
string units;
};
class HdrRecordNull: public HdrRecordBase {
public:
// no additional data over base class
HdrRecordNull(const string _kw, const string _com="",
const string _un=""): HdrRecordBase(_kw,_com,_un) {}
virtual HdrRecordNull* duplicate() const {
return new HdrRecordNull(*this);
}
};
// Header Record that holds arbitary data class:
template <class T>
class HdrRecord: public HdrRecordBase {
private:
T val;
mutable string valString; //string representation of value
public:
HdrRecord(const string _kw,
const T _val,
const string _com="",
const string _un=""): val(_val),
HdrRecordBase(_kw, _com, _un) {}
virtual HdrRecord* duplicate() const {
return new HdrRecord(*this);
}
T& Value() {return val;}
const T& Value() const {return val;}
void* voidPtr() {return static_cast<void *> (&val);}
const void* voidPtr() const {return static_cast<const void *> (&val);}
bool setValueString(const string _v) {
istringstream iss(_v.c_str());
string leftover;
return !(iss >> val) || (iss >> leftover);
};
string getValueString() const {
ostringstream os;
os << val;
return os.str();
}
FITS::DataType dataType() const {return FITS::FITSTypeOf<T>();}
};
//specializations for bool
template<>
string
HdrRecord<bool>::getValueString() const;
template<>
bool
HdrRecord<bool>::setValueString(const string _v);
//and for string - enclose in quotes
template<>
string
HdrRecord<string>::getValueString() const;
// Also specialize double since default formatting is not good;
// need to force printing of decimal point.
template<>
string
HdrRecord<double>::getValueString() const;
///////////////////////////////////////////////////////////////\
// Now the class for Header itself:
class Header {
private:
mutable std::list<HdrRecordBase*> hlist;
mutable std::list<HdrRecordBase*>::iterator hptr; //current record
bool isAltered;
bool lock;
std::list<string> lcomment; //Comment and History strings
std::list<string> lhistory;
void checkLock(const string& s="") {
if (isLocked()) throw HeaderLockError(s);
}
public:
Header(): hlist(), hptr(hlist.begin()), isAltered(false), lock(false) {}
Header(const Header& rhs): hlist(), hptr(hlist.begin()),
isAltered(false), lock(false) {
copyFrom(rhs);
}
void copyFrom(const Header& rhs) {
checkLock("copyFrom()");
hlist.clear(); lcomment.clear(); lhistory.clear();
std::list<HdrRecordBase*>::const_iterator rhsptr;
for (rhsptr=rhs.hlist.begin(); rhsptr!=rhs.hlist.end(); ++rhsptr)
hlist.push_back( (*rhsptr)->duplicate());
hptr = hlist.begin();
std::list<string>::const_iterator sptr;
for (sptr=rhs.lcomment.begin(); sptr!=rhs.lcomment.end(); ++sptr)
lcomment.push_back(*sptr);
for (sptr=rhs.lhistory.begin(); sptr!=rhs.lhistory.end(); ++sptr)
lhistory.push_back(*sptr);
touch();
}
Header& operator=(const Header& rhs) {
if (this==&rhs) return *this;
checkLock("operator=");
copyFrom(rhs); //copyFrom checks locking and touches
isAltered = false;
return *this;
}
~Header() {
// Unlock for destruction:
lock = false;
for (hptr=hlist.begin(); hptr!=hlist.end(); ++hptr)
delete *hptr;
}
Header* duplicate() const {
return new Header(*this);
}
// Clear all header records, plus comments & history
void clear() {
checkLock("clear()");
hlist.clear();
hptr=hlist.begin();
lcomment.clear();
lhistory.clear();
touch();
}
void reset() {clear();}
// History/Comment accessors
const std::list<string>& comments() const {return lcomment;}
const std::list<string>& history() const {return lhistory;}
void addComment(const string s) {lcomment.push_back(s); touch();}
void addHistory(const string s) {lhistory.push_back(s); touch();}
bool isChanged() const {return isAltered;} //changed since creation?
void clearChanged() {isAltered=false;} //reset altered flag
void touch() {isAltered=true;}
bool isLocked() const {return lock;}
void setLock() {lock = true;}
// Manipulate the pointer to current header record:
void rewind() const {hptr=hlist.begin();}
bool atEnd() const {return hptr==hlist.end();}
int size() const {
return hlist.size() + lcomment.size() + lhistory.size();
}
HdrRecordBase* current() {checkLock("current()"); touch(); return *hptr;}
const HdrRecordBase* constCurrent() const {return *hptr;}
void incr() const {++hptr;}
const HdrRecordBase* current() const {return constCurrent();}
// Append contents of another header to this one
// Overwrite any duplicate keywords (except HISTORY and COMMENT)
void operator+=(const Header& rhs) {
checkLock("operator+=()");
if (this==&rhs) return;
for (list<HdrRecordBase*>::const_iterator rptr=rhs.hlist.begin();
rptr!=rhs.hlist.end();
++rptr) {
try {erase((*rptr)->getKeyword());} catch (HeaderError &i) {}
hlist.push_back( (*rptr)->duplicate());
}
lcomment.insert(lcomment.end(),
rhs.lcomment.begin(),
rhs.lcomment.end());
lhistory.insert(lhistory.end(),
rhs.lhistory.begin(),
rhs.lhistory.end());
touch();
}
// Add/remove header records, by base class ptr or keyword
void append(HdrRecordBase* record) {checkLock("append()"); hlist.push_back(record); touch();}
void insert(HdrRecordBase* record) {
checkLock("insert()"); hlist.insert(hptr,record); touch();
}
void erase() {
checkLock("erase()");
delete *hptr; hptr=hlist.erase(hptr); touch();
}
void erase(const string kw) {
if (find(kw)) {
checkLock("erase()");
delete *hptr;
hptr=hlist.erase(hptr);
touch();
} else
throw HeaderError("Cannot find record with keyword " + kw);
}
template <class T>
void append(const string keyword, const T& value,
const string comment="", const string units="") {
checkLock("append()");
hlist.push_back(new HdrRecord<T>(keyword, value, comment,units));
touch();
}
template <class T>
void replace(const string keyword, const T& value,
const string comment="", const string units="") {
checkLock("replace()");
try {erase(keyword);} catch (HeaderError &i) {}
append(keyword, value, comment, units);
}
void appendNull(const string keyword,
const string comment="") {
checkLock("appendNull()");
hlist.push_back(new HdrRecordNull(keyword, comment));
touch();
}
template <class T>
void insert(const string keyword, const T& value,
const string comment="", const string units="") {
checkLock("insert()");
hlist.insert(hptr, new HdrRecord<T>(keyword, value, comment,units));
touch();
}
void insertNull(const string keyword,
const string comment="") {
checkLock("insertNull()");
hlist.insert(hptr, new HdrRecordNull(keyword, comment));
touch();
}
HdrRecordBase* find(const string keyword) {
list<HdrRecordBase*>::iterator start(hptr);
checkLock("find()");
touch(); // ?? note header is marked as altered just for returning
// a non-const pointer to header record.
for ( ; hptr!=hlist.end(); ++hptr)
if ((*hptr)->matchesKey(keyword)) return *hptr;
// search from beginning to starting point
for (hptr=hlist.begin(); hptr!=hlist.end() && hptr!=start; ++hptr)
if ((*hptr)->matchesKey(keyword)) return *hptr;
return 0; //null pointer if nothing found
}
const HdrRecordBase* findConst(const string keyword) const {
std::list<HdrRecordBase*>::iterator start(hptr);
for ( ; hptr!=hlist.end(); ++hptr)
if ((*hptr)->matchesKey(keyword)) return *hptr;
// search from beginning to starting point
for (hptr=hlist.begin(); hptr!=hlist.end() && hptr!=start; ++hptr)
if ((*hptr)->matchesKey(keyword)) return *hptr;
return 0; //null pointer if nothing found
}
const HdrRecordBase* find(const string keyword) const {
return findConst(keyword);
}
// Get/set the value of an existing record. Bool returns false if
// keyword doesn't exist or does not match type of argument.
template <class T>
bool getValue(const string keyword, T& outVal) const;
template <class T>
bool setValue(const string keyword, const T& inVal);
};
// Get header from a character stream or vice-versa:
std::istream& operator>>(std::istream& is, Header& h);
std::ostream& operator<<(std::ostream& os, const Header& h);
template <class T>
bool
Header::getValue(const string keyword, T& outVal) const {
const HdrRecordBase* b=findConst(keyword);
if (!b) return false;
const HdrRecord<T> *dhdr;
dhdr = dynamic_cast<const HdrRecord<T>*> (b);
if (!dhdr) return false;
outVal = dhdr->Value();
return true;
}
template <class T>
bool
Header::setValue(const string keyword, const T& inVal) {
checkLock("setValue()");
HdrRecordBase* b=find(keyword);
if (!b) return false;
HdrRecord<T> *dhdr;
dhdr = dynamic_cast<HdrRecord<T>*> (b);
if (!dhdr) return false;
dhdr->Value() = inVal;
touch();
return true;
}
} // namespace img
#endif //FHEADER_H