From cfa6c8553d5494349a7f69ac71ec96b38acc3f8f Mon Sep 17 00:00:00 2001 From: Daniel Dreibrodt Date: Wed, 6 Nov 2019 17:28:58 +0100 Subject: [PATCH] Add NaN support to NSNumber (Fixes issue #54) --- src/main/java/com/dd/plist/NSNumber.java | 117 ++++++++++++++--------- 1 file changed, 70 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/dd/plist/NSNumber.java b/src/main/java/com/dd/plist/NSNumber.java index b94b071..4c19853 100644 --- a/src/main/java/com/dd/plist/NSNumber.java +++ b/src/main/java/com/dd/plist/NSNumber.java @@ -112,29 +112,38 @@ public NSNumber(byte[] bytes, final int startIndex, final int endIndex, final in public NSNumber(String text) { if (text == null) throw new IllegalArgumentException("The given string is null and cannot be parsed as number."); - try { - long l; - if (text.startsWith("0x")) { - l = Long.parseLong(text.substring(2), 16); - } else { - l = Long.parseLong(text); - } - this.doubleValue = this.longValue = l; - this.type = INTEGER; - } catch (Exception ex) { + + if (text.equalsIgnoreCase("nan")) { + this.doubleValue = Double.NaN; + this.longValue = 0; + this.type = REAL; + } + else if (text.equalsIgnoreCase("true") || text.equalsIgnoreCase("yes")) { + this.type = BOOLEAN; + this.boolValue = true; + this.doubleValue = this.longValue = 1; + } + else if (text.equalsIgnoreCase("false") || text.equalsIgnoreCase("no")) { + this.type = BOOLEAN; + this.boolValue = false; + this.doubleValue = this.longValue = 0; + } + else { try { - this.doubleValue = Double.parseDouble(text); - this.longValue = Math.round(this.doubleValue); - this.type = REAL; - } catch (Exception ex2) { + long l; + if(text.startsWith("0x")) { + l = Long.parseLong(text.substring(2), 16); + } else { + l = Long.parseLong(text); + } + this.doubleValue = this.longValue = l; + this.type = INTEGER; + } catch(Exception ex) { try { - this.boolValue = text.equalsIgnoreCase("true") || text.equalsIgnoreCase("yes"); - if(!this.boolValue && !(text.equalsIgnoreCase("false") || text.equalsIgnoreCase("no"))) { - throw new Exception("not a boolean"); - } - this.type = BOOLEAN; - this.doubleValue = this.longValue = this.boolValue ? 1 : 0; - } catch (Exception ex3) { + this.doubleValue = Double.parseDouble(text); + this.longValue = Math.round(this.doubleValue); + this.type = REAL; + } catch(Exception ex2) { throw new IllegalArgumentException("The given string neither represents a double, an int nor a boolean value."); } } @@ -224,21 +233,26 @@ public boolean isReal() { /** * Gets this instance's boolean value. * - * @return true if the value is true or non-zero, false otherwise. + * @return true if the value is true or non-zero and not Double.NaN; otherwise, false. */ public boolean boolValue() { if (this.type == BOOLEAN) return this.boolValue; else - return this.doubleValue() != 0; + return !Double.isNaN(this.doubleValue) && this.doubleValue != 0; } /** * Gets this instance's long integer value. * * @return The value of the number as a long. + * @throws IllegalStateException The integer value is not available because the value of this NSNumber instance is NaN. */ public long longValue() { + if (this.type == REAL && Double.isNaN(this.doubleValue)) { + throw new IllegalStateException("The integer value is not available because the value of this NSNumber instance is NaN."); + } + return this.longValue; } @@ -249,8 +263,13 @@ public long longValue() { * Otherwise the value might be inaccurate. * * @return The value of the number as an int. + * @throws IllegalStateException The integer value is not available because the value of this NSNumber instance is NaN. */ public int intValue() { + if (this.type == REAL && Double.isNaN(this.doubleValue)) { + throw new IllegalStateException("The integer value is not available because the value of this NSNumber instance is NaN."); + } + return (int) this.longValue; } @@ -280,13 +299,13 @@ public float floatValue() { public String stringValue() { switch (this.type) { case INTEGER: { - return String.valueOf(this.longValue()); + return String.valueOf(this.longValue); } case REAL: { - return String.valueOf(this.doubleValue()); + return String.valueOf(this.doubleValue); } case BOOLEAN: { - return String.valueOf(this.boolValue()); + return String.valueOf(this.boolValue); } default: { throw new IllegalStateException("The NSNumber instance has an invalid type: " + this.type); @@ -339,13 +358,13 @@ public NSNumber clone() { public String toString() { switch (this.type()) { case INTEGER: { - return String.valueOf(this.longValue()); + return String.valueOf(this.longValue); } case REAL: { - return String.valueOf(this.doubleValue()); + return String.valueOf(this.doubleValue); } case BOOLEAN: { - return String.valueOf(this.boolValue()); + return String.valueOf(this.boolValue); } default: { return super.toString(); @@ -359,25 +378,26 @@ void toXML(StringBuilder xml, int level) { switch (this.type()) { case INTEGER: { xml.append(""); - xml.append(this.longValue()); + xml.append(this.longValue); xml.append(""); break; } case REAL: { xml.append(""); - xml.append(this.doubleValue()); + xml.append(Double.isNaN(this.doubleValue) ? "nan" : String.valueOf(this.doubleValue)); xml.append(""); break; } case BOOLEAN: { - if (this.boolValue()) + if (this.boolValue) xml.append(""); else xml.append(""); break; } - default: - break; + default: { + throw new IllegalStateException("The NSNumber instance has an invalid type: " + this.type); + } } } @@ -387,33 +407,34 @@ void toBinary(BinaryPropertyListWriter out) throws IOException { case INTEGER: { if (this.longValue() < 0) { out.write(0x13); - out.writeBytes(this.longValue(), 8); - } else if (this.longValue() <= 0xff) { + out.writeBytes(this.longValue, 8); + } else if (this.longValue <= 0xff) { out.write(0x10); out.writeBytes(this.longValue(), 1); - } else if (this.longValue() <= 0xffff) { + } else if (this.longValue <= 0xffff) { out.write(0x11); out.writeBytes(this.longValue(), 2); - } else if (this.longValue() <= 0xffffffffL) { + } else if (this.longValue <= 0xffffffffL) { out.write(0x12); - out.writeBytes(this.longValue(), 4); + out.writeBytes(this.longValue, 4); } else { out.write(0x13); - out.writeBytes(this.longValue(), 8); + out.writeBytes(this.longValue, 8); } break; } case REAL: { out.write(0x23); - out.writeDouble(this.doubleValue()); + out.writeDouble(this.doubleValue); break; } case BOOLEAN: { - out.write(this.boolValue() ? 0x09 : 0x08); + out.write(this.boolValue ? 0x09 : 0x08); break; } - default: - break; + default: { + throw new IllegalStateException("The NSNumber instance has an invalid type: " + this.type); + } } } @@ -421,7 +442,7 @@ void toBinary(BinaryPropertyListWriter out) throws IOException { protected void toASCII(StringBuilder ascii, int level) { this.indent(ascii, level); if (this.isBoolean()) { - ascii.append(this.boolValue() ? "YES" : "NO"); + ascii.append(this.boolValue ? "YES" : "NO"); } else { ascii.append(this.toString()); } @@ -449,9 +470,11 @@ protected void toASCIIGnuStep(StringBuilder ascii, int level) { } else { ascii.append("<*BN>"); } - } - default: break; + } + default: { + throw new IllegalStateException("The NSNumber instance has an invalid type: " + this.type); + } } }