--- /dev/null
+/*\r
+ * JSON number value\r
+ *\r
+ * Copyright(c) 2009 olyutorskii\r
+ * $Id: JsNumber.java 915 2009-11-24 11:19:48Z olyutorskii $\r
+ */\r
+\r
+package jp.sourceforge.jindolf.json;\r
+\r
+import java.io.IOException;\r
+import java.math.BigDecimal;\r
+import java.math.BigInteger;\r
+\r
+/**\r
+ * JSON 数値Value。\r
+ * 10を基数としたjava.math.BigDecimalを実装ベースとする\r
+ * IEEE754浮動小数ではない。\r
+ */\r
+public class JsNumber\r
+ extends AbstractJsValue\r
+ implements Comparable<JsNumber> {\r
+\r
+ /**\r
+ * 文字ストリームから符号付きの数字並びを読み込む。\r
+ * +符号は読み飛ばされる。\r
+ * 冒頭の連続する0はそのまま読まれる。\r
+ * @param reader 文字入力\r
+ * @param app 出力先\r
+ * @param allowZeroTrail 冒頭の2つ以上連続するゼロを許すならtrue\r
+ * @return 引数と同じ出力先\r
+ * @throws IOException 入出力エラー\r
+ * @throws JsParseException パースエラーもしくは入力終了\r
+ */\r
+ private static Appendable appendDigitText(JsonReader reader,\r
+ Appendable app,\r
+ boolean allowZeroTrail)\r
+ throws IOException, JsParseException{\r
+ int chData;\r
+\r
+ chData = reader.read();\r
+ if (chData < '\u0000') throw new JsParseException();\r
+ else if(chData == '-') app.append('-');\r
+ else if(chData != '+') reader.unread(chData);\r
+\r
+ boolean hasAppended = false;\r
+ boolean zeroStarted = false;\r
+ for(;;){\r
+ chData = reader.read();\r
+ if(chData < '\u0000') throw new JsParseException();\r
+\r
+ if('0' <= chData && chData <= '9'){\r
+ app.append((char)chData);\r
+\r
+ if(zeroStarted && ! allowZeroTrail){\r
+ throw new JsParseException();\r
+ }\r
+\r
+ if(chData == '0' && ! hasAppended ){\r
+ zeroStarted = true;\r
+ }\r
+\r
+ hasAppended = true;\r
+ }else{\r
+ if( ! hasAppended ) throw new JsParseException();\r
+ reader.unread(chData);\r
+ break;\r
+ }\r
+ }\r
+\r
+ return app;\r
+ }\r
+\r
+ /**\r
+ * 文字ストリームから符号付きの数字並びを読み込む。\r
+ * +符号はパースエラーとなる。\r
+ * @param reader 文字入力\r
+ * @param app 出力先\r
+ * @return 引数と同じ出力先\r
+ * @throws IOException 入出力エラー\r
+ * @throws JsParseException パースエラーもしくは入力終了\r
+ */\r
+ private static Appendable appendIntegerPart(JsonReader reader,\r
+ Appendable app )\r
+ throws IOException, JsParseException{\r
+ int chData;\r
+\r
+ chData = reader.read();\r
+ if(chData < '\u0000') throw new JsParseException();\r
+ if(chData == '+') throw new JsParseException();\r
+ reader.unread(chData);\r
+\r
+ appendDigitText(reader, app, false);\r
+\r
+ return app;\r
+ }\r
+\r
+ /**\r
+ * 文字ストリームから「.」で始まる小数部を読み込む。\r
+ * 小数部がなければなにもせずに戻る。\r
+ * @param reader 文字入力\r
+ * @param app 出力先\r
+ * @return 引数と同じ出力先\r
+ * @throws IOException 入出力エラー\r
+ * @throws JsParseException パースエラーもしくは入力終了\r
+ */\r
+ private static Appendable appendFractionPart(JsonReader reader,\r
+ Appendable app )\r
+ throws IOException, JsParseException{\r
+ int chData;\r
+\r
+ chData = reader.read();\r
+ if(chData < '\u0000') throw new JsParseException();\r
+ if(chData != '.'){\r
+ reader.unread(chData);\r
+ return app;\r
+ }\r
+\r
+ app.append(".");\r
+\r
+ boolean hasAppended = false;\r
+ for(;;){\r
+ chData = reader.read();\r
+ if(chData < '\u0000') throw new JsParseException();\r
+\r
+ if('0' <= chData && chData <= '9'){\r
+ app.append((char)chData);\r
+ hasAppended = true;\r
+ }else{\r
+ if( ! hasAppended ) throw new JsParseException();\r
+ reader.unread(chData);\r
+ break;\r
+ }\r
+ }\r
+\r
+ return app;\r
+ }\r
+\r
+ /**\r
+ * 文字ストリームから「e」もしくは「E」で始まる指数部を読み込む。\r
+ * 指数部がなければなにもせずに戻る。\r
+ * @param reader 文字入力\r
+ * @param app 出力先\r
+ * @return 引数と同じ出力先\r
+ * @throws IOException 入出力エラー\r
+ * @throws JsParseException パースエラーもしくは入力終了\r
+ */\r
+ private static Appendable appendExpPart(JsonReader reader,\r
+ Appendable app )\r
+ throws IOException, JsParseException{\r
+ int chData;\r
+\r
+ chData = reader.read();\r
+ if(chData < '\u0000') throw new JsParseException();\r
+ if(chData != 'e' && chData != 'E'){\r
+ reader.unread(chData);\r
+ return app;\r
+ }\r
+\r
+ app.append('E');\r
+\r
+ appendDigitText(reader, app, true);\r
+\r
+ return app;\r
+ }\r
+\r
+ /**\r
+ * 文字ストリームからJSON数値Valueを読み込む。\r
+ * @param reader 文字入力\r
+ * @return 数値Value\r
+ * @throws IOException 入力エラー\r
+ * @throws JsParseException パースエラーもしくは入力終了\r
+ */\r
+ static JsNumber parseNumber(JsonReader reader)\r
+ throws IOException, JsParseException{\r
+ Json.skipWhiteSpace(reader);\r
+\r
+ StringBuilder numText = new StringBuilder();\r
+ appendIntegerPart (reader, numText);\r
+ appendFractionPart(reader, numText);\r
+ appendExpPart (reader, numText);\r
+ JsNumber result = new JsNumber(numText);\r
+\r
+ return result;\r
+ }\r
+\r
+ private BigDecimal decimal;\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * @param val 初期数値\r
+ */\r
+ public JsNumber(long val){\r
+ this(BigDecimal.valueOf(val));\r
+ return;\r
+ }\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * @param val 初期数値\r
+ */\r
+ public JsNumber(double val){\r
+ this(BigDecimal.valueOf(val));\r
+ return;\r
+ }\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * @param val 初期数値\r
+ */\r
+ public JsNumber(BigInteger val){\r
+ this(new BigDecimal(val));\r
+ return;\r
+ }\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * 書式はjava.math.BigDecinal#BigDecimal(String)に準ずる。\r
+ * @param val 初期数値の文字列表記\r
+ * @throws NumberFormatException 不正な数値表記\r
+ */\r
+ public JsNumber(CharSequence val) throws NumberFormatException{\r
+ this(new BigDecimal(val.toString()));\r
+ return;\r
+ }\r
+\r
+ /**\r
+ * コンストラクタ。\r
+ * @param val 初期数値\r
+ * @throws NullPointerException 引数がnull\r
+ */\r
+ public JsNumber(BigDecimal val) throws NullPointerException{\r
+ super();\r
+ if(val == null) throw new NullPointerException();\r
+ this.decimal = val;\r
+ return;\r
+ }\r
+\r
+ /**\r
+ * BigDecimal型の数値を返す。\r
+ * @return BigDecimal型数値\r
+ */\r
+ public BigDecimal getBigDecimal(){\r
+ return this.decimal;\r
+ }\r
+\r
+ /**\r
+ * int型の数値を返す。\r
+ * @return int型数値\r
+ */\r
+ public int intValue(){\r
+ return this.decimal.intValue();\r
+ }\r
+\r
+ /**\r
+ * long型の数値を返す。\r
+ * @return long型数値\r
+ */\r
+ public long longValue(){\r
+ return this.decimal.longValue();\r
+ }\r
+\r
+ /**\r
+ * float型の数値を返す。\r
+ * @return float型数値\r
+ */\r
+ public float floatValue(){\r
+ return this.decimal.floatValue();\r
+ }\r
+\r
+ /**\r
+ * double型の数値を返す。\r
+ * @return double型数値\r
+ */\r
+ public double doubleValue(){\r
+ return this.decimal.doubleValue();\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ * @return {@inheritDoc}\r
+ */\r
+ @Override\r
+ public int hashCode(){\r
+ return this.decimal.hashCode();\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ * 「1.2」と「0.12E+1」など、スケールの一致しない値は異なる値と見なされる。\r
+ * @param obj {@inheritDoc}\r
+ * @return {@inheritDoc}\r
+ */\r
+ @Override\r
+ public boolean equals(Object obj){\r
+ if(obj == null) return false;\r
+ if(this == obj) return true;\r
+ if( ! (obj instanceof JsNumber) ) return false;\r
+ JsNumber number = (JsNumber) obj;\r
+ return this.decimal.equals(number.decimal);\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ * 「1.2」と「0.12E+1」など、スケールが異なっても値が同じであれば\r
+ * 等しいと見なされる。\r
+ * @param value {@inheritDoc}\r
+ * @return {@inheritDoc}\r
+ */\r
+ public int compareTo(JsNumber value){\r
+ if(this == value) return 0;\r
+ return this.decimal.compareTo(value.decimal);\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ * java.math.BigDecimal#toString()に準ずる。\r
+ * ※ JSON規格のパーサで解釈できるはず。\r
+ * @return {@inheritDoc}\r
+ */\r
+ @Override\r
+ public String toString(){\r
+ return this.decimal.toString();\r
+ }\r
+\r
+}\r