View Javadoc
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 &mdash; 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 }