wicketの余計なお世話に絶望した!wicket1.3のナンバーフォーマットのバグ
PropertyModel等でBigDecimalにマッピングしたTextFieldを作って、10000000…と入れてみて下さい。
100,000,000,000,000,000,000,000,000,000
とフォーマットして表示されるようになっているのです。
ちょっと動作を追ってみましたが1.3.0はフォーマットしなかったので、1.3.1からのようです。(1.3.3まで確認)
ReleaseNoteを見てみましたが関係ありそうなのは[WICKET-1254] Binding to a BigDecimal don't honor browser locale - ASF JIRAぐらいでしたが直接関係あるんでしょうか?
この時点でフォーマットしたくない人は困ると思いますが、これ24桁以上入力すると化けるんですよ。
↑の値は↓となります。
99,999,999,999,999,991,433,150,857,216
入力した文字列をBigDecimalConverterでConvertしてるんですが、ソースから抜粋してわかりやすくすると以下のような動作になってました。
BigDecimal hoge = new BigDecimal("100000000000000000000000000000"); System.out.println(hoge); NumberFormat nf = NumberFormat.getInstance(locale); //各localeでNumberFormatを取得して値を文字列にフォーマット String formatString = nf.format(hoge); System.out.println(formatString); try { //同じNumberFormatにフォーマットした文字列を入れるとDoubleになる。 Double parseObject = (Double) nf.parseObject(formatString); System.out.println(parseObject); //DoubleをBigDecimalに再度入れたところで精度問題で化ける System.out.println(new BigDecimal(parseObject.doubleValue())); } catch (ParseException e) { }
出力はこうなります
100000000000000000000000000000 100,000,000,000,000,000,000,000,000,000 1.0E29 99999999999999991433150857216
ということでjavaのNumberFormat自体がParse時にDoubleを返していて、BigDecimalに入れると化けるのでwicketのせいではないような気もします…が!
そもそもwicketが余計な事してるのが悪いと思います。
フォーマットしたければ自分でカスタムコンポーネントを作れるわけですし、wicket側でやってくれるにしてもフォーマットするConverterを設定したコンポーネントの提供等通常は何もしないで、必要なときだけフォーマットしてくれる方法は他にもありそうですし。
IConverterのエラーメッセージを変更する - 凡人プログラマもそうですが、あんまり余計な挙動は入れて欲しくないですね。こういうのはextension等に入れるべきじゃないかなーと。
こんなレアな問題で困っている不運な方の為に対処法も書いておきます
- Application#newConverterLocatorをオーバーライドして、自作ConverterLocatorを作成、BigDecimalのコンバータを置き換える。
- 問題発生するTextFieldでgetConverterを定義してやり、自作Converterを返す。
ちなみに以下の自作Converterは上記バグを修正した版です。TextFieldにgetConverterを定義して問題発生しない事を確認しました。
BigDecimalを生成する部分でnumber.doubleValue()では無く、number.toString()にしました。(すごく対症療法っぽいですが)
本家のバグもここを修正するだけで直ると思います。
return new IConverter() { @Override public Object convertToObject(String value, Locale locale) { Number number = null; try { number = NumberFormat.getInstance(locale).parse(value); } catch (ParseException e) { e.printStackTrace(); } return new BigDecimal(number.toString()); } @Override public String convertToString(Object value, Locale locale) { return NumberFormat.getInstance(locale).format(value); } };
なぜこの日記のエントリが少ないかについて
上記、ナンバーフォーマットのバグについて、最初は気軽にあー、バグってたなー書いとこうとおもってただけなんですよ。
で、エントリ書き終わって気づいたら1時間半経過( ゚д゚)ポカーン
書き始めると
- 最新版で修正されていたか
- いつからバグってたか
ーちょっとバグってるんじゃなくてそもそもこの機能おかしくねぇ??
- 対処法、ConverterLocatorの方でやってたけどgetConverterでもできるんじゃね?
- これdoubleValue()じゃなくてtoString()ならいけるんじゃね?→あ、いけた!
…など。そりゃ時間かかるわ。
アウトプットしてみようと思わなければここまで深く追ったり考えついたりしないわけでとってもいい勉強になってるとは思うんですよ!
でも、いっつもこれじゃあ気軽に合間時間でちょっとエントリ書いとくか♪…ってな気分にはなれないんですよ!子持ちエンジニアがブログ書ける時間なんて妻子が寝静まった後だけなんですよ!(これはちょっと少数派かつ仕方ないですが)
そんで、結局週1程度。
毎日のように濃いエントリを書いてる方々はいったいどんだけ凄いんだ…
ええ、お気づきかとは思いますが、このエントリはほぼ愚痴です。お耳汚し失礼しました。