1 package nom.tam.fits.header;
2
3 import java.util.NoSuchElementException;
4
5 import nom.tam.fits.HeaderCard;
6
7 /*
8 * #%L
9 * INDI for Java Utilities for the fits image format
10 * %%
11 * Copyright (C) 2012 - 2015 indiforjava
12 * %%
13 * This is free and unencumbered software released into the public domain.
14 *
15 * Anyone is free to copy, modify, publish, use, compile, sell, or
16 * distribute this software, either in source code form or as a compiled
17 * binary, for any purpose, commercial or non-commercial, and by any
18 * means.
19 *
20 * In jurisdictions that recognize copyright laws, the author or authors
21 * of this software dedicate any and all copyright interest in the
22 * software to the public domain. We make this dedication for the benefit
23 * of the public at large and to the detriment of our heirs and
24 * successors. We intend this dedication to be an overt act of
25 * relinquishment in perpetuity of all present and future rights to this
26 * software under copyright law.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
31 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
32 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
33 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
34 * OTHER DEALINGS IN THE SOFTWARE.
35 * #L%
36 */
37
38 /**
39 * Interface for standardized header keyword implementations. Standardized header keys help with proper usage, with
40 * restricted use and value types as appropriate. Using keywords that implement this interface make it less likely for
41 * one to end up with inproperly constructed FITS files. Therefore, their usage is highly encouranged when possible.
42 *
43 * @see HeaderCard#setValueCheckingPolicy(nom.tam.fits.HeaderCard.ValueCheck)
44 * @see nom.tam.fits.Header#setKeywordChecking(nom.tam.fits.Header.KeywordCheck)
45 */
46 public interface IFitsHeader {
47
48 /** Max numeric index we may use to replace <i>n</i> in the Java name of indexed variables. */
49 int MAX_INDEX = 999;
50
51 /** An enumeration of HDU types in which a header keyword may be used. */
52 enum HDU {
53 /** keyword may be used in any HDU */
54 ANY,
55 /** image and/or random groups keywords */
56 IMAGE,
57 /** keyword for random groups only */
58 GROUPS,
59 /** Generic table keyword, can be used both in ASCII and binary tables */
60 TABLE,
61 /** keyword for ASCII tables only */
62 ASCII_TABLE,
63 /** keyword for binary tables */
64 BINTABLE,
65 /** keyword must appear in the primary HDU only */
66 PRIMARY,
67 /** keyword must appear in extension HDUs only */
68 EXTENSION,
69 /** @deprecated Use {@link #ANY} instead. */
70 @Deprecated
71 PRIMARY_EXTENSION;
72
73 }
74
75 /** Documentation sources for the various known conventions. */
76 enum SOURCE {
77 /**
78 * Checksum keywords. See
79 * <a href="http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/checksum/checksum.html">checksum doc</a>
80 */
81 CHECKSUM("http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/checksum/checksum.html"),
82
83 /**
84 * CXC keywords. See <a href=
85 * "http://cxc.harvard.edu/contrib/arots/fits/content.txt">http://cxc.harvard.edu/contrib/arots/fits/content.txt</a>
86 */
87 CXC("http://cxc.harvard.edu/contrib/arots/fits/content.txt"),
88 /**
89 * ESO keywords. See <a href=
90 * "http://arcdev.hq.eso.org/dicb/dicd/dic-1-1.4.html">http://arcdev.hq.eso.org/dicb/dicd/dic-1-1.4.html</a>
91 */
92 ESO("http://arcdev.hq.eso.org/dicb/dicd/dic-1-1.4.html"),
93 /**
94 * HEASARC keywords. See <a href=
95 * "http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/ofwg_recomm/r13.html">http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/ofwg_recomm/r13.html</a>
96 */
97 HEASARC("http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/ofwg_recomm/r13.html"),
98 /**
99 * The keyword is integral to the workings of the library. Users should not attempt set set or modify.
100 */
101 INTEGRAL(null),
102 /**
103 * Mandatory keywords defined by the FITS standard.
104 */
105 MANDATORY("http://heasarc.gsfc.nasa.gov/docs/fcg/standard_dict.html"),
106 /**
107 * MaxImDL keywords. See <a href=
108 * "http://www.cyanogen.com/help/maximdl/FITS_File_Header_Definitions.htm">http://www.cyanogen.com/help/maximdl/FITS_File_Header_Definitions.htm</a>
109 */
110 MaxImDL("http://www.cyanogen.com/help/maximdl/FITS_File_Header_Definitions.htm"),
111 /**
112 * NOAO keywords. See <a href=
113 * "http://iraf.noao.edu/iraf/web/projects/ccdmosaic/imagedef/fitsdic.html">http://iraf.noao.edu/iraf/web/projects/ccdmosaic/imagedef/fitsdic.html</a>
114 */
115 NOAO("http://iraf.noao.edu/iraf/web/projects/ccdmosaic/imagedef/fitsdic.html"),
116 /**
117 * Reserved keywords specified by the FITS standard.
118 */
119 RESERVED("http://heasarc.gsfc.nasa.gov/docs/fcg/standard_dict.html"),
120 /**
121 * ROSAT keywords. (No link available.)
122 */
123 ROSAT(null),
124 /**
125 * SBIG keywords. See <a href=
126 * "http://archive.sbig.com/pdffiles/SBFITSEXT_1r0.pdf">http://archive.sbig.com/pdffiles/SBFITSEXT_1r0.pdf</a>
127 */
128 SBIG("http://archive.sbig.com/pdffiles/SBFITSEXT_1r0.pdf"),
129 /**
130 * STScI keywords. See <a href=
131 * "http://tucana.noao.edu/ADASS/adass_proc/adass_95/zaraten/zaraten.html">http://tucana.noao.edu/ADASS/adass_proc/adass_95/zaraten/zaraten.html</a>
132 */
133 STScI("http://tucana.noao.edu/ADASS/adass_proc/adass_95/zaraten/zaraten.html"),
134 /**
135 * UCOLICK keywords. See <a href="http://www.ucolick.org">http://www.ucolick.org</a>
136 */
137 UCOLICK("http://www.ucolick.org"),
138 /**
139 * developed over time, source long forgotten.
140 */
141 UNKNOWN(null);
142
143 private final String url;
144
145 SOURCE(String url) {
146 this.url = url;
147 }
148
149 /**
150 * Returns the URL that defines this particular header value, which may be <code>null</code>.
151 *
152 * @return The URL that contains the keyword specification or <code>null</code> if unknown or undefined.
153 */
154 public String url() {
155 return url;
156 }
157 }
158
159 /** Values types to which implementing keywords can be restricted to. */
160 enum VALUE {
161 /** The keyword takes no value (i.e. END or comment-style keywords */
162 NONE,
163
164 /** keyword expects a logical 'T' or 'F' value */
165 LOGICAL,
166
167 /** keyword expects a String value */
168 STRING,
169
170 /** keyword expects an integer type value */
171 INTEGER,
172
173 /** keyword expects a floating-point value (integers allowed). */
174 REAL,
175
176 /** keyword expects a complex value */
177 COMPLEX,
178
179 /** The keyword may be used with any value type */
180 ANY
181 }
182
183 /**
184 * (<i>primarily for internal use</i>) Returns the concrete implementation of this header entry, which provides
185 * implementation of access methods.
186 *
187 * @return the implementation of this keyword, which provides the actual access methods. Implementations of this
188 * interface should simply return themselves.
189 *
190 * @since 1.19
191 */
192 default FitsKey impl() {
193 return null;
194 }
195
196 /**
197 * Returns the comment associated to this FITS header entry. The comment is entirely optional, and it may not be
198 * appear in full (or at all) in the FITS header. Comments should thus never contain essential information. Their
199 * purpose is only to provide non-essential extra information for human use.
200 *
201 * @return the associated standard comment.
202 *
203 * @see HeaderCard#getComment()
204 * @see HeaderCard#setComment(String)
205 */
206 default String comment() {
207 return impl().comment();
208 }
209
210 /**
211 * Returns the type of HDU(s) in which this header entry may be used.
212 *
213 * @return the HDU type(s) that this keyword may support.
214 */
215 default HDU hdu() {
216 return impl().hdu();
217 }
218
219 /**
220 * <p>
221 * Returns the FITS header keyword (or keyword template) for this header entry. Standard FITS keywords are limited
222 * to 8 characters, and contain only epper-case letters, numbers, hyphen, and underscore characters. Lower-case 'n'
223 * characters may be included as placeholders for indexing conventions that must be filled before the keyword may be
224 * used in headers and/or header cards.
225 * </p>
226 *
227 * @return the FITS header keyword for this entry. The returned keyword may include an indexing pattern (lower-case
228 * 'n' characters), which may need to be filled via {@link #n(int...)} before the keyword may be used to
229 * construct header cards or be used in FITS headers. (Alternative coordinate markers, via lower case
230 * 'a' at the end of the keyword definition, are stripped and should not be included in the returned
231 * keyword name pattern.)
232 *
233 * @see #n(int...)
234 */
235 default String key() {
236 return impl().key();
237 }
238
239 /**
240 * Constructs an indexed FITS header keyword entry from this stem, replacing index place-holders (indicated by
241 * lower-case 'n' in the name) with actual numerical values. Numbering for FITS header keywords always starts from
242 * 1, and should never exceed 999. Note, that for keywords that have multiple indices, you may specify them all in a
243 * single call, or may use successive calls to fill indices in the order they appear (the latter is somewhat less
244 * efficient, but still entirely legal).
245 *
246 * @param numbers the 1-based indices to add to the stem, in the order they appear in the the
247 * enum name.
248 *
249 * @return an indexed instance of this FITS header entry
250 *
251 * @throws IndexOutOfBoundsException if the index is less than 0 or exceeds 999. (In truth we should throw an
252 * exception for 0 as well, but seems to be common not-quite-legal FITS usage
253 * with 0 indices. Hence we relax the condition).
254 * @throws IllegalStateException if the resulting indexed keyword exceeds the maximum 8-bytes allowed for
255 * standard FITS keywords.
256 * @throws NoSuchElementException If more indices were supplied than can be filled for this keyword.
257 *
258 * @see #extractIndices(String)
259 */
260 default IFitsHeader n(int... numbers) throws IndexOutOfBoundsException, NoSuchElementException, IllegalStateException {
261 StringBuffer headerName = new StringBuffer(key());
262 for (int number : numbers) {
263 if (number < 0 || number > MAX_INDEX) {
264 throw new IndexOutOfBoundsException(key() + ": index " + number + " is out of bounds.");
265 }
266
267 int indexOfN = headerName.indexOf("n");
268
269 if (indexOfN < 0) {
270 throw new NoSuchElementException("Too many indices (" + numbers.length + ") supplied for " + key());
271 }
272
273 headerName.replace(indexOfN, indexOfN + 1, Integer.toString(number));
274 }
275
276 if (headerName.length() > HeaderCard.MAX_KEYWORD_LENGTH) {
277 throw new IllegalStateException("indexed keyword " + headerName.toString() + " is too long.");
278 }
279
280 return new FitsKey(headerName.toString(), status(), hdu(), valueType(), comment());
281 }
282
283 /**
284 * Returns the standard convention, which defines this FITS header entry
285 *
286 * @return the standard or convention that specifies this FITS heacer keyword
287 */
288 default SOURCE status() {
289 return impl().status();
290 }
291
292 /**
293 * The type(s) of value(s) this FITS header entry might take.
294 *
295 * @return the value type(s) for this FITS header entry
296 */
297 default VALUE valueType() {
298 return impl().valueType();
299 }
300
301 /**
302 * Extracts the indices for this stndardized key from an actual keyword realization. The keyword realization must be
303 * match the indexing and/or alternative coordinate system pattern for this key, or else an exception will be
304 * thrown.
305 *
306 * @param key The actual keyword as it appears in a FITS header
307 *
308 * @return An array of indices that appear in the key, or <code>null</code> if the keyword
309 * is not one that can be indexed.
310 *
311 * @throws IllegalArgumentException if the keyword does not match the pattern of this standardized FITS key
312 *
313 * @see #n(int...)
314 * @see Standard#match(String)
315 *
316 * @since 1.19
317 */
318 default int[] extractIndices(String key) throws IllegalArgumentException {
319 String pattern = key();
320 int i, j = 0, lp = pattern.length(), lk = key.length();
321 int n = 0;
322
323 for (i = 0; i < lp; i++) {
324 if (pattern.charAt(i) == 'n') {
325 n++;
326 }
327 }
328
329 if (n == 0) {
330 return null;
331 }
332
333 int[] idx = new int[n];
334
335 for (i = 0, n = 0; i < lp; i++) {
336 if (pattern.charAt(i) == 'n') {
337 if (i + 1 < lp && pattern.charAt(i + 1) == 'n') {
338 idx[n++] = key.charAt(j++) - '0';
339 } else {
340 int value = 0;
341 while (j < lk && Character.isDigit(key.charAt(j))) {
342 value = FitsKey.BASE_10 * value + key.charAt(j++) - '0';
343 }
344 idx[n++] = value;
345 }
346 } else if (key.charAt(j++) != pattern.charAt(i)) {
347 throw new IllegalArgumentException("Key " + key + " does no match pattern " + pattern);
348 }
349 }
350
351 return idx;
352 }
353
354 }