New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Header consistency, validation, and standards. #170
Conversation
other small tweaks.
This PR is a significant overhaul of the FITS header handling (mostly affecting
@olebole, @mbtaylor, @Zlika, @vforchi, @pdowler, @mabula117, @wcleveland: Since you have been engaged with the repo of late, I would like to invite you to take a look at the PR before I merge it. Build the jar from it if you like, and try it with your application. If you have questions or comments, or you find new bugs in the PR itself, feel free to comment here, or in one of the linked issues -- whichever is more appropriate. If no showstoppers come up, I will plan to merge the PR at the start of November, and proceed to composing a pre-release of 1.16 soon after that, so we can do more testing and fix any remaining issues before the official release (maybe in December)... I also want to thank you for your contributions to the upcoming release, and for your efforts in general! :-) |
I have not attempted a full review of all the changes, but I've built it and run some application code and unit tests against it. The construction-time exceptions for invalid headers required some changes in my code, since I'd previously been handling that by some patched code within HeaderCard that silently tidied up illegal characters; but I agree the way it's done now makes more sense. Other than that, I haven't spotted any issues. The changes are quite extensive, so I can imagine there might be some nasty surprises relating to changed behaviour of my applications in the future... but we'll wait and see. |
Thanks Mark for taking a look. I agree that some nasty surprises might be lurking, but hopefully much fewer than before. (There have been a little too many nasty surprises for my taste earlier). I did try to add a lot more test scenarios also, to try pre-empt as many as I could think of -- but of course, for sure they don't cover all corner cases. When the bugs surface, we can address them with one or more maintenance release(s), which is how it's been done in the past too... I am also planning on a freeze period (~1 month) after composing a release candidate so whoever is willing can test it and we can fix only whatever new critical bugs we uncover in the process... |
Lots of fixes and improvements to the handling of headers:
Header Keywords
HeaderCardException
is thrown, with an informative description. The validation enforces the following properties:-
and_
, and max 8 characters for base keywords.0x20
thru0x7E
) for HIERARCH style keys. (Optional switch for case-sensitive vs upper-case-only processing viaIHierarchKeyFormatter.setCaseSensitive(boolean)
)HeaderCard.validateKey(String key)
can be used to check the validity of FITS keywords from anywhere, any time.IHierarchKeyFormatter
allows to select between upper-case only (case-insensitive) hierarch components (default), or mixed-case (case-sensitive) components.IHierarchKeyFormatter
is used to figure out exact space left after HIERARCH keys for the values. (So we don't breach the 80-character limit).HeaderCard.hasHierarchKey()
can be used to check if the keyword is a long HIERARCH-style keyword (true
only whenFitsFactory.setUseHierarch()
is enabled).NonStandard.CONTINUE
deprecated in favor of newStandard.CONTINUE
since it is now part of the FITS 4.0 standard.Header values
HeaderCard
keeps track of the underlying value class, which it was created with (making it unnecessary to guess type from serialized value for cards constructed in code, rather than parsed from string/stream). This also eliminates to track 'nullability' separately internally.0x20
thru0x7E
), and aHeaderCardException
is thrown if trying to construct aHeaderCard
with an illegal value. Similarly,setValue(String)
orsetComment(String)
will throw a runtimeIllegalArgumentException
is the argument contains characters not supported by FITS in headers.HeaderCard.validateChars(String key)
can be used to check the validity of FITS header values and comments, any time.HeaderCard.isValidChar(char)
can be used to check if specific characters are allowed in FITS headers, andString HeaderCard.sanitize(String)
can convert arbitrary strings for representation in FITS headers by replacing invalid characters with?
(I.e., making the changes by @olebole visible).null
Header values for all types (String
,Boolean
,Number
, orComplexValue
). FITS allows keyword assignments with no value field, regardless of what constructor orsetValue()
method was used.Number values
ComplexValue
class.HeaderCardException
being thrown. FITS has no standard for representing such values in the header. Also,setValue(...)
with NaN or Infinite values will throw anIllegalArgumentException
FitsFactory.setUseExponentD(boolean)
can be used to allow 'D' instead of 'E' for the exponent, when the value has more precision than can be supported by a 32-bit float. (FitsFactory.isUseExponentD()
can be used to check the setting).Float
toDouble
even if the number would otherwise fit in a 32-bit float.FlexFormat
class:BigInteger
only), automatically. Numbers are printed with their native precision for primitive types. ForBigDecimal
andBigInteger
, precision may be reduced to fit the value in the available card space, but always preserving at least 16 decimal places after the leading figure. Tailing zeroes will be ommitted. ALongValueException
is thrown only if there is not enough room to represent the values in the card space available. (This is similar to the earlier behavior but with more predictable results.)LongValueException
(runtime) is thrown.Number
, instead of separateint
,long
,float
,double
,BigInteger
,BigDecimal
cases. The simplification preserves the prior API, and expands it. It also makes it unnecessary to cast to/fromBigDecimal
when formatting/parsing number values.HeaderCard.isIntegerType()
andisDecimalType()
methods to easily determine whether the underlying value is some type of integer (byte
,short
,int
,long
, orBigInteger
), or a decimal type (float
,double
, orBigDecimal
).HeaderCardBuilder.scale(int)
/noScale()
methods in favor of the more meaningful and aptly namedprecision(int)
/autoPrecision()
methods.Formatting Settings
HeaderCardFormatter
andHeaderCardParser
classes to handle the formatting and parsing of header records. Both are more efficient than the previous implementations (FitsLineAppender and FitsSubString have been deprecated as a result).FitsFactory.setDefaults()
method resets ALL settings to default.FitsFactory
. (Some were hidden before.)Other changes
HeaderCard
constructors, which should not be public or are convoluted. (We'll remove them in a future release.)HeaderCard.createCommentStyleCard(String key, String comment)
, or the the even simplercreateCommentCard(String comment)
orcreateHistoryCard(String text)
. These replace an awfully confusing constructor that was used previously to create comment-style cards.HeaderCard.saveNewHeaderCard(...)
has been deprecated. Not only its name being misspelled ('save' instead of 'safe'), it is meant for internal use only, and should not be exposed to the public in future releases.HeaderCard
gained a few public convenience methods to make life easier, such asboolean isCommentStyleCard()
orint spaceForValue()
.HeaderCard
(and newly added classes), with more detailed descriptions, and cross-references.Header.get...Value()
is made more consistent, with or without default values or standard keys, for all. EspeciallygetStringValue()
with a default, which was a nagging omission before.Header.addValue(...)
andHeader.insert...(...)
methods now return the newly created card instead ofvoid
before. The caller may ignore it (same as before) or else use the new card directly instead of having to callHeader.findCard()
to locate it...Header.addValue(..., Complex, ...)
, andgetComplexValue(...)
convenience methods.Header.addHexValue(...)
, andgetHexValue(...)
convenience methods.Header.setParserWarningsEnabled(boolean)
, and the setting may be checked viaisParserWarningsEnabled()
. When enabled, the parsing of FITS headers will report on any violation of strict FITS standards, even if it does not prevent the successful reading of the header. Developers can use the logged information (with Logger name "nom.tam.fits.HeaderCardParser"), to learn about any/all issues affecting 3rd party headers.Header.insertCommentStyleMultiline()
method, which preserves long comments by wrapping them into multiple comment-style cards. It is now the default implementation used byHeader.insertComment(String)
,insertHistory(String)
, and the newinsertUnkeyedComment(String)
methods, which now return the number of cards inserted.Header.ensureCardSpace(int)
to allow to preallocate header space for a minimum number of cards. When writing to stream, the header will add blank cards as necessary to reserve the header space for future additions, as per FITS 4.0 specification. The method is called also when reading headers, s.t. they can stay rewritable with the same amount of reserved space. At the same time, the reading will not actually add trailing blank cards, such that new cards can be added as desired in the reserved preallocated space.Header.getMinimumSize()
can be used to find the minimum byte size a header can have given the preallocated card space.Header.resetOriginalSize()
is deprecated in favor of usingensureCardSpace()
, andHeader.getOriginalSize()
is tweaked to invariably return the size of the original header as was read (the fact that it did not before was a nasty 'feature'). As a result the header can stayrewritable()
though a much broader range of manipulation, as long as it continues to fit within the original byte size.Test cases have been adjusted as necessary or made more meaningful.