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