JDK-8169480 : Inconsistencies across Format class hierarchy in their API spec and actual implementation of Exceptions
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.text
  • Affected Version: 9
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2016-11-09
  • Updated: 2018-01-11
  • Resolved: 2017-01-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b152Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Description
Some of the APIs in Format class and its subclasses are inconsistent in regards to their api spec and implementation of Exception. Some of the apis are given below. Please check through all the possible apis in the Format hierarchy where these issues may need to be corrected.

Going as per the class hierarchy:

1. Format -> DateFormat -> SimpleDateFormat

1.1 format() methods

a.  SimpleDateFormat format method's api spec should be changed

    from:

    * @exception NullPointerException if the given {@code date} is {@code null}.
    */
    @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
    
    to:
    
    * @exception NullPointerException if any of the parameters is {@code null}.
    */
    @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
							   
b.  The api spec of Format's public abstract StringBuffer format(Object obj,.... is

     * @exception NullPointerException if <code>toAppendTo</code> or
     *            <code>pos</code> is null
     * @exception IllegalArgumentException if the Format cannot format the given
     *            object
     */
    public abstract StringBuffer format(Object obj,
                    StringBuffer toAppendTo,
                    FieldPosition pos);
                    
    The api spec of the overridden DateFormat's format() method should be updated with the similar @exception. This is a final method.

     * "h a z (zzzz)" and the alignment field DateFormat.TIMEZONE_FIELD,
     * the begin index and end index of fieldPosition will be set to
     * 5 and 8, respectively, for the first occurrence of the timezone
     * pattern character 'z'.
     * @see java.text.Format
     */
    public final StringBuffer format(Object obj, StringBuffer toAppendTo,
                                     FieldPosition fieldPosition)
    {

	
1.2 parse() methods


a. SimpleDateFormat defines NPE on parse method

    * @exception NullPointerException if <code>text</code> or <code>pos</code> is null.
    */
    @Override
    public Date parse(String text, ParsePosition pos)

Its immediate super class, DateFormat's parse() methods api spec does not define that

    * @exception ParseException if the beginning of the specified string
    *            cannot be parsed.
    */
    public Date parse(String source) throws ParseException
    {
    
    
    * start position if the parse failed.
    *
    * @return      A {@code Date}, or {@code null} if the input could not be parsed
    */
    public abstract Date parse(String source, ParsePosition pos);
    
We can not add that DateFormat's parse() in api spec since doing that may imposes NPE to be thrown by its subclasses which could be behavioral change.

So, we can add an implementation note (@implNote or @implSpec) about the current behaviour.


1.3 DateFormat's setCalendar(), setNumberFormat(), setTimeZone() should add an implementation note about the current behaviour.

1.4 Update the api spec of SimpleDateFormat's set2DigitYearStart(Date startDate) with "@throws NullPointerException if startDate is null"

2. Format -> NumberFormat -> DecimalFormat hierarchy

2.1 format() method

a.  NumberFormat format() method clearly specifies the exception

     * @exception        IllegalArgumentException if <code>number</code> is
     *                   null or not an instance of <code>Number</code>.
     * @exception        NullPointerException if <code>toAppendTo</code> or
     *                   <code>pos</code> is null
     * @exception        ArithmeticException if rounding is needed with rounding
     *                   mode being set to RoundingMode.UNNECESSARY
     * @see              java.text.FieldPosition
     */
    @Override
    public StringBuffer format(Object number,
                               StringBuffer toAppendTo,
                               FieldPosition pos) {

    But in below two methods, we can add an implementation note (@implNote, @implSpec) about the existing behaviour
    mentioning that the subclasses can define their own way of implementation of exception.
    
    
     * @exception        ArithmeticException if rounding is needed with rounding
     *                   mode being set to RoundingMode.UNNECESSARY
     * @see java.text.Format#format
     */
    public abstract StringBuffer format(double number,
                                        StringBuffer toAppendTo,
                                        FieldPosition pos);
    
    
     * @exception        ArithmeticException if rounding is needed with rounding
     *                   mode being set to RoundingMode.UNNECESSARY
     * @see java.text.Format#format
     */
    public abstract StringBuffer format(long number,
                                        StringBuffer toAppendTo,
                                        FieldPosition pos);
 
 
b.  DecimalFormat, should specify the NPE exception in api spec, if "result" or "fieldPosition" is null

     * @exception       ArithmeticException if rounding is needed with rounding
     *                  mode being set to RoundingMode.UNNECESSARY
     * @return The formatted number string
     * @see java.text.FieldPosition
     */
    @Override
    public StringBuffer format(long number, StringBuffer result,
                               FieldPosition fieldPosition) {
    
    
     * @exception ArithmeticException if rounding is needed with rounding
     *            mode being set to RoundingMode.UNNECESSARY
     * @return The formatted number string
     * @see java.text.FieldPosition
     */
    @Override
    public StringBuffer format(double number, StringBuffer result,
                               FieldPosition fieldPosition) {

							   
2.2. parse() method

a.  DecimalFormat implements and specifies NPE

     * @exception  NullPointerException if <code>text</code> or
     *             <code>pos</code> is null.
     */
    @Override
    public Number parse(String text, ParsePosition pos) {

    
    On the other hand, in NumberFormat, parse() does not specify any NPE, so an implementation note (@implNote or @implSpec)
    should be added about the existing behaviour (that a subclass may provide their own implementation and specification about NPE)
    
     * Does not throw an exception; if no object can be parsed, index is
     * unchanged!
     *
     * @param source the String to parse
     * @param parsePosition the parse position
     * @return the parsed value
     * @see java.text.NumberFormat#isParseIntegerOnly
     * @see java.text.Format#parseObject
     */
    public abstract Number parse(String source, ParsePosition parsePosition);
    
    * @exception ParseException if the beginning of the specified string
    *            cannot be parsed.
    */
    public Number parse(String source) throws ParseException {
	

3. Format -> MessageFormat

3.1 format() methods

a.  In MessageFormat, the api spec of format() methods can be updated to throw NPE if "result" is null.
    FieldPosition's null condition is handled internally, so it is not throwing any exception if FieldPosition is null.

     * @exception IllegalArgumentException if an argument in the
     *            <code>arguments</code> array is not of the type
     *            expected by the format element(s) that use it.
     */
    public final StringBuffer format(Object[] arguments, StringBuffer result,
                                     FieldPosition pos)    
                                    
                                    
     * @exception IllegalArgumentException if an argument in the
     *            <code>arguments</code> array is not of the type
     *            expected by the format element(s) that use it.
     */
    public final StringBuffer format(Object arguments, StringBuffer result,
                                     FieldPosition pos)


b.  Update the api spec with NPE, if pattern is null

     * @exception IllegalArgumentException if the pattern is invalid,
     *            or if an argument in the <code>arguments</code> array
     *            is not of the type expected by the format element(s)
     *            that use it.
     */
    public static String format(String pattern, Object ... arguments) {
        MessageFormat temp = new MessageFormat(pattern);
        return temp.format(arguments);

c.  Should add NPE in api spec of MessageFormat constructors (MessageFormat(pattern), MessageFormat(pattern, locale)) if "pattern" is null


4. Format -> NumberFormat -> ChoiceFormat

4.1. format() methods

a.  Update the api spec of the ChoiceFormat format(double number......) method to throw NPE if "toAppendTo" parameter is null

    * @param toAppendTo where text is appended.
    * @param status ignore no useful status is returned.
    */
    public StringBuffer format(double number, StringBuffer toAppendTo,
                               FieldPosition status) {


4.2. parse() method of ChoiceFormat

a.  Should update the api spec to throw NPE if "status" is null. For "text" parameter, the api spec should mention that a NPE will be thrown if choiceFormats.length is not zero and "text" is null, because     "text" is only checked when choiceFormats.length is not zero.
     
    * status.index is unchanged and status.errorIndex is set to the
    * first index of the character that caused the parse to fail.
    * @return A Number representing the value of the number parsed.
    */
    public Number parse(String text, ParsePosition status) {
        // find the best number (defined as the one with the longest parse)
        int start = status.index;
                              

4.3.  Update api spec of constructors of ChoiceFormat to throw NPE if their parameters are null

    * @param newPattern the new pattern string
    * @see #applyPattern
    */
    public ChoiceFormat(String newPattern)  {
        applyPattern(newPattern);
       
       
    * @param limits limits in ascending order
    * @param formats corresponding format strings
    * @see #setChoices
    */
    public ChoiceFormat(double[] limits, String[] formats) {
        setChoices(limits, formats);