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 — 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 }