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.List; 019 020import org.opengion.fukurou.util.StringUtil; 021import org.opengion.fukurou.util.TagBuffer; 022import org.opengion.fukurou.util.XHTMLTag; 023import org.opengion.hayabusa.common.HybsSystem; 024import org.opengion.hayabusa.common.HybsSystemException; 025import org.opengion.hayabusa.html.FormatterType; 026import org.opengion.hayabusa.html.TableFormatter; 027import org.opengion.hayabusa.html.ViewAjaxTreeTableParam; 028 029/** 030 * JavaScript のツリー階層を持ったテーブル表示を行う、ツリーテーブル表示クラスです。 031 * 032 * AbstractViewForm により、setter/getterメソッドのデフォルト実装を提供しています。 033 * 各HTMLのタグに必要な setter/getterメソッドのみ、追加定義しています。 034 * 035 * AbstractViewForm を継承している為、ロケールに応じたラベルを出力させる事が出来ます。 036 * 037 * @og.rev 7.3.2.3 (2021/04/09) システム定数のJSP_IMGを使用します。(※ SYS.JSP + SYS.IMAGE_DIR) 038 * @og.group 画面表示 039 * 040 * @version 4.0 041 * @author Hiroki Nakamura 042 * @since JDK5.0, 043 */ 044public class ViewForm_HTMLAjaxTreeTable extends ViewForm_HTMLCustomTable { 045 /** このプログラムのVERSION文字列を設定します。 {@value} */ 046 private static final String VERSION = "8.5.5.1 (2024/02/29)" ; 047 048// // 6.4.4.2 (2016/04/01) JSP + "/image/" にする。 049// private static final String JSPIMG = HybsSystem.sys( "JSP" ) + "/image/" ; 050 // 8.0.3.0 (2021/12/17) hayabusa.taglib.ViewAjaxTreeParamTag へ移動 051 private static final String JSPIMG = HybsSystem.sys( "JSP_IMG" ) + "/" ; // 互換性の関係で最後に"/"を追加 052 053 private int[] childSearchKeys ; 054 private String childSearchJsp ; 055 private String levelClm ; 056 private int levelClmPos = -1; 057 private String imgCollapsed ; 058 private String imgExpanded ; 059 private String imgNoSub ; 060 private boolean expandAll ; // 4.3.3.0 (2008/10/01) 061 private int childViewStartNo= -1; // 4.3.3.0 (2008/10/01) 062 private int expCtrlClmPos = -1; // 4.3.5.0 (2008/02/01) 063 064 /** 065 * デフォルトコンストラクター 066 * 067 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 068 */ 069 public ViewForm_HTMLAjaxTreeTable() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 070 071 /** 072 * DBTableModel から HTML文字列を作成して返します。 073 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。 074 * 表示残りデータが pageSize 以下の場合は、残りのデータをすべて出力します。 075 * 076 * @og.rev 4.3.3.0 (2008/10/01) noTransition属性,childViewStartNo属性対応 077 * @og.rev 4.3.7.4 (2009/07/01) tbodyタグの入れ子を解消(FireFox対応) 078 * @og.rev 6.4.3.4 (2016/03/11) tdに、[カラム]が無いケースで、次の[カラム]のクラス属性が、前方すべてのtdにセットされてしまう対応。 079 * @og.rev 6.4.4.2 (2016/04/01) TableFormatterのタイプ別値取得処理の共通部をまとめる。 080 * @og.rev 6.4.5.0 (2016/04/08) メソッド変更( getColumnDbType(int) → getClassName(int) ) 081 * @og.rev 6.8.1.1 (2017/07/22) ckboxTD変数は、<td> から <td に変更します(タグの最後が記述されていない状態でもらう)。 082 * 083 * @param strNo 表示開始位置 084 * @param pageSize 表示件数 085 * @return DBTableModelから作成された HTML文字列 086 * @og.rtnNotNull 087 */ 088 @Override 089 public String create( final int strNo, final int pageSize ) { 090 if( getRowCount() == 0 ) { return ""; } // 暫定処置 091 092 initParam(); 093 094 // 4.3.3.0 (2008/10/01) 子データ差分取得用 095 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 096 final int startNo = childViewStartNo >= 0 ? childViewStartNo : strNo; 097 098 if( headerFormat == null ) { 099 makeDefaultFormat(); 100 } 101 102 headerFormat.makeFormat( getDBTableModel() ); 103 104 final StringBuilder out = new StringBuilder( BUFFER_LARGE ) 105 .append( getCountForm( startNo,pageSize ) ) 106 .append( getHeader() ); 107 108 if( bodyFormatsCount == 0 ) { 109 bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT]; 110 bodyFormats[0] = headerFormat ; 111 bodyFormatsCount ++ ; 112 } 113 else { 114 for( int i=0; i<bodyFormatsCount; i++ ) { 115 bodyFormats[i].makeFormat( getDBTableModel() ); 116 } 117 } 118 119 int bgClrCnt = 0; 120 final int lastNo = getLastNo( startNo, pageSize ); // 6.3.9.1 (2015/11/27) forループの近くに移動 121 for( int row=startNo; row<lastNo; row++ ) { 122 if( isSkip( row ) || isSkipNoEdit( row ) ) { continue; } // 4.3.1.0 (2008/09/08) 123 for( int i=0; i<bodyFormatsCount; i++ ) { 124 final TableFormatter bodyFormat = bodyFormats[i]; 125 if( ! bodyFormat.isUse( row,getDBTableModel() ) ) { continue; } // 3.5.4.0 (2003/11/25) 126 out.append("<tbody").append( getBgColorCycleClass( bgClrCnt++,row ) ); 127 if( isNoTransition() ) { // 4.3.3.0 (2008/10/01) 128 out.append( getHiddenRowValue( row ) ); 129 } 130 out.append('>') // 6.0.2.5 (2014/10/31) char を append する。 131 .append( bodyFormat.getTrTag() ); 132 133 if( isNumberDisplay() ) { 134 final String ckboxTD = "<td" + bodyFormat.getRowspan(); // 6.8.1.1 (2017/07/22) 135 out.append( makeCheckbox( ckboxTD,row,0 ) ); 136 } 137 138 int cl = 0; 139 for( ; cl<bodyFormat.getLocationSize(); cl++ ) { 140 String fmt = bodyFormat.getFormat(cl); 141 final int loc = bodyFormat.getLocation(cl); 142 if( ! bodyFormat.isNoClass() && loc >= 0 ) { 143 // 6.4.3.4 (2016/03/11) tdに、[カラム]が無いケースで、次の[カラム]のクラス属性が、前方すべてのtdにセットされてしまう対応。 144 final int idx = fmt.lastIndexOf( "<td" ); 145 if( idx >= 0 ) { // matchしてるので、あるはず 146 // 8.5.4.2 (2024/01/12) class 属性がフォーマット中に存在する場合、追記になる。 147 fmt = insertClassName( fmt,loc,idx ); // 8.5.4.2 (2024/01/12) 148 149 // final String tdclass = " class=\"" + getClassName(loc) + "\" "; // 6.4.5.0 (2016/04/08) 150 // fmt = fmt.substring( 0,idx+3 ) + tdclass + fmt.substring( idx+3 ) ; 151 } 152 } 153 out.append( fmt ); 154 if( loc >= 0 ) { 155 if( levelClm != null && levelClm.equals( getDBColumn( loc ).getName() ) ) { 156 out.append( getLvlClmTag( row ) ); 157 } 158 else { 159 // 6.4.4.2 (2016/04/01) 処理の共通部をまとめる。 160 out.append( getTypeCaseValue( bodyFormat.getType(cl),row,loc ) ); 161 } 162 } 163 else { 164 out.append( bodyFormat.getSystemFormat(row,loc) ); 165 } 166 } 167 out.append( bodyFormat.getFormat(cl) ) 168 .append("</tbody>").append( CR ); 169 } 170 } 171 172 if( footerFormat != null ) { 173 // 6.3.9.0 (2015/11/06) 引数にTableFormatterを渡して、処理の共有化を図る。 174 out.append( getTableFoot( footerFormat ) ); 175 } 176 177 out.append("</table>").append( CR ) 178 .append( getScrollBarEndDiv() ) 179 .append( getParameterTag() ); 180 181 return out.toString(); 182 } 183 184 /** 185 * フォーマットを設定します。 186 * 187 * @og.rev 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 188 * 189 * @param list TableFormatterのリスト 190 */ 191 @Override 192 public void setFormatterList( final List<TableFormatter> list ) { // 4.3.3.6 (2008/11/15) Generics警告対応 193 bodyFormats = new TableFormatter[BODYFORMAT_MAX_COUNT]; 194 195 bodyFormatsCount = 0; 196 // 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop 197 for( final TableFormatter format : list ) { 198// for( int i=0; i<list.size(); i++ ) { 199// final TableFormatter format = list.get( i ); // 4.3.3.6 (2008/11/15) Generics警告対応 200 // 8.5.5.1 (2024/02/29) switch文にアロー構文を使用 201// switch( format.getFormatType() ) { 202// case TYPE_HEAD : headerFormat = format; break; 203// case TYPE_BODY : bodyFormats[bodyFormatsCount++] = format; break; 204// case TYPE_FOOT : footerFormat = format; break; 205// default : final String errMsg = "FormatterType の定義外の値が指定されました。"; 206// // 4.3.4.4 (2009/01/01) 207// throw new HybsSystemException( errMsg ); 208// } 209 switch( format.getFormatType() ) { 210 case TYPE_HEAD -> headerFormat = format; 211 case TYPE_BODY -> bodyFormats[bodyFormatsCount++] = format; 212 case TYPE_FOOT -> footerFormat = format; 213 default -> { 214 final String errMsg = "FormatterType の定義外の値が指定されました。"; 215 // 4.3.4.4 (2009/01/01) 216 throw new HybsSystemException( errMsg ); 217 } 218 } 219 } 220 } 221 222 /** 223 * フォーマッターが設定されていない場合は、DBTableModelの情報からデフォルトの 224 * フォーマッターを作成します。 225 * 226 * @og.rev 4.3.3.6 (2008/11/15) columnDisplay,noDisplay対応 227 * @og.rev 4.3.5.0 (2008/02/01) 全展開コントロール用カラムへの対応 228 */ 229 private void makeDefaultFormat() { 230 final String[] clms = getDBTableModel().getNames(); 231 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 232 .append( "<tr>" ); 233 for( int i=0; i<clms.length; i++ ) { 234 if( isColumnDisplay( i ) && i != expCtrlClmPos ) { // 4.3.3.6 (2008/11/15) 4.3.5.0 (2008/02/01) 235 buf.append( "<td>[" ).append( clms[i] ).append( "]</td>" ); // 6.4.4.2 (2016/04/01) 236 } 237 } 238 buf.append( "</tr>" ); 239 240 final TableFormatter formatter = new TableFormatter(); 241 formatter.setFormat( buf.toString() ); 242 formatter.setFormatType( FormatterType.TYPE_HEAD ); 243 244 headerFormat = formatter; 245 } 246 247 /** 248 * フォーマットメソッドを使用できるかどうかを問い合わせます。 249 * 250 * @return フォーマットメソッドを使用できるか 251 */ 252 @Override 253 public boolean canUseFormat() { 254 return true; 255 } 256 257 /** 258 * 初期パラメーターを設定します。 259 * 260 * @og.rev 4.3.3.0 (2008/10/01) 初期全展開の属性追加 261 * @og.rev 4.3.5.0 (2008/02/01) 全展開時の状態をコントロールするためのフラグを追加 262 * @og.rev 8.0.3.0 (2021/12/17) JSPIMG の連結に、StringUtil.urlAppend を使用する。 263 */ 264 private void initParam() { 265 final String[] tmp = StringUtil.csv2Array( getParam( ViewAjaxTreeTableParam.CHILD_SEARCH_KEYS, "" ) ); 266 childSearchKeys = new int[tmp.length]; 267 for( int i=0; i<tmp.length; i++ ) { 268 childSearchKeys[i] = getDBTableModel().getColumnNo( tmp[i] ); 269 } 270 childSearchJsp = getParam( ViewAjaxTreeTableParam.CHILD_SEARCH_JSP, "getChildTag.jsp" ); 271 levelClm = getParam( ViewAjaxTreeTableParam.LVL_CLM_KEY, "LVL" ); 272 levelClmPos = getDBTableModel().getColumnNo( levelClm ); 273// imgCollapsed = getParam( ViewAjaxTreeTableParam.IMG_COLLAPSED, "collapsed.gif" ); 274// imgExpanded = getParam( ViewAjaxTreeTableParam.IMG_EXPANDED, "expanded.gif" ); 275// imgNoSub = getParam( ViewAjaxTreeTableParam.IMG_NO_SUB, "nosub.gif" ); 276 imgCollapsed = StringUtil.urlAppend( JSPIMG, getParam( ViewAjaxTreeTableParam.IMG_COLLAPSED, "collapsed.gif" ) ); // 8.0.3.0 (2021/12/17) 277 imgExpanded = StringUtil.urlAppend( JSPIMG, getParam( ViewAjaxTreeTableParam.IMG_EXPANDED, "expanded.gif" ) ); // 8.0.3.0 (2021/12/17) 278 imgNoSub = StringUtil.urlAppend( JSPIMG, getParam( ViewAjaxTreeTableParam.IMG_NO_SUB, "nosub.gif" ) ); // 8.0.3.0 (2021/12/17) 279 expandAll = Boolean.valueOf( getParam( ViewAjaxTreeTableParam.EXPAND_ALL, "false" ) ); // 4.3.2.0 (2008/09/11) 280 childViewStartNo= Integer.parseInt( getParam( ViewAjaxTreeTableParam.CHILD_VIEW_START_NO, "-1" ) ); // 6.0.2.4 (2014/10/17) メソッド間違い 281 final String expCtrlClm = getParam( ViewAjaxTreeTableParam.EXPAND_CONTROL_CLM_KEY, "EXPAND_CONTROL" ); // 4.3.5.0 (2008/02/01) 282 expCtrlClmPos = getDBTableModel().getColumnNo( expCtrlClm, false ); 283 } 284 285 /** 286 * JavaScriptに渡すためのパラメータをhiddenタグで出力します。 287 * 288 * @og.rev 4.3.3.0 (2008/10/01) 初期全展開対応 289 * @og.rev 4.3.5.0 (2008/02/01) 全展開時の状態をコントロールするためのフラグを追加 290 * @og.rev 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。 291 * @og.rev 6.4.3.4 (2016/03/11) forループを、2回まわさずに、1回にまとめます。 292 * @og.rev 8.0.3.0 (2021/12/17) JSPIMG の連結に、StringUtil.urlAppend を使用する。 293 * @og.rev 8.5.3.0 (2023/09/08) DynamicAttributes対応 294 * 295 * @param row 行番号 296 * @return HTMLタグ 297 * @og.rtnNotNull 298 */ 299 private String getLvlClmTag( final int row ) { 300 // 6.4.2.0 (2016/01/29) ソースを見ていたら、共通値が結構あったので、再利用します。 301 final String lvlClmVal = getValue( row, levelClmPos ); 302 303 // 6.4.3.4 (2016/03/11) forループを、2回まわさずに、1回にまとめます。 304 final StringBuilder keys = new StringBuilder( BUFFER_MIDDLE ).append( "command," ).append( levelClm ); 305 final StringBuilder vals = new StringBuilder( BUFFER_MIDDLE ).append( "NEW," ).append( lvlClmVal ); 306 307 // 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop 308 for( final int clm : childSearchKeys ) { 309// for( int i=0; i<childSearchKeys.length; i++ ) { 310// final int clm = childSearchKeys[i]; 311 keys.append( ',' ).append( getColumnName( clm ) ); // 6.0.2.5 (2014/10/31) char を append する。 312 vals.append( ',' ).append( getValue( row, clm ) ); // 6.0.2.5 (2014/10/31) char を append する。 313 } 314 315 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 316 final String imgsrc ; 317 final StringBuilder clazz = new StringBuilder( BUFFER_MIDDLE ); 318 clazz.append( "lvlctl unreplaceable" ); 319 if( expandAll ) { // 4.3.3.0 (2008/10/01) 320 if( row == getRowCount() - 1 321 || Integer.parseInt( lvlClmVal ) >= Integer.parseInt( getValue( row+1, levelClmPos ) ) ) { // 6.4.2.0 (2016/01/29) 322 final boolean isExp = expCtrlClmPos > -1 && StringUtil.nval( getValue( row, expCtrlClmPos ), false ) ; 323 if( isExp ) { 324// imgsrc = JSPIMG + imgCollapsed; 325 imgsrc = imgCollapsed; // 8.0.3.0 (2021/12/17) 326 } 327 else { 328// imgsrc = JSPIMG + imgNoSub; 329 imgsrc = imgNoSub; // 8.0.3.0 (2021/12/17) 330 clazz.append( " fetched nosub" ); 331 } 332 } 333 else { 334// imgsrc = JSPIMG + imgExpanded; 335 imgsrc = imgExpanded; // 8.0.3.0 (2021/12/17) 336 clazz.append( " fetched expanded" ); 337 } 338 } 339 else { 340// imgsrc = JSPIMG + imgCollapsed; 341 imgsrc = imgCollapsed; // 8.0.3.0 (2021/12/17) 342 } 343 344 // 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。 345 final String tag = new TagBuffer( "img" ) // 6.1.1.0 (2015/01/17) refactoring. 連結記述 346 .add( "class" , clazz.toString() ) 347 .add( "src" , imgsrc ) 348 .add( "alt" , "Level " + lvlClmVal ) // 6.4.2.0 (2016/01/29) 共通値を設定する。 349 .add( "title" , "Level " + lvlClmVal ) // 6.4.2.0 (2016/01/29) alt属性にtitle属性を追記。 350 .add( "p_lvl" , lvlClmVal ) // 6.4.2.0 (2016/01/29) 共通値を設定する。8.5.3.0 (2023/09/08) 先頭に"p_"付与 351 .add( "p_keys" , keys.toString() ) // 8.5.3.0 (2023/09/08) 先頭に"p_"付与 352 .add( "p_vals" , vals.toString() ) // 8.5.3.0 (2023/09/08) 先頭に"p_"付与 353 .makeTag(); 354 355 return getRendererValue( row, levelClmPos ) + tag; 356 } 357 358 /** 359 * JavaScriptに渡すためのパラメーターをhiddenタグをして出力します。 360 * 361 * @og.rev 8.0.3.0 (2021/12/17) JSPIMG の連結に、StringUtil.urlAppend を使用する。 362 * 363 * @return hiddenタグ 364 * @og.rtnNotNull 365 */ 366 private String getParameterTag() { 367 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 368 .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.CHILD_SEARCH_JSP, childSearchJsp ) ) 369// .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_COLLAPSED, JSPIMG + imgCollapsed ) ) 370// .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_EXPANDED, JSPIMG + imgExpanded ) ) 371// .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_NO_SUB, JSPIMG + imgNoSub ) ); 372 .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_COLLAPSED, imgCollapsed ) ) // 8.0.3.0 (2021/12/17) 373 .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_EXPANDED, imgExpanded ) ) // 8.0.3.0 (2021/12/17) 374 .append( XHTMLTag.hidden( ViewAjaxTreeTableParam.IMG_NO_SUB, imgNoSub ) ); // 8.0.3.0 (2021/12/17) 375 return buf.toString(); 376 } 377}