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.hayabusa.db;
017
018import java.util.Calendar;
019
020import org.opengion.hayabusa.common.HybsSystem;
021import org.opengion.fukurou.util.LogWriter;
022
023/**
024 * データのコード情報を取り扱うクラスです。
025 *
026 * 開始、終了、ステップの情報から、HTMLのメニューやリストを作成するための オプション
027 * タグを作成したり、与えられたキーをもとに、チェック済みのオプションタグを作成したりします。
028 * 
029 * ここでは、時間(時:分)の自動生成を行います。パラメータで、開始、終了、ステップ、開始前設定値、終了後設定値
030 * を指定できます。
031 * キーは、4文字の HHMM 形式で与えられます。ラベルは、HH:MM になります。
032 * ステップは、分単位です。つまり、1時間の場合は、"60" と指定します。"0100"ではありません。
033 * 開始前設定値、終了後設定値はそれぞれ、開始の前と終了の後ろに特別に値を設定できます。
034 *
035 * 開始、または、終了に、現在時刻からの相対値を指定する事ができます。
036 * H1 ~ HXXX とすれば、現在時刻の時に数字部分を+-します。分は0に初期化されます。
037 *
038 * パラメータの初期値は、開始(0700)、終了(1900)、ステップ(30)、開始前設定値(null)、終了後設定値(null) です。
039 * 
040 * 例:0800,2000,30  → 0800,0830,0900,0930,1000,・・・1900,1930,2000 のプルダウン
041 * 例:0800,2000,30,0000:△,2400:▽  → 0000,0800,0830,0900,0930,1000,・・・1900,1930,2000,2400 のプルダウン
042 *
043 * @og.group 選択データ制御
044 * @og.rev 5.6.1.1 (2013/02/08) 新規追加
045 *
046 * @version  4.0
047 * @author   Kazuhiko Hasegawa
048 * @since    JDK5.0,
049 */
050// public class Selection_HM implements Selection {
051public class Selection_HM extends Selection_NULL {
052        private final String   CACHE ;
053        private final String   ST_ED_STEP ;
054
055        private final long      maxCacheTime ;          // キャッシュの破棄タイミングを計るための最大有効時間
056
057        /**
058         * コンストラクター
059         *
060         * 引数は、開始、終了、ステップ、開始前設定値、終了後設定値 です。
061         * パラメータの初期値は、開始(0700)、終了(1900)、ステップ(30)、開始前設定値(null)、終了後設定値(null) です。
062         *
063         * @param       editPrm 開始、終了、ステップ、開始前設定値、終了後設定値 を表す引数(例:0800,2000,30)
064         */
065        public Selection_HM( final String editPrm ) {
066        //      if( param.length < 3 ) {
067        //              String errMsg = "引数は、開始、終了、ステップ、[開始前設定値]、[終了後設定値]です。最低でも3個必要です。";
068        //              throw new IllegalArgumentException( errMsg );
069        //      }
070
071                String[] param = (editPrm == null) ? new String[0] : editPrm.split( "," ) ;
072
073                String start = (param.length > 0) ? param[0].trim() : "0700" ;
074                String end   = (param.length > 1) ? param[1].trim() : "1900" ;
075                String step  = (param.length > 2) ? param[2].trim() : "30" ;
076
077                String stOp  = (param.length > 3) ? param[3].trim() : null ;
078                String enOp  = (param.length > 4) ? param[4].trim() : null ;
079
080                int stepTime    = Integer.parseInt( step );
081                if( stepTime == 0 ) {
082                        String errMsg = "ステップ に 0 は指定できません。無限ループします。";
083                        throw new IllegalArgumentException( errMsg );
084                }
085
086                Calendar cal = Calendar.getInstance();
087                calendarCalc( cal, start );
088
089                Calendar endCal = Calendar.getInstance();
090                calendarCalc( endCal, end );
091
092                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
093
094                addOption( buf,stOp );          // 開始前設定値 の追加
095
096                // cal.before( endCal ) では、同一時刻の場合に false になる為、ここの判定では使えません。
097                // sign を掛け算しているのは、逆順対応
098                int sign = ( stepTime > 0 ) ? 1 : -1 ;  // ステップの符号。
099                while( endCal.compareTo( cal ) * sign >= 0 ) {
100                        int hh = cal.get( Calendar.HOUR_OF_DAY );       // 時
101                        int mm = cal.get( Calendar.MINUTE );            // 分
102
103                        String hVal = (hh < 10) ? "0" + hh : String.valueOf( hh ) ;
104                        String mVal = (mm < 10) ? "0" + mm : String.valueOf( mm ) ;
105
106                        buf.append( "<option value=\"" ).append( hVal ).append( mVal ).append( "\"" );
107                        buf.append( ">" ).append( hVal ).append( ":" ).append( mVal ).append( "</option>" );
108
109                        cal.add( Calendar.MINUTE,stepTime );            // 時刻に加えるのは、分
110                }
111
112                addOption( buf,enOp );          // 終了後設定値 の追加
113
114                CACHE = buf.toString();
115                ST_ED_STEP = "Start=" + start + " , End=" + end + " , Step=" + step + " , StartBefore=" + stOp + " , EndAfter=" + enOp ;
116
117
118                // キャシュの有効期間を求めるための時刻を作成します。キャッシュは、時間指定があれば、同一時間内のみ有効です。
119                Calendar now   = Calendar.getInstance();
120                boolean nowBase = start.charAt(0) == 'H' || end.charAt(0) == 'H' ;
121                if( nowBase ) {
122                        now.add( Calendar.HOUR   , 1 );                         // 1時間進めます。
123                        now.set( Calendar.MINUTE , 0 );                         // 分、秒 をリセットします。
124                        now.set( Calendar.SECOND , 0 );
125                }
126                else {
127                        now.add( Calendar.YEAR   , 1 );                         // 1年間進めます。(現在時刻をベースに指定ない為、無制限キャッシュの意味)
128                }
129
130                maxCacheTime = now.getTimeInMillis() ;
131        }
132
133        /**
134         * 開始、または 終了の文字列から、カレンダオブジェクトを作成します。
135         * 基準となる日付に計算した結果を反映させます。
136         *
137         * prmB は、日付についての加減算処理を行うためのコマンドを指定します。
138         * ・数字       :HHMM 形式の時分です。
139         * ・H1 ~ HXXX :現在時刻に数字部分を+-します。分は0に初期化されます。
140         *
141         * @param   cal     基準となる日付(Calendarオブジェクト)
142         * @param   prmB        処理コマンド
143         *
144         */
145        private void calendarCalc( final Calendar cal,final String prmB ) {
146                boolean nowBase = prmB.charAt(0) == 'H' ;
147
148                if( nowBase ) {
149                        int hour        = Integer.parseInt( prmB.substring( 1 ) );
150                        cal.add( Calendar.HOUR_OF_DAY,hour );
151                        cal.set( Calendar.MINUTE     ,0 );
152                        cal.set( Calendar.SECOND     ,0 );
153                }
154                else {
155                        int hour        = Integer.parseInt( prmB.substring( 0,2 ) );
156                        int minute      = Integer.parseInt( prmB.substring( 2,4 ) );
157                        cal.set( Calendar.HOUR_OF_DAY,hour );
158                        cal.set( Calendar.MINUTE     ,minute );
159                        cal.set( Calendar.SECOND     ,0 );
160                }
161        }
162
163        /**
164         * 開始前設定値、または 終了後設定値の文字列から、オプション文字列を合成します。
165         * このオプションは、引数のStringBuilder に、オプションタグを追加して返します。
166         * optVal が null の場合は、処理しません。
167         *
168         * @param   buf     文字列連結する StringBuilderオブジェクト。このオブジェクトに追加します。
169         * @param   optVal  開始前設定値、または 終了後設定値 文字列("0000:△" 形式)
170         *
171         */
172        private void addOption( final StringBuilder buf,final String optVal ) {
173                if( optVal != null ) {
174                        int adrs = optVal.indexOf( ':' );
175                        if( adrs > 0 ) {
176                                buf.append( "<option value=\"" ).append( optVal.substring( 0,adrs ) ).append( "\"" );
177                                buf.append( ">" ).append( optVal.substring( adrs+1 ) ).append( "</option>" );
178                        }
179                        // 開始前設定値 が存在する場合、"0000:△" 形式必須
180                        else {
181                                String errMsg = "引数は、0000:△ 形式です。";
182                                throw new IllegalArgumentException( errMsg );
183                        }
184                }
185        }
186
187        /**
188         * 初期値が選択済みの 選択肢(オプション)を返します。
189         * このオプションは、引数の値を初期値とするオプションタグを返します。
190         * このメソッドでは、ラベル(短)が設定されている場合でも、これを使用せずに必ずラベル(長)を使用します。
191         *
192         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
193         *
194         * @param   selectValue  選択されている値
195         * @param   seqFlag  シーケンスアクセス機能 [true:ON/false:OFF]
196         *
197         * @return  オプションタグ
198         * @see     #getOption( String, boolean, boolean )
199         */
200//      public String getOption( final String selectValue,final boolean seqFlag ) {
201//              return getOption( selectValue, seqFlag, false );
202//      }
203
204        /**
205         * 初期値が選択済みの 選択肢(オプション)を返します。
206         * このオプションは、引数の値を初期値とするオプションタグを返します。
207         * このメソッドでは、引数のuseShortLabelがtrueに指定された場合に、ラベル(短)をベースとした
208         * ツールチップ表示を行います。
209         *
210         * @param   selectValue  選択されている値
211         * @param   seqFlag  シーケンスアクセス機能 [true:ON/false:OFF]
212         * @param   useShortLabel ラベル(短)をベースとしたオプション表示を行うかどうか。(未使用)
213         *
214         * @return  オプションタグ
215         * @see     #getOption( String, boolean )
216         */
217        @Override
218        public String getOption( final String selectValue,final boolean seqFlag, final boolean useShortLabel ) {
219                // マッチするアドレスを探す。
220                int selected = CACHE.indexOf( "\"" + selectValue + "\"" );
221
222                if( selected < 0 ) {
223                        if( selectValue != null && selectValue.length() > 0 ) {
224                                String errMsg = "時分範囲に存在しない値が指定されました。"
225                                                        + " value=[" + selectValue + "]"
226                                                        + HybsSystem.CR + ST_ED_STEP ;
227                                LogWriter.log( errMsg );
228                        }
229                        return CACHE;
230                }
231                else {
232                        // "時分" 文字列の位置が、selected なので、時分の文字数+2までが、前半部分になる。(時分の文字数は4固定のはず)
233                        int indx = selected + selectValue.length() + 2 ;
234
235                        StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
236                        // 3.6.0.6 (2004/10/22) シーケンスアクセス機能を指定する seqFlag を導入
237                        if( seqFlag ) {
238                                buf.append( "<option value=\"" ).append( selectValue ).append( "\"" );
239                        }
240                        else {
241                                buf.append( CACHE.substring( 0,indx ) );
242                        }
243                        buf.append( " selected=\"selected\"" );
244                        buf.append( CACHE.substring( indx ) );
245                        return buf.toString() ;
246                }
247        }
248
249        /**
250         * 初期値が選択済みの 選択肢(オプション)を返します。
251         * このオプションは、引数の値を初期値とするオプションタグを返します。
252         * ※ このクラスでは実装されていません。
253         *
254         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
255         *
256         * @param   name         ラジオの name
257         * @param   selectValue  選択されている値
258         * @param   useLabel     ラベル表示の有無 [true:有/false:無]
259         *
260         * @return  オプションタグ
261         */
262//      public String getRadio( final String name,final String selectValue,final boolean useLabel ) {
263//              String errMsg = "このクラスでは実装されていません。";
264//              throw new UnsupportedOperationException( errMsg );
265//      }
266
267        /**
268         * 初期値が選択済みの 選択肢(オプション)を返します。
269         * このオプションは、引数の値を初期値とするオプションタグを返します。
270         * ※ このクラスでは実装されていません。
271         *
272         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
273         *
274         * @param   selectValue  選択されている値
275         *
276         * @return  オプションタグ
277         */
278//      public String getRadioLabel( final String selectValue ) {
279//              String errMsg = "このクラスでは実装されていません。";
280//              throw new UnsupportedOperationException( errMsg );
281//      }
282
283        /**
284         * 選択肢(value)に対するラベルを返します。
285         * 選択肢(value)が、存在しなかった場合は、選択肢そのものを返します。
286         * getValueLabel( XX ) は、getValueLabel( XX,false ) と同じです。
287         *
288         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
289         *
290         * @param   selectValue 選択肢の値
291         *
292         * @return  選択肢のラベル
293         * @see     #getValueLabel( String,boolean )
294         */
295//      public String getValueLabel( final String selectValue ) {
296//              return getValueLabel( selectValue,false );
297//      }
298
299        /**
300         * 選択肢(value)に対するラベルを返します。
301         * 選択肢(value)が、存在しなかった場合は、選択肢そのものを返します。
302         * このメソッドでは、短縮ラベルを返すかどうかを指定するフラグを指定します。
303         * getValueLabel( XX,false ) は、getValueLabel( XX ) と同じです。
304         *
305         * @param       selectValue     選択肢の値
306         * @param       flag    短縮ラベルを [true:使用する/false:しない](未使用)
307         *
308         * @return  選択肢のラベル
309         * @see     #getValueLabel( String )
310         */
311        @Override
312        public String getValueLabel( final String selectValue,final boolean flag ) {
313                // あろうがなかろうが、選択肢そのものを返します。
314                return selectValue;
315        }
316
317        /**
318         * マルチ・キーセレクトを使用するかどうかを返します。
319         * true:使用する。false:使用しない です。
320         * ただし、実際に使用するかどうかは、HTML出力時に決めることが出来ます。
321         * ここでは、USE_MULTI_KEY_SELECT が true で、USE_SIZE(=20)以上の場合に
322         * true を返します。
323         *
324         * @return  選択リストで、マルチ・キーセレクトを使用するかどうか(true:使用する)(false固定)
325         */
326        @Override
327        public boolean useMultiSelect() {
328                return true;
329        }
330
331        /**
332         * オブジェクトのキャッシュが時間切れかどうかを返します。
333         * キャッシュが時間切れ(無効)であれば、true を、有効であれば、
334         * false を返します。
335         *
336         * @return  キャッシュが時間切れなら true
337         */
338        @Override
339        public boolean isTimeOver() {
340                return System.currentTimeMillis() > maxCacheTime ;
341        }
342}