1 package nom.tam.fits;
2
3 import java.util.concurrent.ExecutorService;
4 import java.util.concurrent.Executors;
5 import java.util.concurrent.ThreadFactory;
6
7 import nom.tam.fits.header.Standard;
8 import nom.tam.fits.header.hierarch.IHierarchKeyFormatter;
9 import nom.tam.fits.header.hierarch.StandardIHierarchKeyFormatter;
10 import nom.tam.image.compression.hdu.CompressedImageData;
11 import nom.tam.image.compression.hdu.CompressedImageHDU;
12 import nom.tam.image.compression.hdu.CompressedTableData;
13 import nom.tam.image.compression.hdu.CompressedTableHDU;
14
15 /*
16 * #%L
17 * nom.tam FITS library
18 * %%
19 * Copyright (C) 2004 - 2024 nom-tam-fits
20 * %%
21 * This is free and unencumbered software released into the public domain.
22 *
23 * Anyone is free to copy, modify, publish, use, compile, sell, or
24 * distribute this software, either in source code form or as a compiled
25 * binary, for any purpose, commercial or non-commercial, and by any
26 * means.
27 *
28 * In jurisdictions that recognize copyright laws, the author or authors
29 * of this software dedicate any and all copyright interest in the
30 * software to the public domain. We make this dedication for the benefit
31 * of the public at large and to the detriment of our heirs and
32 * successors. We intend this dedication to be an overt act of
33 * relinquishment in perpetuity of all present and future rights to this
34 * software under copyright law.
35 *
36 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
37 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
38 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
39 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
40 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
41 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
42 * OTHER DEALINGS IN THE SOFTWARE.
43 * #L%
44 */
45
46 /**
47 * Controls the creation of HDUs to encapsulate a variery of data, based on a few configuration switches. The switches
48 * allow for toggling support for different conventions to set the desired compatibility level. The default settings
49 * produce FITS that are compatibel with version 4.0 of the standard (the latest at the time of writing this). The
50 * switches may also be used to make this library more backward compatible with its previous version also.
51 */
52 public final class FitsFactory {
53
54 private static final boolean DEFAULT_USE_ASCII_TABLES = false;
55
56 private static final boolean DEFAULT_USE_HIERARCH = true;
57
58 private static final boolean DEFAULT_USE_EXPONENT_D = false;
59
60 private static final boolean DEFAULT_LONG_STRINGS_ENABLED = true;
61
62 private static final boolean DEFAULT_CHECK_ASCII_STRINGS = false;
63
64 private static final boolean DEFAULT_ALLOW_TERMINAL_JUNK = true;
65
66 private static final boolean DEFAULT_ALLOW_HEADER_REPAIRS = true;
67
68 private static final boolean DEFAULT_SKIP_BLANK_AFTER_ASSIGN = false;
69
70 private static final boolean DEFAULT_CASE_SENSITIVE_HIERARCH = false;
71
72 /**
73 * AK: true is the legacy behavior TODO If and when it is changed to false, the corresponding Logger warnings in
74 * BinaryTable should also be removed.
75 */
76 private static final boolean DEFAULT_USE_UNICODE_CHARS = true;
77
78 private static final IHierarchKeyFormatter DEFAULT_HIERARCH_FORMATTER = new StandardIHierarchKeyFormatter();
79
80 /**
81 * An class for aggregating all the settings internal to {@link FitsFactory}.
82 *
83 * @author Attila Kovacs
84 */
85 protected static final class FitsSettings implements Cloneable {
86
87 private boolean useAsciiTables;
88
89 private boolean useHierarch;
90
91 private boolean useExponentD;
92
93 private boolean checkAsciiStrings;
94
95 private boolean allowTerminalJunk;
96
97 private boolean allowHeaderRepairs;
98
99 private boolean longStringsEnabled;
100
101 private boolean useUnicodeChars;
102
103 @Deprecated
104 private boolean skipBlankAfterAssign;
105
106 private IHierarchKeyFormatter hierarchKeyFormatter = DEFAULT_HIERARCH_FORMATTER;
107
108 private FitsSettings() {
109 useAsciiTables = DEFAULT_USE_ASCII_TABLES;
110 useHierarch = DEFAULT_USE_HIERARCH;
111 useUnicodeChars = DEFAULT_USE_UNICODE_CHARS;
112 checkAsciiStrings = DEFAULT_CHECK_ASCII_STRINGS;
113 useExponentD = DEFAULT_USE_EXPONENT_D;
114 allowTerminalJunk = DEFAULT_ALLOW_TERMINAL_JUNK;
115 allowHeaderRepairs = DEFAULT_ALLOW_HEADER_REPAIRS;
116 longStringsEnabled = DEFAULT_LONG_STRINGS_ENABLED;
117 skipBlankAfterAssign = DEFAULT_SKIP_BLANK_AFTER_ASSIGN;
118 hierarchKeyFormatter = DEFAULT_HIERARCH_FORMATTER;
119 hierarchKeyFormatter.setCaseSensitive(DEFAULT_CASE_SENSITIVE_HIERARCH);
120 }
121
122 @Override
123 protected FitsSettings clone() {
124 try {
125 return (FitsSettings) super.clone();
126 } catch (CloneNotSupportedException e) {
127 return null;
128 }
129 }
130
131 private FitsSettings copy() {
132 return clone();
133 }
134
135 /**
136 * Returns the formatter instance for HIERARCH style keywords. Our own standard is to define such keywords
137 * internally as starting with the string <code>HIERARCH.</code> followed by a dot-separated hierarchy, or just
138 * an unusually long FITS keywords that cannot be represented by a standard 8-byte keyword. The HIERARCH
139 * formatted will take such string keywords and will format them according to its rules when writing them to
140 * FITS headers.
141 *
142 * @return The formatter instance used for HIERARCH-style keywords.
143 */
144 protected IHierarchKeyFormatter getHierarchKeyFormatter() {
145 return hierarchKeyFormatter;
146 }
147
148 /**
149 * Checks if we should use the letter 'D' to mark exponents of double-precision values (in FITS headers and
150 * ASCII tables). For ecample, in the typical Java number formatting the String <code>1.37E-13</code> may
151 * represent either a <code>float</code> or <code>double</code> value -- which are not exactly the same. For
152 * that reason FITS offers the possibility to replace 'E' in the string formatted number with 'D' when the value
153 * specifies a double-precision number, thus disambiguating the two.
154 *
155 * @return <code>true</code> if we will use 'D' to denote the exponent of double-precision values in FITS
156 * headers and ASCII tables.
157 */
158 protected boolean isUseExponentD() {
159 return useExponentD;
160 }
161
162 /**
163 * Checks if we treat junk after the last properly formed HDU silently withotu generating an exception. When
164 * this setting is <code>true</code> we can read corrupted FITS files (at least partially) without raising an
165 * alarm.
166 *
167 * @return <code>true</code> if we allow additional bytes after the last readable HDU to be present in FITS
168 * files without throwing an exception. Otherwise <code>false</code>.
169 */
170 protected boolean isAllowTerminalJunk() {
171 return allowTerminalJunk;
172 }
173
174 /**
175 * Whether we check if ASCII strings in FITS files conform to the restricted set of characters (0x20 trough
176 * 0x7E) allowed by the FITS standard. If the checking is enabled, we will log any such violations so they can
177 * be inspected and perhaps fixed.
178 *
179 * @return <code>true</code> if we should check and report if string appearing in FITS files do not conform to
180 * specification. Otherwise <code>false</code>
181 */
182 protected boolean isCheckAsciiStrings() {
183 return checkAsciiStrings;
184 }
185
186 /**
187 * Checks if we allow storing long string values (using the OGIP 1.0 convention) in FITS headers. Such long
188 * string may span multiple 80-character header records. They are now standard as of FITS 4.0, but they were not
189 * in earlier specifications. When long strings are not enabled, we will throw a {@link LongValueException}
190 * whenever one tries to add a string value that cannot be contained in a single 80-character header record.
191 *
192 * @return <code>true</code> (default) if we allow adding long string values to out FITS headers. Otherwise
193 * <code>false</code>.
194 */
195 protected boolean isLongStringsEnabled() {
196 return longStringsEnabled;
197 }
198
199 /**
200 * @deprecated The FITS standard is very explicit that assignment must be "= " (equals followed by a space). If
201 * we allow skipping the space, it will result in a non-standard FITS, and may render it
202 * unreadable for other tools.
203 *
204 * @return whether to use only "=", instead of the standard "= " between the keyword and the value.
205 */
206 @Deprecated
207 protected boolean isSkipBlankAfterAssign() {
208 return skipBlankAfterAssign;
209 }
210
211 /**
212 * Whether to write tables as ASCII tables automatically if possible. Binary tables are generally always a
213 * better option, as they are both more compact and flexible but sometimes we might want to make our table data
214 * to be human readable in a terminal without needing any FITS-specific tool -- even though the 1970s is long
215 * past...
216 *
217 * @return <code>true</code> if we have a preference for writing table data in ASCII format (rather than
218 * binary), whenever that is possible. Otherwise <code>false</code>
219 */
220 protected boolean isUseAsciiTables() {
221 return useAsciiTables;
222 }
223
224 /**
225 * Whether we allow using HIERARCH-style keywords, which may be longer than the standard 8-character FITS
226 * keywords, and may specify a hierarchy, and may also allow upper and lower-case characters depending on what
227 * formatting rules we use. Our own standard is to define such keywords internally as starting with the string
228 * <code>HIERARCH.</code> followed by a dot-separated hierarchy, or just an unusually long FITS keywords that
229 * cannot be represented by a standard 8-byte keyword.
230 *
231 * @return <code>true</code> if we allow HIERARCH keywords. Otherwise <code>false</code>
232 */
233 protected boolean isUseHierarch() {
234 return useHierarch;
235 }
236
237 /**
238 * Checks if we allow storing Java <code>char[]</code> arrays in binary tables as 16-bit <code>short[]</code>.
239 * Otherwise we will store them as simple 8-bit ASCII.
240 *
241 * @return <code>true</code> if <code>char[]</code> is stored as <code>short[]</code> in binary tables, or
242 * <code>false</code> if we store than as 8-bit ASCII.
243 */
244 protected boolean isUseUnicodeChars() {
245 return useUnicodeChars;
246 }
247
248 /**
249 * Checks if we are tolerant to FITS standard violations when reading 3rd party FITS files.
250 *
251 * @return <code>true</code> if we tolerate minor violations of the FITS standard when interpreting headers,
252 * which are unlikely to affect the integrity of the FITS otherwise. The violations will still be
253 * logged, but no exception will be generated. Or, <code>false</code> if we want to generate
254 * exceptions for such error.s
255 */
256 protected boolean isAllowHeaderRepairs() {
257 return allowHeaderRepairs;
258 }
259
260 }
261
262 private static final FitsSettings GLOBAL_SETTINGS = new FitsSettings();
263
264 private static final ThreadLocal<FitsSettings> LOCAL_SETTINGS = new ThreadLocal<>();
265
266 private static ExecutorService threadPool;
267
268 /**
269 * the size of a FITS block in bytes.
270 */
271 public static final int FITS_BLOCK_SIZE = 2880;
272
273 /**
274 * @deprecated (<i>for internal use</i>) Will reduce visibility in the future
275 *
276 * @return Given a Header construct an appropriate data.
277 *
278 * @param hdr header to create the data from
279 *
280 * @throws FitsException if the header did not contain enough information to detect the type of the data
281 */
282 @Deprecated
283 public static Data dataFactory(Header hdr) throws FitsException {
284
285 if (ImageHDU.isHeader(hdr)) {
286 if (hdr.getIntValue(Standard.NAXIS, 0) == 0) {
287 return new NullData();
288 }
289
290 Data d = ImageHDU.manufactureData(hdr);
291 // Fix for positioning error noted by V. Forchi
292 if (hdr.findCard(Standard.EXTEND) != null) {
293 hdr.nextCard();
294 }
295 return d;
296 }
297 if (RandomGroupsHDU.isHeader(hdr)) {
298 return RandomGroupsHDU.manufactureData(hdr);
299 }
300 if (AsciiTableHDU.isHeader(hdr)) {
301 return AsciiTableHDU.manufactureData(hdr);
302 }
303 if (CompressedImageHDU.isHeader(hdr)) {
304 return CompressedImageHDU.manufactureData(hdr);
305 }
306 if (CompressedTableHDU.isHeader(hdr)) {
307 return CompressedTableHDU.manufactureData(hdr);
308 }
309 if (BinaryTableHDU.isHeader(hdr)) {
310 return BinaryTableHDU.manufactureData(hdr);
311 }
312 if (UndefinedHDU.isHeader(hdr)) {
313 return UndefinedHDU.manufactureData(hdr);
314 }
315 throw new FitsException("Unrecognizable header in dataFactory");
316 }
317
318 /**
319 * Whether the letter 'D' may replace 'E' in the exponential notation of doubl-precision values. FITS allows (even
320 * encourages) the use of 'D' to indicate double-recision values. For example to disambiguate between 1.37E-3
321 * (single-precision) and 1.37D-3 (double-precision), which are not exatly the same value in binary representation.
322 *
323 * @return Do we allow automatic header repairs, like missing end quotes?
324 *
325 * @since 1.16
326 *
327 * @see #setUseExponentD(boolean)
328 */
329 public static boolean isUseExponentD() {
330 return current().isUseExponentD();
331 }
332
333 /**
334 * Whether <code>char[]</code> arrays are written as 16-bit integers (<code>short[]</code>) int binary tables as
335 * opposed as FITS character arrays (<code>byte[]</code> with column type 'A'). See more explanation in
336 * {@link #setUseUnicodeChars(boolean)}.
337 *
338 * @return <code>true</code> if <code>char[]</code> get written as 16-bit integers in binary table columns (column
339 * type 'I'), or as FITS 1-byte ASCII character arrays (as is always the case for <code>String</code>)
340 * with column type 'A'.
341 *
342 * @since 1.16
343 *
344 * @see #setUseUnicodeChars(boolean)
345 */
346 public static boolean isUseUnicodeChars() {
347 return current().isUseUnicodeChars();
348 }
349
350 /**
351 * Whether extra bytes are tolerated after the end of an HDU. Normally if there is additional bytes present after an
352 * HDU, it would be the beginning of another HDU -- which must start with a very specific sequence of bytes. So,
353 * when there is data beyond the end of an HDU that does not appear to be another HDU, it's junk. We can either
354 * ignore it, or throw an exception.
355 *
356 * @return Is terminal junk (i.e., non-FITS data following a valid HDU) allowed.
357 *
358 * @see #setAllowTerminalJunk(boolean)
359 */
360 public static boolean getAllowTerminalJunk() {
361 return current().isAllowTerminalJunk();
362 }
363
364 /**
365 * Whether we allow 3rd party FITS headers to be in violation of the standard, attempting to make sense of corrupted
366 * header data as much as possible.
367 *
368 * @return Do we allow automatic header repairs, like missing end quotes?
369 *
370 * @see #setAllowHeaderRepairs(boolean)
371 */
372 public static boolean isAllowHeaderRepairs() {
373 return current().isAllowHeaderRepairs();
374 }
375
376 /**
377 * Returns the formatter instance for HIERARCH style keywords. Our own standard is to define such keywords
378 * internally as starting with the string <code>HIERARCH.</code> followed by a dot-separated hierarchy, or just an
379 * unusually long FITS keywords that cannot be represented by a standard 8-byte keyword. The HIERARCH formatted will
380 * take such string keywords and will format them according to its rules when writing them to FITS headers.
381 *
382 * @return the formatter to use for hierarch keys.
383 *
384 * @see #setHierarchFormater(IHierarchKeyFormatter)
385 */
386 public static IHierarchKeyFormatter getHierarchFormater() {
387 return current().getHierarchKeyFormatter();
388 }
389
390 /**
391 * Whether we can use HIERARCH style keywords. Such keywords are not part of the current FITS standard, although
392 * they constitute a recognised convention. Even if other programs may not process HIRARCH keywords themselves,
393 * there is generally no harm to putting them into FITS headers, since the convention is such that these keywords
394 * will be simply treated as comments by programs that do not recognise them.
395 *
396 * @return <code>true</code> if we are processing HIERARCH style keywords
397 *
398 * @see #setUseHierarch(boolean)
399 */
400 public static boolean getUseHierarch() {
401 return current().isUseHierarch();
402 }
403
404 /**
405 * whether ASCII tables should be used where feasible.
406 *
407 * @return <code>true</code> if we ASCII tables are allowed.
408 *
409 * @see #setUseAsciiTables(boolean)
410 */
411 public static boolean getUseAsciiTables() {
412 return current().isUseAsciiTables();
413 }
414
415 /**
416 * Checks whether we should check and validated ASCII strings that goe into FITS. FITS only allows ASCII characters
417 * between 0x20 and 0x7E in ASCII tables.
418 *
419 * @return Get the current status for string checking.
420 *
421 * @see #setCheckAsciiStrings(boolean)
422 */
423 public static boolean getCheckAsciiStrings() {
424 return current().isCheckAsciiStrings();
425 }
426
427 /**
428 * Whether we allow storing long string in the header, which do not fit into a single 80-byte header record. Such
429 * strings are then wrapped into multiple consecutive header records, OGIP 1.0 standard -- which is nart of FITS
430 * 4.0, and was a recognised convention before.
431 *
432 * @return <code>true</code> If long string support is enabled.
433 *
434 * @see #setLongStringsEnabled(boolean)
435 */
436 public static boolean isLongStringsEnabled() {
437 return current().isLongStringsEnabled();
438 }
439
440 /**
441 * @return whether to use only "=", instead of the standard "= " between the keyword and the value.
442 *
443 * @deprecated The FITS standard is very explicit that assignment must be "= " (equals followed by a blank space).
444 * If we allow skipping the space, it will result in a non-standard FITS, that is likely to break
445 * compatibility with other tools.
446 *
447 * @see #setSkipBlankAfterAssign(boolean)
448 */
449 @Deprecated
450 public static boolean isSkipBlankAfterAssign() {
451 return current().isSkipBlankAfterAssign();
452 }
453
454 /**
455 * .
456 *
457 * @deprecated (<i>for internal use</i>)/ Will reduce visibility in the future
458 *
459 * @return Given Header and data objects return the appropriate type of HDU.
460 *
461 * @param hdr the header, including a description of the data layout.
462 * @param d the type of data object
463 * @param <DataClass> the class of the data
464 *
465 * @throws FitsException if the operation failed
466 */
467 @Deprecated
468 @SuppressWarnings("unchecked")
469 public static <DataClass extends Data> BasicHDU<DataClass> hduFactory(Header hdr, DataClass d) throws FitsException {
470 if (d == null) {
471 return (BasicHDU<DataClass>) new NullDataHDU(hdr);
472 }
473 if (d instanceof ImageData) {
474 return (BasicHDU<DataClass>) new ImageHDU(hdr, (ImageData) d);
475 }
476 if (d instanceof CompressedImageData) {
477 return (BasicHDU<DataClass>) new CompressedImageHDU(hdr, (CompressedImageData) d);
478 }
479 if (d instanceof RandomGroupsData) {
480 return (BasicHDU<DataClass>) new RandomGroupsHDU(hdr, (RandomGroupsData) d);
481 }
482 if (d instanceof AsciiTable) {
483 return (BasicHDU<DataClass>) new AsciiTableHDU(hdr, (AsciiTable) d);
484 }
485 if (d instanceof CompressedTableData) {
486 return (BasicHDU<DataClass>) new CompressedTableHDU(hdr, (CompressedTableData) d);
487 }
488 if (d instanceof BinaryTable) {
489 return (BasicHDU<DataClass>) new BinaryTableHDU(hdr, (BinaryTable) d);
490 }
491 if (d instanceof UndefinedData) {
492 return (BasicHDU<DataClass>) new UndefinedHDU(hdr, (UndefinedData) d);
493 }
494 return null;
495 }
496
497 /**
498 * Creates an HDU that wraps around the specified data object. The HDUs header will be created and populated with
499 * the essential description of the data. The following HDU types may be returned depending on the nature of the
500 * argument:
501 * <ul>
502 * <li>{@link NullDataHDU} -- if the argument is <code>null</code></li>
503 * <li>{@link ImageHDU} -- if the argument is a regular numerical array, such as a <code>double[]</code>,
504 * <code>float[][]</code>, or <code>short[][][]</code></li>
505 * <li>{@link BinaryTableHDU} -- the the argument is an <code>Object[rows][cols]</code> type array with a regular
506 * structure and supported column data types, provided that it cannot be represented by an ASCII table <b>OR</b> if
507 * {@link FitsFactory#getUseAsciiTables()} is <code>false</code></li>
508 * <li>{@link AsciiTableHDU} -- Like above, but only when the data can be represented by an ASCII table <b>AND</b>
509 * {@link FitsFactory#getUseAsciiTables()} is <code>true</code></li>
510 * </ul>
511 *
512 * @return An appropriate HDU to encapsulate the given Java data object
513 *
514 * @param o The object to be described.
515 *
516 * @throws FitsException if the parameter could not be converted to a HDU because the binary representation of
517 * the object is not known..
518 *
519 * @deprecated Use {@link Fits#makeHDU(Object)} instead (this method may either be migrated to
520 * {@link Fits} entirely or else have visibility reduced to the package level).
521 */
522 public static BasicHDU<?> hduFactory(Object o) throws FitsException {
523 Data d;
524 Header h;
525
526 if (o == null) {
527 return new NullDataHDU();
528 } else if (o instanceof Header) {
529 h = (Header) o;
530 d = dataFactory(h);
531 } else if (ImageHDU.isData(o)) {
532 d = ImageHDU.encapsulate(o);
533 h = ImageHDU.manufactureHeader(d);
534 } else if (current().isUseAsciiTables() && AsciiTableHDU.isData(o)) {
535 d = AsciiTableHDU.encapsulate(o);
536 h = AsciiTableHDU.manufactureHeader(d);
537 } else if (BinaryTableHDU.isData(o)) {
538 d = BinaryTableHDU.encapsulate(o);
539 h = BinaryTableHDU.manufactureHeader(d);
540 } else {
541 throw new FitsException("This type of data is not supported for FITS representation");
542 }
543
544 return hduFactory(h, d);
545 }
546
547 // CHECKSTYLE:OFF
548 /**
549 * @deprecated (<i>duplicate method for internal use</i>) Same as {@link #hduFactory(Header, Data)},
550 * and will be removed in the future.
551 *
552 * @return Given Header and data objects return the appropriate type of HDU.
553 *
554 * @param hdr the header of the date
555 * @param d the data
556 * @param <DataClass> the class of the data
557 *
558 * @throws FitsException if the operation failed
559 */
560 @Deprecated
561 public static <DataClass extends Data> BasicHDU<DataClass> HDUFactory(Header hdr, DataClass d) throws FitsException {
562 return hduFactory(hdr, d);
563 }
564
565 // CHECKSTYLE:ON
566
567 // CHECKSTYLE:OFF
568 /**
569 * @return Given an object, create the appropriate FITS header to describe it.
570 *
571 * @param o The object to be described.
572 *
573 * @throws FitsException if the parameter could not be converted to a hdu.
574 *
575 * @deprecated Use {@link Fits#makeHDU(Object)} instead (will removed in the future. Duplicate of
576 * {@link #hduFactory(Object)}
577 */
578 @Deprecated
579 public static BasicHDU<?> HDUFactory(Object o) throws FitsException {
580 return hduFactory(o);
581 }
582
583 // CHECKSTYLE:ON
584
585 /**
586 * Restores all settings to their default values.
587 *
588 * @since 1.16
589 */
590 public static void setDefaults() {
591 FitsSettings s = current();
592 s.useExponentD = DEFAULT_USE_EXPONENT_D;
593 s.allowHeaderRepairs = DEFAULT_ALLOW_HEADER_REPAIRS;
594 s.allowTerminalJunk = DEFAULT_ALLOW_TERMINAL_JUNK;
595 s.checkAsciiStrings = DEFAULT_CHECK_ASCII_STRINGS;
596 s.longStringsEnabled = DEFAULT_LONG_STRINGS_ENABLED;
597 s.skipBlankAfterAssign = DEFAULT_SKIP_BLANK_AFTER_ASSIGN;
598 s.useAsciiTables = DEFAULT_USE_ASCII_TABLES;
599 s.useHierarch = DEFAULT_USE_HIERARCH;
600 s.useUnicodeChars = DEFAULT_USE_UNICODE_CHARS;
601 s.hierarchKeyFormatter = DEFAULT_HIERARCH_FORMATTER;
602 s.hierarchKeyFormatter.setCaseSensitive(DEFAULT_CASE_SENSITIVE_HIERARCH);
603 }
604
605 /**
606 * Sets whether 'D' may be used instead of 'E' to mark the exponent for a floating point value with precision beyond
607 * that of a 32-bit float.
608 *
609 * @param allowExponentD if <code>true</code> D will be used instead of E to indicate the exponent of a decimal with
610 * more precision than a 32-bit float.
611 *
612 * @since 1.16
613 *
614 * @see #isUseExponentD()
615 */
616 public static void setUseExponentD(boolean allowExponentD) {
617 current().useExponentD = allowExponentD;
618 }
619
620 /**
621 * Do we allow junk after a valid FITS file?
622 *
623 * @param allowTerminalJunk value to set
624 *
625 * @see #getAllowTerminalJunk()
626 */
627 public static void setAllowTerminalJunk(boolean allowTerminalJunk) {
628 current().allowTerminalJunk = allowTerminalJunk;
629 }
630
631 /**
632 * Do we allow automatic header repairs, like missing end quotes?
633 *
634 * @param allowHeaderRepairs value to set
635 *
636 * @see #isAllowHeaderRepairs()
637 */
638 public static void setAllowHeaderRepairs(boolean allowHeaderRepairs) {
639 current().allowHeaderRepairs = allowHeaderRepairs;
640 }
641
642 /**
643 * Enable/Disable checking of strings values used in tables to ensure that they are within the range specified by
644 * the FITS standard. The standard only allows the values 0x20 - 0x7E with null bytes allowed in one limited
645 * context. Disabled by default.
646 *
647 * @param checkAsciiStrings value to set
648 *
649 * @see #getCheckAsciiStrings()
650 */
651 public static void setCheckAsciiStrings(boolean checkAsciiStrings) {
652 current().checkAsciiStrings = checkAsciiStrings;
653 }
654
655 /**
656 * There is not a real standard how to write hierarch keys, default we use the one where every key is separated by a
657 * blank. If you want or need another format assing the formater here.
658 *
659 * @param formatter the hierarch key formatter.
660 */
661 public static void setHierarchFormater(IHierarchKeyFormatter formatter) {
662 current().hierarchKeyFormatter = formatter;
663 }
664
665 /**
666 * Enable/Disable longstring support.
667 *
668 * @param longStringsEnabled value to set
669 *
670 * @see #isLongStringsEnabled()
671 */
672 public static void setLongStringsEnabled(boolean longStringsEnabled) {
673 current().longStringsEnabled = longStringsEnabled;
674 }
675
676 /**
677 * If set to true the blank after the assign in the header cards in not written. The blank is stronly recommendet
678 * but in some cases it is important that it can be ommitted.
679 *
680 * @param skipBlankAfterAssign value to set
681 *
682 * @deprecated The FITS standard is very explicit that assignment must be "= " (equals followed
683 * by a blank space). It is also very specific that string values must have
684 * their opening quote in byte 11 (counted from 1). If we allow skipping the
685 * space, we will violate both standards in a way that is likely to break
686 * compatibility with other tools.
687 *
688 * @see #isSkipBlankAfterAssign()
689 */
690 @Deprecated
691 public static void setSkipBlankAfterAssign(boolean skipBlankAfterAssign) {
692 current().skipBlankAfterAssign = skipBlankAfterAssign;
693 }
694
695 /**
696 * Indicate whether ASCII tables should be used where feasible.
697 *
698 * @param useAsciiTables value to set
699 */
700 public static void setUseAsciiTables(boolean useAsciiTables) {
701 current().useAsciiTables = useAsciiTables;
702 }
703
704 /**
705 * Enable/Disable hierarchical keyword processing.
706 *
707 * @param useHierarch value to set
708 */
709 public static void setUseHierarch(boolean useHierarch) {
710 current().useHierarch = useHierarch;
711 }
712
713 /**
714 * <p>
715 * Enable/Disable writing <code>char[]</code> arrays as <code>short[]</code> in FITS binary tables (with column type
716 * 'I'), instead of as standard FITS 1-byte ASCII characters (with column type 'A'). The old default of this library
717 * has been to use unicode, and that behavior remains the default — the same as setting the argument to
718 * <code>true</code>. On the flipside, setting it to <code>false</code> provides more convergence between the
719 * handling of <code>char[]</code> columns and the nearly identical <code>String</code> columns, which have already
720 * been restricted to ASCII before.
721 * </p>
722 *
723 * @param value <code>true</code> to write <code>char[]</code> arrays as if <code>short[]</code> with column type
724 * 'I' to binary tables (old behaviour, and hence default), or else <code>false</code> to write
725 * them as <code>byte[]</code> with column type 'A', the same as for <code>String</code> (preferred
726 * behaviour)
727 *
728 * @since 1.16
729 *
730 * @see #isUseUnicodeChars()
731 */
732 public static void setUseUnicodeChars(boolean value) {
733 current().useUnicodeChars = value;
734 }
735
736 /**
737 * Returns the common thread pool that we use for processing FITS files.
738 *
739 * @return the thread pool for processing FITS files.
740 */
741 public static ExecutorService threadPool() {
742 if (threadPool == null) {
743 initializeThreadPool();
744 }
745 return threadPool;
746 }
747
748 /**
749 * Use thread local settings for the current thread instead of the global ones if the parameter is set to true, else
750 * use the shared global settings.
751 *
752 * @param useThreadSettings true if the thread should not share the global settings.
753 */
754 public static void useThreadLocalSettings(boolean useThreadSettings) {
755 if (useThreadSettings) {
756 LOCAL_SETTINGS.set(GLOBAL_SETTINGS.copy());
757 } else {
758 LOCAL_SETTINGS.remove();
759 }
760 }
761
762 private static void initializeThreadPool() {
763 synchronized (GLOBAL_SETTINGS) {
764 if (threadPool == null) {
765 threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2, //
766 new ThreadFactory() {
767 private int counter = 1;
768
769 @Override
770 public Thread newThread(Runnable r) {
771 Thread thread = new Thread(r, "nom-tam-fits worker " + counter++);
772 thread.setDaemon(true);
773 return thread;
774 }
775 });
776 }
777 }
778 }
779
780 /**
781 * Returns the current settings that guide how we read or produce FITS files.
782 *
783 * @return the current active settings for generating or interpreting FITS files.
784 */
785 protected static FitsSettings current() {
786 FitsSettings settings = LOCAL_SETTINGS.get();
787 if (settings == null) {
788 return GLOBAL_SETTINGS;
789 }
790 return settings;
791 }
792
793 private FitsFactory() {
794 }
795 }