OSDN Git Service

Merge commit '2458eff3aea04f67893bc824b5cf896fbb767332'
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / json / JsNumber.java
diff --git a/src/main/java/jp/sourceforge/jindolf/json/JsNumber.java b/src/main/java/jp/sourceforge/jindolf/json/JsNumber.java
new file mode 100644 (file)
index 0000000..aa521b1
--- /dev/null
@@ -0,0 +1,325 @@
+/*\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