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.view;
017
018import java.util.Calendar;
019
020import org.opengion.hayabusa.common.HybsSystem;
021import org.opengion.fukurou.util.StringUtil;
022// import org.opengion.fukurou.util.XHTMLTag;
023import org.opengion.hayabusa.html.AbstractViewForm;
024import org.opengion.hayabusa.html.ViewCalendarParam;
025import static org.opengion.fukurou.system.HybsConst.BR;         // 6.1.0.0 (2014/12/26) refactoring
026
027/**
028 * 1ヶ月分のカレンダー形式で、検索結果を表示する、カレンダー表示クラスです。
029 *
030 * AbstractViewForm により、setter/getterメソッドのデフォルト実装を提供しています。
031 * 各HTMLのタグに必要な setter/getter メソッドのみ、追加定義しています。
032 *
033 * AbstractViewForm を継承している為、ロケールに応じたラベルを出力させる事が出来ます。
034 *
035 * @og.group 画面表示
036 *
037 * @version     4.0
038 * @author      Kazuhiko Hasegawa
039 * @since       JDK5.0,
040 */
041public class ViewForm_HTMLCalendar extends AbstractViewForm {
042        /** このプログラムのVERSION文字列を設定します。 {@value} */
043        private static final String VERSION = "8.5.3.0 (2023/09/08)" ;
044
045        private static final String[] WEEK_JA = { "日" ,"月" ,"火" ,"水" ,"木" ,"金" ,"土" ,"日" ,"月" };        // 6.4.1.1 (2016/01/16) WEEK_ja → WEEK_JA refactoring
046        private static final String[] WEEK_EN = { "SUN","MON","TUE","WED","THU","FRI","SAT","SUN","MON" };      // 6.4.1.1 (2016/01/16) WEEK_en → WEEK_EN refactoring
047        private static final String[] WEEK_MK = { "SUN","",   "",   "",   "",   "",   "SAT","SUN",""    };
048
049        private String[] week;
050        private String[] viewKeys;                                                              // カレンダを指定するキー配列       // 3.6.0.0 (2004/09/17)
051        private String   ymKey;                                                                 // 年月のカラム名
052        private String   dayKey;                                                                // 休日(0:通常、1:休日)のカラム名
053        private String   valKey;                                                                // カレンダに表示する値のカラム名
054        private boolean  valueBrFlag;                                                   // 日付けと値の間に、BRタグを入れるかどうか
055        private int              firstWeek;                                                             // 曜日の開始(0:日曜 1:月曜)
056        private int              columnSize;                                                    // カレンダの横方向の繰り返し数      // 3.6.0.0 (2004/09/17)
057
058        /**
059         * デフォルトコンストラクター
060         *
061         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
062         */
063        public ViewForm_HTMLCalendar() { super(); }             // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
064
065        /**
066         * DBTableModel から HTML文字列を作成して返します。
067         * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
068         * 表示残りデータが pageSize 以下の場合は、残りのデータをすべて出力します。
069         *
070         * @og.rev 3.5.2.1 (2003/10/27) 属性値の指定のシングルクオートをダブルクオートに変更。
071         * @og.rev 3.6.0.0 (2004/09/17) 複数カレンダに対応
072         * @og.rev 3.6.0.5 (2004/10/18) calenderParam の viewKeys バグ対応。
073         * @og.rev 3.7.0.1 (2005/01/31) 全件チェックコントロール処理変更
074         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
075         * @og.rev 5.10.7.1 (2019/01/18) 1件の場合でもチェックボックスを表示
076         * @og.rev 8.2.0.2 (2022/06/24) HTML5廃止対応
077         *
078         * @param       startNo         表示開始位置
079         * @param       pageSize        表示件数
080         *
081         * @return      DBTableModelから作成された HTML文字列
082         * @og.rtnNotNull
083         */
084        public String create( final int startNo, final int pageSize ) {
085                if( getRowCount() == 0 ) { return ""; }                         // 暫定処置
086
087                paramInit();
088
089                final int lastNo = getLastNo( startNo, pageSize );
090
091                final StringBuilder out = new StringBuilder( BUFFER_LARGE )
092                        .append( "<table><tr>" ).append( CR );
093
094//              final boolean onlyOne = ( lastNo - startNo ) == 1 ;     // 互換性のため、1件の処理のみ変更
095                final boolean onlyOne = lastNo - startNo == 1 ;         // 互換性のため、1件の処理のみ変更             // 6.9.7.0 (2018/05/14) PMD Useless parentheses.
096
097                // 3.7.0.1 (2005/01/31) 全件チェックコントロール処理変更
098                if( ! onlyOne && isUseCheckControl() && "checkbox".equals( getSelectedType() ) ) {
099                        out.append( "<tr><td>" ).append( getAllCheckControl() ).append( "</td></tr>");
100                }
101
102                for( int row=startNo; row<lastNo; row++ ) {
103//                      out.append( "<td valign=\"top\">" );
104                        out.append( "<td style=\"vertical-align:top;\">" );                                     // 8.2.0.2 (2022/06/24) Modify
105
106                        if( isWritable( row ) ) {
107                                // 5.10.7.1 (2019/01/18) 1件でもチェックボックス表示に変更
108        //                      if( onlyOne ) {
109        //                              out.append( XHTMLTag.hidden( HybsSystem.ROW_SEL_KEY,String.valueOf( row ) ) )
110        //                                      .append( CR );
111        //                      }
112        //                      else {
113                                        out.append( "<input type=\"" ).append( getSelectedType() )
114                                                .append( "\" name=\"" ).append( HybsSystem.ROW_SEL_KEY ).append( '"' );
115                                        if( isChecked( row ) ) { out.append( " checked=\"checked\"" ); }
116//                                      out.append( " value=\"" ).append( row ).append( "\" />" );
117                                        out.append( " value=\"" ).append( row ).append( "\" >" );       // 7.0.1.0 (2018/10/15)
118        //                      }
119                        }
120
121                        // 3.6.0.5 (2004/10/18)
122                        // 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop
123                        for( final String vkey : viewKeys ) {
124                                if( "_".equals( vkey ) ) { continue; }
125                                final int newCol = getColumnNo( vkey );
126//                      for( int col=0; col<viewKeys.length; col++ ) {
127//                              if( "_".equals( viewKeys[col] ) ) { continue; }
128//                              final int newCol = getColumnNo( viewKeys[col] );
129                                out.append("<span id=\"label\">")
130                                        .append( getColumnLabel( newCol ) )
131                                        .append( ":</span>" )
132                                        .append( getValueLabel(row,newCol) )
133                                        .append( "&nbsp;" );
134                        }
135                        out.append( BR )
136                                .append( makeCalendarData( row ) )
137                                .append( "</td>" );
138                        if( (row+1) % columnSize == 0 ) {
139                                out.append( "</tr><tr>" ).append( CR );
140                        }
141                }
142                out.append( "</tr></table>" ).append( CR );
143
144                return out.toString();
145        }
146
147        /**
148         * DBTableModel の指定の行番号より、カレンダのHTML文字列を作成して返します。
149         *
150         * @og.rev 3.6.0.0 (2004/09/17) create( int startNo, int pageSize ) のロジックを移動
151         * @og.rev 3.6.0.5 (2004/10/18) 印刷時の罫線出力関連機能の追加。id 属性を出力します。
152         * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"/>" 止めを、">" に変更します)。
153         * @og.rev 8.2.0.2 (2022/06/24) HTML5廃止対応
154         * @og.rev 8.5.3.0 (2023/09/08) HTML5廃止対応
155         *
156         * @param       row     指定の行番号
157         * @return      指定の行番号のカレンダのHTML文字列
158         * @og.rtnNotNull
159         */
160        private String makeCalendarData( final int row ) {
161                final String yyyymm = getValue( row,getColumnNo( ymKey ));
162                final Calendar currentCal = getCalendar( yyyymm );
163
164                final StringBuilder out = new StringBuilder( BUFFER_LARGE )
165//                      .append( "<table id=\"viewCalendar\" border=\"0\" cellpadding=\"1\" cellspacing=\"2\">" )
166//                      .append( "<table id=\"viewCalendar\" border=\"0\" cellpadding=\"1\" >" )        // 8.2.0.2 (2022/06/24) Modify
167                        .append( "<table id=\"viewCalendar\" >" )       // 8.5.3.0 (2023/09/08) Modify
168                        .append( CR )                                                                           // 3.6.0.5 (2004/10/18)
169                        .append( " <tr><td class=\"titleStyle\" colspan=\"7\">" )
170                        .append( getValueLabel( row,getColumnNo( ymKey )) )
171                        .append( "</td></tr>" ).append( CR );
172
173                // 今月の最終日
174                final int daysInMonth = currentCal.getActualMaximum(Calendar.DAY_OF_MONTH);
175                // カレンダーの週の初めを設定する。月曜開始のカレンダーを作成するときに利用
176                currentCal.setFirstDayOfWeek( Calendar.SUNDAY + firstWeek );
177
178                // 週を表す番号 0~6 の間
179                int culDay = ( currentCal.get(Calendar.DAY_OF_WEEK)
180                                                + ( 7 - currentCal.getFirstDayOfWeek() ) ) % 7 ;
181
182                // 曜日欄を記述します。
183                out.append(" <tr>").append( CR );
184                for( int i=0; i<7; i++ ) {
185//                      out.append("  <td width=\"14%\" class=\"dayHead" )
186                        out.append("  <td class=\"dayHead" )                                                            // 8.2.0.2 (2022/06/24) Modify
187                                .append( WEEK_MK[i+firstWeek] ).append( "\">" )
188                                .append( week[i+firstWeek] ).append( "</td>" )
189                                .append( CR );
190                }
191                out.append(" </tr>").append( CR )
192                        .append(" <tr>").append( CR );                                  // 6.1.0.0 (2014/12/26) refactoring
193
194                // 第一週の日付欄の空きスペースの計算
195                if( culDay != 0 ) {
196                        out.append("  <td colspan=\"" ).append( culDay ).append( "\">&nbsp;</td>" ).append( CR );
197                }
198
199                // 日付欄を埋めます。
200//              final String BR = valueBrFlag ? "<br />" : "" ;
201                final String BR = valueBrFlag ? "<br>" : "" ;                                                   // 7.0.1.0 (2018/10/15)
202                for( int day=1; day <= daysInMonth; day++ ) {
203                        final int    daycol = getColumnNo( dayKey + day );                                      // 動的日付けカラム番号取得
204                        final String daylbl = getValueLabel( row,daycol,day );                          // ローカルの日付けラベルを求めるメソッド
205                        // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
206                        final String vallbl = valKey == null
207                                                        ? ""
208                                                        : ( BR + getValueLabel( row,getColumnNo( valKey + day ) ) );    // 動的値カラム番号取得
209
210                        if( "1".equals( getValue( row,daycol ))) {
211                                out.append("  <td class=\"holyday\">" );
212                        } else {
213                                out.append("  <td class=\"day" )
214                                        .append( WEEK_MK[culDay+firstWeek] )
215                                        .append("\">");
216                        }
217                        out.append( daylbl )
218                                .append( vallbl )
219                                .append("</td>")
220                                .append( CR );
221
222                        // 週の終わりで、行を折り返す。
223                        if( culDay == 6 ) {
224                                out.append(" </tr>" ).append( CR )
225                                        .append( " <tr>").append( CR );                 // 6.1.0.0 (2014/12/26) refactoring
226                                culDay = 0;
227                        } else {
228                                culDay++;
229                        }
230                }
231
232                // 最終週の日付欄の空きスペースの計算
233                if( 7 != culDay ) {                             // 6.4.2.1 (2016/02/05) PMD refactoring. Useless parentheses.
234                        out.append( "  <td colspan=\"" ).append( 7-culDay ).append( "\">&nbsp;</td>" );
235                }
236                out.append( CR )
237                        .append( " </tr>" ).append( CR )
238                        .append( "</table>" ).append( CR );
239
240                return out.toString();
241        }
242
243        /**
244         * このビーに対する特別な初期化を行う。
245         *
246         * @og.rev 3.5.4.0 (2003/11/25) TableFormatter クラスを使用するように変更。
247         * @og.rev 3.5.5.9 (2004/06/07) ヘッダーの日付け表示に Locale を加味できるように変更
248         * @og.rev 3.6.0.0 (2004/09/17) viewKeys , columnSize 属性の追加
249         *
250         */
251        private void paramInit() {
252
253                final String viewKeysCd         = getParam( ViewCalendarParam.VIEW_KEYS                 ,ViewCalendarParam.VIEW_VALUES                  );      // 3.6.0.0 (2004/09/17)
254                ymKey                                           = getParam( ViewCalendarParam.YM_KEY                    ,ViewCalendarParam.YM_VALUE                             );
255                dayKey                                          = getParam( ViewCalendarParam.DAY_KEY                   ,ViewCalendarParam.DAY_VALUE                    );
256                valKey                                          = getParam( ViewCalendarParam.VALUE_KEY                 ,null                                                                   );
257                final String valueBrCd          = getParam( ViewCalendarParam.VALUE_BR_FLAG_KEY ,ViewCalendarParam.VALUE_BR_FLAG_VALUE  );
258                final String firstWeekCd        = getParam( ViewCalendarParam.FIRSTWEEK_KEY             ,ViewCalendarParam.FIRSTWEEK_VALUE              );
259                final String headerLocale       = getParam( ViewCalendarParam.HEADER_LOCALE_KEY ,ViewCalendarParam.HEADER_LOCALE_VALUE  );
260                final String columnSizeCd       = getParam( ViewCalendarParam.COLUMN_SIZE_KEY   ,ViewCalendarParam.COLUMN_SIZE_VALUE    );      // 3.6.0.0 (2004/09/17)
261
262                viewKeys    = StringUtil.csv2Array( viewKeysCd );
263                firstWeek   = Integer.parseInt( firstWeekCd );
264                valueBrFlag = Boolean.parseBoolean( valueBrCd );        // 6.1.0.0 (2014/12/26) refactoring
265                columnSize  = Integer.parseInt( columnSizeCd );         // 3.6.0.0 (2004/09/17)
266
267                // 曜日ヘッダーをコピーして作成します。
268                if( "ja".equals( headerLocale ) ) {
269                        week = WEEK_JA;
270                }
271                else {
272                        week = WEEK_EN;
273                }
274        }
275
276        /**
277         * row行、colum列 のデータの値をHTML文字列に変換して返します。
278         * リソースバンドルが登録されている場合は、リソースに応じた
279         * HTML文字列を作成します。
280         * カレンダーViewに特化した、ラベル値を返します。
281         *
282         * @og.rev 3.8.0.9 (2005/10/17) writableControl 追加による引数変更
283         *
284         * @param       row             行番号
285         * @param       column  カラム番号
286         * @param       day             日付
287         * @return      row行、colum列 のデータの値
288         * @og.rtnNotNull
289         */
290        private String getValueLabel( final int row, final int column, final int day ) {
291                // 6.3.9.1 (2015/11/27) A method should have only one exit point, and that should be the last statement in the method.(PMD)
292                final String rtn ;
293                if( isColumnWritable( column ) && isWritable( row ) ) {
294                        final String val = getValue( row,column ) ;
295                        rtn = day + getEditorValue( row,column,val );
296                }
297                else {
298                        rtn = String.valueOf( day );
299                }
300                return rtn ;
301        }
302
303        /**
304         * 指定のYYYYMM文字列から、カレンダーオブジェクトを作成して返します。
305         * 日にちは、1日にセットされます。
306         *
307         * @param       ym      YYYYMM文字列
308         *
309         * @return      カレンダーオブジェクト
310         */
311        private Calendar getCalendar( final String ym ) {
312                final Calendar cal = Calendar.getInstance();
313
314                if( ym != null && ym.length() == 6 ) {
315                        final int yyyy = Integer.parseInt( ym.substring( 0,4 ) );
316                        final int mm   = Integer.parseInt( ym.substring( 4,6 ) ) - 1;
317                        final int dd   = 1;
318                        cal.set( yyyy,mm,dd,0,0,0 );
319                }
320                else {
321                        // カレンダーを今月の1日に設定する。
322                        cal.set(Calendar.DAY_OF_MONTH, 1);
323                }
324
325                return cal ;
326        }
327
328        /**
329         * フォーマットメソッドを使用できるかどうかを問い合わせます。
330         *
331         * @og.rev 3.6.0.0 (2004/09/17) 親クラス変更に伴なう追加
332         *
333         * @return      使用可能(true)/ 使用不可能 (false)
334         */
335        public boolean canUseFormat() {
336                return false;
337        }
338
339        /**
340         * 表示項目の編集(並び替え)が可能かどうかを返します。
341         *
342         * @og.rev 5.1.6.0 (2010/05/01) 新規追加
343         *
344         * @return      表示項目の編集(並び替え)が可能かどうか(false:不可能)
345         */
346        @Override
347        public boolean isEditable() {
348                return false;
349        }
350}