001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.plugin.column;
017
018import org.opengion.fukurou.util.StringUtil;
019import org.opengion.hayabusa.db.AbstractEditor;
020import org.opengion.hayabusa.db.CellEditor;
021import org.opengion.hayabusa.db.DBColumn;
022import org.opengion.fukurou.util.XHTMLTag;
023
024import java.text.DecimalFormat;
025
026/**
027 * DECIMAL エディターは、カラムのデータをDecimal(10進数、小数)表示する場合に
028 * 使用するクラスです。
029 *
030 * 編集パラメータに与えられた文字列は、java.text.DecimalFormat を使用して
031 * フォーマットされます。
032 * フォーマット変換前に、カンマなどの数値変換時にエラーになる情報を削除しておきます。
033 * 標準のフォーマットは"#,##0.#"です。
034 * default値が設定されていない場合の初期値は0.0にフォーマット処理をしたものです。
035 *
036 * フォーマットルールは、{@link java.text.DecimalFormat} を参照願います。
037 *
038 * 7.4.2.3 (2021/06/09)
039 *  フォーマットは、表示桁数 に応じて決定します。
040 *  ただし、パラメータ指定 が優先されます。
041 *
042 *  カラムの表示に必要な属性は、DBColumn オブジェクト より取り出します。
043 * このクラスは、DBColumn オブジェクト毎に1つ作成されます。
044 *
045 * @og.group データ編集
046 * @og.rev 3.8.8.5 (2007/03/09) 新規作成
047 * @og.rev 5.4.3.6 (2012/01/19) コメント修正
048 *
049 * @version  4.0
050 * @author       Kazuhiko Hasegawa
051 * @since    JDK5.0,
052 */
053public class Editor_DECIMAL extends AbstractEditor {
054        /** このプログラムのVERSION文字列を設定します。   {@value} */
055        private static final String VERSION = "7.4.2.3 (2021/06/09)" ;
056
057        // 7.4.2.3 (2021/06/09) フォーマットは、表示桁数 に応じて決定します(小数6桁以上の精度は受け付けられない)
058        private static final String[] FRMT = new String[] { "#,##0" , "#,##0.0" , "#,##0.00" , "#,##0.000" , "#,##0.0000" , "#,##0.00000" , "#,##0.000000" } ;
059
060        private final int       minFraction;
061        private final DecimalFormat format ;
062        private final String    defValue ;
063
064        /**
065         * デフォルトコンストラクター。
066         * このコンストラクターで、基本オブジェクトを作成します。
067         *
068         */
069        public Editor_DECIMAL() {
070                super();                // 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
071                // 4.3.4.4 (2009/01/01)
072                minFraction = 0;
073                format = null;
074                defValue   = null;
075        }
076
077        /**
078         * DBColumnオブジェクトを指定したprivateコンストラクター。
079         *
080         * @og.rev 5.3.1.0 (2009/12/01) 初期値がnullの場合は、defValueはnullとする。
081         * @og.rev 5.4.0.0 (2011/10/01) RendererParamが読み込まれているバグを修正
082         * @og.rev 7.4.2.3 (2021/06/09) フォーマットは、表示桁数 に応じて決定します(パラメータ指定 が優先)。
083         *
084         * @param       clm     DBColumnオブジェクト
085         */
086        private Editor_DECIMAL( final DBColumn clm ) {
087                super( clm );
088                minFraction = clm.getSizeY();
089
090                final String fm = makeFormat( clm );
091//              String fm = clm.getEditorParam();
092//              if( fm == null || fm.isEmpty() || fm.equals( "_" ) ) {
093//                      fm = "#,##0.#";
094//              }
095                format = new DecimalFormat( fm );
096
097                // 5.3.1.0 (2009/12/01)
098                final String defv = clm.getDefault();
099                if( defv == null || defv.isEmpty() ) {
100                        defValue = "";
101                }
102                else {
103                        double dd = 0.0;
104                        // 6.0.2.5 (2014/10/31) null でないことがわかっている値の冗長な null チェックがあります。
105                        if( !"_".equals( defv ) ) {
106                                dd  = StringUtil.parseDouble( defv );
107                        }
108                        defValue = format.format( dd );
109                }
110
111                // -XXX,XXX,XXX.YYY 形式では、カンマ、小数点、マイナスなどの項目が入ります。
112                final int maxlength = (int)(clm.getSizeX() * 4.0/3.0) + minFraction + 2 ;       // +2 は、マイナスと小数点
113                attributes.set( "maxlength"   ,String.valueOf( maxlength ) );
114                tagBuffer.add( XHTMLTag.inputAttri( attributes ) );
115        }
116
117        /**
118         * フォーマット文字列を返します。
119         *
120         * フォーマットは、表示桁数 に応じて決定します。
121         * ただし、パラメータ指定 が優先されます。
122         *
123         * @og.rev 7.4.2.3 (2021/06/09) フォーマットは、表示桁数 に応じて決定します(パラメータ指定 が優先)。
124         *
125         * @param       clm     DBColumnオブジェクト
126         *
127         * @return      フォーマット文字列
128         */
129        private String makeFormat( final DBColumn clm ) {
130                String fm = clm.getEditorParam();                                                       // パラメータ指定 が優先
131
132                if( fm == null || fm.isEmpty() || fm.equals( "_" ) ) {
133                        fm = "#,##0.##";                                                                                // 初期値:桁を増やしておきます。
134
135                        // 7.4.2.3 (2021/06/09) フォーマットは、表示桁数 に応じて決定します。
136                        final String viewLen = clm.getViewLength();
137                        if( viewLen != null ) {
138                                final int ad = viewLen.indexOf( '.' );
139                                if( ad >= 0 ) {
140                                        final String st = viewLen.substring( ad+1 );            // 小数点以下の桁数取得
141                                        try {
142                                                final int su = Integer.parseInt( st );
143                                                fm = su < FRMT.length ? FRMT[su] : FRMT[FRMT.length-1] ;
144                                        }
145                                        catch( final Throwable th ) {
146                                                final String errMsg = "表示桁数の小数部の変換に失敗しました" + CR
147                                                                                        + "viewLen=" + viewLen
148                                                                                        + CR
149                                                                                        + th.getMessage() ;
150                                                System.err.println( errMsg );
151                                        }
152                                }
153                        }
154                }
155
156                return fm;
157        }
158
159        /**
160         * 各オブジェクトから自分のインスタンスを返します。
161         * 自分自身をキャッシュするのか、新たに作成するのかは、各サブクラスの実装に
162         * まかされます。
163         *
164         * @param       clm     DBColumnオブジェクト
165         *
166         * @return      CellEditorオブジェクト
167         * @og.rtnNotNull
168         */
169        public CellEditor newInstance( final DBColumn clm ) {
170                return new Editor_DECIMAL( clm );
171        }
172
173        /**
174         * データの編集用文字列を返します。
175         *
176         * @param   value 入力値
177         *
178         * @return  データの編集用文字列
179         */
180        @Override
181        public String getValue( final String value ) {
182                return super.getValue( formatValue( value ) );
183        }
184
185        /**
186         * name属性を変えた、データ表示/編集用のHTML文字列を作成します。
187         * テーブル上の name に 行番号を付加して、名前_行番号 で登録するキーを作成し、
188         * リクエスト情報を1つ毎のフィールドで処理できます。
189         *
190         * @param       row   行番号
191         * @param       value 入力値
192         *
193         * @return      データ表示/編集用の文字列
194         */
195        @Override
196        public String getValue( final int row,final String value ) {
197                return super.getValue( row,formatValue( value ) );
198        }
199
200        /**
201         * 内部的なフォーマット変換後の文字列を返します。
202         *
203         * @og.rev 5.3.1.0 (2009/12/01) 値が0の場合は、初期値を適用する。
204         *
205         * @param       value 入力値
206         *
207         * @return      データの表示用文字列
208         */
209        private String formatValue( final String value ) {
210                if( value == null || value.trim().isEmpty() || "0".equals( value ) ) {
211                        return defValue;
212                }
213
214                final double dd = StringUtil.parseDouble( value );
215
216                final String rtn ;
217                synchronized( format ) {
218                        rtn = format.format( dd );
219                }
220
221                return rtn;
222        }
223}