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.io.Serializable; 019import java.util.Arrays; 020import java.util.Comparator; 021import java.util.LinkedHashSet; 022import java.util.Set; 023import java.util.TreeSet; 024 025import org.opengion.fukurou.util.StringUtil; 026import org.opengion.hayabusa.common.HybsSystem; 027import org.opengion.hayabusa.common.HybsSystemException; 028import org.opengion.hayabusa.db.DBColumn; 029import org.opengion.hayabusa.db.DBColumnConfig; 030import org.opengion.hayabusa.db.DBTableModel; 031import org.opengion.hayabusa.db.DBTableModelSorter; 032import org.opengion.hayabusa.db.DBTableModelUtil; 033import org.opengion.hayabusa.db.Selection; 034import org.opengion.hayabusa.html.CrossMap; 035import org.opengion.hayabusa.html.ViewCrossTableParam; 036import org.opengion.hayabusa.resource.CodeData; 037import org.opengion.hayabusa.resource.ResourceManager; 038 039/** 040 * クロス集計テーブル作成クラスです。 041 * 042 * select dept.dname,emp.deptno,substrb(job,1,2) as X,job,mgr,sum(sal),count(*) 043 * from emp,dept 044 * where emp.deptno = dept.deptno 045 * group by dept.dname,emp.deptno,cube(job,mgr) 046 * order by emp.deptno,job,mgr; 047 * 048 * HEAD1 :ヘッダー。前段と同じデータは表示させない。 049 * HEAD2 :キーブレイクさせるカラム。また、前段と同じデータは表示させない。 050 * HEAD3 :キーブレイクさせないカラム。また、前段と同じデータでも表示させる。 051 * ROW :行データのヘッダーになるカラム 052 * COL :列データのヘッダーになるカラム。下記のSUM1,SUM2の両方のヘッダーになる。 053 * SUM1 :列データの値になるカラム。 054 * SUM2 :列データの値になるカラム。 055 * 056 * SUMカラムの数、キーブレイクのカラム名、グループ化するカラム名を 057 * 指定することで、これらのクロス集計結果の表示方法を指定します。 058 * 059 * breakColumn = "DEPTNO" キーブレイクのカラム名 060 * noGroupColumns = "X" グループ化するカラム名 061 * sumNumber = "2" SUMカラムの数 062 * cubeXColumn = "JOB" CUBE計算の1つ目(X)カラムを指定 063 * cubeYColumn = "MGR" CUBE計算の2つ目(Y)カラムを指定 064 * cubeSortType = "NUMBER" CUBE Y の列ヘッダーのソート方法を指定 065 * gokeiSortDir = "false" 合計カラムのソート方向を指定(初期値:ソートしない) 066 * shokeiLabel = "SHOKEI" 列小計のカラムに表示するラベルID 067 * gokeiLabel = "GOKEI" 列合計のカラムに表示するラベルID 068 * useHeaderColumn= "false" ヘッダーカラムにレンデラー、エディターを適用するかを指定 069 * useClassAdd = "false" 各列情報のclass属性に、カラム名などを付与するかどうかを指定 070 * useHeaderResource = "false" ヘッダー表示にラベルリソースを利用するか 071 * 072 * 各カラムの属性(HEAD,SUM等)を認識する方法 073 * 074 * HEAD1 HEAD2 HEAD3 ROW COL SUM1 SUM2 という並びを認識する方法は、 075 * 多数の前提条件を利用して、出来るだけ少ないパラメータで自動認識 076 * させています。 077 * 若干理解しにくいかもしれませんが、慣れてください。 078 * 079 * 前提条件: 080 * ROW,COL は、必ず1個ずつ存在する。 081 * HEAD群、ROW,COL,SUM群 という並びになっている。 082 * SUM群の数は、パラメータで指定する。 083 * 計算方法: 084 * HEAD数=カラム数(7)-SUM数(2)-1(ROW,COL分) = 4 個 (0 〜 3) 085 * ROWアドレス=cubeXColumn 設定 (3) ※ アドレスは0から始まる為 086 * COLアドレス=cubeYColumn 設定 (4) 087 * SUMアドレス=HEAD数+1 〜 カラム数(7)-1 (5 〜 6) 088 * 089 * @og.rev 3.5.4.0 (2003/11/25) 新規作成 090 * @og.group 画面表示 091 * 092 * @version 4.0 093 * @author Kazuhiko Hasegawa 094 * @since JDK5.0, 095 */ 096public class ViewForm_HTMLCrossTable extends ViewForm_HTMLTable { 097 //* このプログラムのVERSION文字列を設定します。 {@value} */ 098 private static final String VERSION = "5.7.4.3 (2014/03/28)" ; 099 100 private String[] groupByData = null; 101 private String[] groupByCls = null; 102 103 // 3.5.4.8 (2004/02/23) 機能改善 104 private int rowClmNo = -1; // ROWカラムのカラム番号 105 private int colClmNo = -1; // CLMカラムのカラム番号 106 private int headCount = 0; // HEADカラムの数 107 private int sumCount = 1; // 合計カラムの数 108 private int breakClmNo = -1; // ブレークするカラムのカラム番号 109 private boolean[] noGroupClm = null; // グループ化する/しないのフラグ配列 110 private String shokeiLabel = "小計"; // 列小計のカラムに表示するラベルID 111 private String gokeiLabel = "合計"; // 列合計のカラムに表示するラベルID 112 private String gokeiSortDir = null; // 列合計のカラムをソートする方向 113 114 // 3.5.6.3 (2004/07/12) ソート方式[STRING,NUMBER,LOAD] 115 private String cubeSortType = "LOAD"; 116 117 private DBTableModel table2 = null; 118 private boolean firstStep = true; 119 120 private String[] clmKeys = null; // 集計部のカラムキー(集計カラムが複数でも一つ)の配列 121 private String[] clsAdd = null; // 5.2.2.0 (2010/11/01) class属性に付与されるカラムキー 122 123 private String noDisplayKeys = null; // 3.7.0.4 (2005/03/18) 124 private String columnDisplayKeys = null; // 5.2.2.0 (2010/11/01) 125 126 private boolean firstClmGokei = false; // 5.0.0.3 (2009/09/22) 127 private boolean useHeaderColumn = false; // 5.2.2.0 (2010/11/01) 128 private boolean useClassAdd = false; // 5.2.2.0 (2010/11/01) class属性にカラムキーを追加するかどうか 129 private boolean useHeaderResource = false; // 5.5.5.0 (2012/07/28) 130 private String headerCode = null; // 5.5.5.0 (2012/07/28) 131 132 /** 133 * 初期化します。 134 * ここでは、内部で使用されているキャッシュをクリアし、 135 * 新しいモデル(DBTableModel)と言語(lang) を元に内部データを再構築します。 136 * ただし、設定情報は、以前の状態がそのままキープされています。 137 * 138 * @og.rev 3.5.4.8 (2004/02/23) paramInit メソッドで、初期化を行います。 139 * @og.rev 3.5.6.1 (2004/06/25) lang 言語コード 属性を削除します。 140 * 141 * @param table DBTableModelオブジェクト 142 */ 143 @Override 144 public void init( final DBTableModel table ) { 145 table2 = table; 146 firstStep = true; 147 } 148 149 /** 150 * 内容をクリア(初期化)します。 151 * 152 * @og.rev 3.5.6.3 (2004/07/12) cubeSortType , gokeiSortDir 属性を追加します。 153 * @og.rev 3.7.0.4 (2005/03/18) noDisplayKeys 属性を追加します。 154 * @og.rev 3.7.1.1 (2005/05/31) shokeiLabel,gokeiLabel の初期値変更 155 * @og.rev 5.2.2.0 (2010/11/01) columnDisplayKeys、clsAdd、useClassAdd 属性を追加します 156 * @og.rev 5.5.5.0 (2012/07/20) useHeaderResource追加 157 */ 158 @Override 159 public void clear() { 160 super.clear(); 161 groupByData = null; 162 groupByCls = null; 163 rowClmNo = -1; // ROWカラムのカラム番号 164 colClmNo = -1; // CLMカラムのカラム番号 165 headCount = 0; // HEADカラムの数 166 sumCount = 1; // 合計カラムの数 167 breakClmNo = -1; // ブレークするカラムのカラム番号 168 noGroupClm = null; // グループ化する/しないのフラグ配列 169 table2 = null; 170 firstStep = true; 171 clmKeys = null; 172 clsAdd = null; // 5.2.2.0 (2010/11/01) 173 shokeiLabel = "小計"; // 列小計のカラムに表示するラベルID 174 gokeiLabel = "合計"; // 列合計のカラムに表示するラベルID 175 cubeSortType = "LOAD"; // 3.5.6.3 (2004/07/12) 176 gokeiSortDir = null; // 3.5.6.3 (2004/07/12) 列合計のカラムをソートする方向 177 noDisplayKeys = null; // 3.7.0.4 (2005/03/18) 178 columnDisplayKeys = null; // 5.2.2.0 (2010/11/01) 179 firstClmGokei = false; // 5.2.2.0 (2010/11/01) 180 useHeaderColumn = false; // 5.2.2.0 (2010/11/01) 181 useClassAdd = false; // 5.2.2.0 (2010/11/01) 182 useHeaderResource = false; // 5.5.5.0 (2012/07/20) 183 headerCode = null; // 5.5.5.0 (2012/07/28) 184 } 185 186 /** 187 * DBTableModel から HTML文字列を作成して返します。 188 * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。 189 * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。 190 * 191 * @og.rev 3.5.5.0 (2004/03/12) No 欄そのものの作成判断ロジックを追加 192 * @og.rev 3.5.6.1 (2004/06/25) lang 言語コード 属性を削除します。 193 * @og.rev 3.5.6.4 (2004/07/16) ヘッダーとボディー部をJavaScriptで分離 194 * @og.rev 3.7.0.4 (2005/03/18) setNoDisplay メソッドを追加 195 * @og.rev 4.3.1.0 (2008/09/08) 編集行のみを表示する属性(isSkipNoEdit)追加 196 * @og.rev 5.0.0.3 (2009/09/22) 合計列をcubeの先頭に出せるようにする 197 * @og.rev 5.1.0.0 (2009/11/04) ↑で合計列が複数カラム存在する場合に正しく表示されないバグを修正 198 * @og.rev 5.2.2.0 (2010/11/01) setColumnDisplay メソッドを追加 199 * 200 * @param startNo 表示開始位置 201 * @param pageSize 表示件数 202 * 203 * @return DBTableModelから作成された HTML文字列 204 */ 205 @Override 206 public String create( final int startNo, final int pageSize ) { 207 if( firstStep ) { 208 paramInit( table2 ); 209 super.init( makeCrossTable(table2) ); 210 super.setNoDisplay( noDisplayKeys ) ; // 3.7.0.4 (2005/03/18) 211 super.setColumnDisplay( columnDisplayKeys ) ; // 5.2.2.0 (2010/11/01) 212 markerSet( this ); // 3.5.6.4 (2004/07/16) 213 firstStep = false; 214 } 215 216 if( getRowCount() == 0 ) { return ""; } // 暫定処置 217 218 int clmCnt = getColumnCount(); // 3.5.5.7 (2004/05/10) 219 220 headerLine = null; 221 222 int lastNo = getLastNo( startNo, pageSize ); 223 int blc = getBackLinkCount(); 224 String backData = null; 225 226 StringBuilder out = new StringBuilder( HybsSystem.BUFFER_LARGE ); 227 228 out.append( getCountForm( startNo,pageSize ) ); 229 out.append( getHeader() ); 230 231 String ckboxTD = " <td class=\"" + ViewCrossTableParam.HEADER1 + "\">"; 232 233 out.append("<tbody>").append( HybsSystem.CR ); 234 int bgClrCnt = 0; 235 boolean shokei; 236 for( int row=startNo; row<lastNo; row++ ) { 237// if( isSkip( row ) ) { continue; } // 3.5.3.1 (2003/10/31) 238 if( isSkip( row ) || isSkipNoEdit( row ) ) { continue; } // 4.3.1.0 (2008/09/08) 239 // キーブレイク時のヘッダー設定 240 if( breakClmNo >= 0 ) { 241 String val = getValue( row,breakClmNo ); 242 if( backData == null ) { // キーブレイクの初期データ設定。 243 backData = val; 244 } 245 else { 246 if( ! backData.equals( val ) ) { 247 backData = val; 248 out.append( getHeadLine() ); 249 } 250 } 251 } 252 // 小計ヘッダー時のクラス設定 253 String val2 = getValue( row,rowClmNo ); 254 if( val2.length() == 0 ) { 255 shokei = true; 256 out.append("<tr class=\"").append( ViewCrossTableParam.SHOKEI ).append("\">"); 257 } 258 else { 259 shokei = false; 260 out.append("<tr").append( getBgColorCycleClass( bgClrCnt++ ) ).append(">"); 261 } 262 out.append( HybsSystem.CR ); 263 // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加 264 if( isNumberDisplay() ) { 265 out.append( makeCheckbox( ckboxTD, row, blc ) ).append( HybsSystem.CR ); 266 } 267 for(int column = 0; column < clmCnt; column++) { 268 if( isColumnDisplay( column ) ) { 269 if( column < headCount-1 ) { // CUBEではない行ヘッダー部 270 String val = getGroupData( column,getRendererValue(row,column) ); 271 out.append(" <td class=\"").append( groupByCls[column] ).append("\">"); 272 out.append( val ); 273 } 274 else if( column == headCount-1 ) { // ヘッダーの最後尾 275 if( shokei ) { 276 out.append(" <td class=\"").append( ViewCrossTableParam.SHOKEI ).append("\">"); 277 out.append( shokeiLabel ); 278 } 279 else { 280 if( breakClmNo > 0 ) { // ヘッダーがある場合 281 out.append(" <td class=\"").append( groupByCls[column-1] ).append("\">"); 282 } 283 else { 284 out.append(" <td class=\"").append( ViewCrossTableParam.HEADER1 ).append("\">"); 285 } 286 out.append( getRendererValue(row,column) ); 287 } 288 } 289 // else if( column >= clmCnt-sumCount ) { // CUBEの最終カラム(列合計) 290 else if( column >= clmCnt-sumCount && ! firstClmGokei ) { // 5.0.0.3 (2009/09/22) CUBEの最終カラム(列合計) 291 out.append(" <td class=\"").append( ViewCrossTableParam.SHOKEI ).append("\">"); 292 out.append( getRendererValue(row,column) ); 293 } 294// else if( column == headCount && firstClmGokei ) { // 5.0.0.3 (2009/09/22) CUBEの先頭カラム(列合計) 295 else if( column >= headCount && column < headCount + sumCount && firstClmGokei ) { // 5.1.0.0 (2009/11/04) 296 out.append(" <td class=\"").append( ViewCrossTableParam.SHOKEI ).append("\">"); 297// out.append( getRendererValue(row,clmCnt-sumCount) ); 298 out.append( getRendererValue(row,clmCnt-sumCount+(column-headCount)) ); // 5.1.0.0 (2009/11/04) 299 } 300 else { // カラム SUM列 301 if( useClassAdd && clsAdd[column] != null ) { 302 out.append(" <td class=\"").append( clsAdd[column] ).append("\">"); 303 } 304 else { 305 out.append(" <td>"); 306 } 307 if( firstClmGokei ){ 308// out.append( getRendererValue(row,column-1) ); // 合計行を先頭に出すと1つずれる 309 out.append( getRendererValue(row,column-sumCount) ); // 5.1.0.0 (2009/11/04) 310 } 311 else{ 312 out.append( getRendererValue(row,column) ); 313 } 314 } 315 out.append("</td>").append( HybsSystem.CR ); 316 } 317 } 318 out.append("</tr>").append( HybsSystem.CR ); 319 } 320 out.append("</tbody>").append( HybsSystem.CR ); 321 out.append("</table>").append( HybsSystem.CR ); 322 323 out.append( getScrollBarEndDiv() ); // 3.8.0.3 (2005/07/15) 324 return out.toString(); 325 } 326 327 /** 328 * パラメータ内容を初期化します。 329 * 330 * @og.rev 3.5.4.8 (2004/02/23) 新規作成 331 * @og.rev 3.5.6.3 (2004/07/12) 列ヘッダーのソート方法を指定 332 * @og.rev 5.0.0.3 (2009/09/22) 合計行をCUBEの先頭に持ってくるためのフラグ追加 333 * @og.rev 5.2.2.0 (2010/11/01) useHeaderColumn,useClassAdd 属性の追加 334 * 335 * @param table 入力もとの DBTableModelオブジェクト 336 */ 337 private void paramInit( final DBTableModel table ) { 338 String breakColumn = getParam( ViewCrossTableParam.BREAK_COLUMN_KEY , null ); 339 String noGroupColumns = getParam( ViewCrossTableParam.NO_GROUP_COLUMNS_KEY , null ); 340 String sumNumber = getParam( ViewCrossTableParam.SUM_NUMBER_KEY , null ); 341 shokeiLabel = getParam( ViewCrossTableParam.SHOKEI_LABEL_KEY , shokeiLabel ); 342 gokeiLabel = getParam( ViewCrossTableParam.GOKEI_LABEL_KEY , gokeiLabel ); 343 String cubeXColumn = getParam( ViewCrossTableParam.CUBE_X_COLUMN_KEY , null ); // CUBE計算の1つ目(X)カラムを指定 344 String cubeYColumn = getParam( ViewCrossTableParam.CUBE_Y_COLUMN_KEY , null ); // CUBE計算の2つ目(Y)カラムを指定 345 cubeSortType = getParam( ViewCrossTableParam.CUBE_SORT_TYPE_KEY , "LOAD" ); // 3.5.6.3 (2004/07/12) 346 gokeiSortDir = getParam( ViewCrossTableParam.GOKEI_SORT_DIR_KEY , null ); // 3.5.6.3 (2004/07/12) 347 firstClmGokei = StringUtil.nval( getParam( ViewCrossTableParam.FIRST_CLM_GOKEI_KEY , null ), false); // 5.0.0.3 (2009/09/22) 348 useHeaderColumn = StringUtil.nval( getParam( ViewCrossTableParam.USE_HEADER_COLUMN , null ), false); // 5.2.2.0 (2010/11/01) 349 useClassAdd = StringUtil.nval( getParam( ViewCrossTableParam.USE_CLASS_ADD , null ), false); // 5.2.2.0 (2010/11/01) 350 useHeaderResource = StringUtil.nval( getParam( ViewCrossTableParam.USE_HEADER_RSC , null ), false); // 5.5.5.0 (2012/07/20) 351 headerCode = getParam( ViewCrossTableParam.HEADER_CODE_KEY , null ); // 5.5.5.0 (2012/07/28) 352 353 if( sumNumber != null ) { 354 sumCount = Integer.parseInt( sumNumber ); 355 } 356 357 // HEAD数=カラム数-SUM数-1(COL分) ROW は、HEADに含みます。 358 headCount = table.getColumnCount() - sumCount - 1; 359 360 // 3.5.5.9 (2004/06/07) 361 if( cubeXColumn != null ) { 362 rowClmNo = table.getColumnNo( cubeXColumn ); 363 } 364 else { 365 rowClmNo = headCount-1; // ROWカラムのカラム番号 366 } 367 368 // 3.5.5.9 (2004/06/07) 369 if( cubeYColumn != null ) { 370 colClmNo = table.getColumnNo( cubeYColumn ); 371 } 372 else { 373 colClmNo = headCount; // CLMカラムのカラム番号 374 } 375 376 if( breakColumn != null ) { 377 breakClmNo = table.getColumnNo( breakColumn ); 378 } 379 380 groupByData = new String[headCount]; 381 groupByCls = new String[headCount]; 382 Arrays.fill( groupByCls,ViewCrossTableParam.HEADER2 ); // 変であるが、最初に入れ替えが発生する為。 383 384 noGroupClm = new boolean[headCount]; // グループ化する/しないのフラグ配列 385 Arrays.fill( noGroupClm,false ); 386 387 if( noGroupColumns != null ) { 388 String[] gClms = StringUtil.csv2Array( noGroupColumns ); 389 for( int i=0; i<gClms.length; i++ ) { 390 noGroupClm[table.getColumnNo( gClms[i] )] = true; 391 } 392 } 393 394 if( ! "true".equalsIgnoreCase( gokeiSortDir ) && 395 ! "false".equalsIgnoreCase( gokeiSortDir ) ) { 396 gokeiSortDir = null; 397 } 398 } 399 400 /** 401 * CUBEではない行ヘッダー部の値が前と同じならば、ゼロ文字列を返します。 402 * 403 * @param clm カラム番号 404 * @param val 比較する値 405 * 406 * @return 前と同じなら,""を、異なる場合は、引数の val を返します。 407 */ 408 private String getGroupData( final int clm,final String val ) { 409 if( noGroupClm[clm] ) { return val; } 410 411 if( val.equals( groupByData[clm] )) { 412 return ""; 413 } 414 else { 415 groupByData[clm] = val; 416 groupByCls[clm] = ( groupByCls[clm].equals( ViewCrossTableParam.HEADER1 )) ? ViewCrossTableParam.HEADER2 : ViewCrossTableParam.HEADER1 ; 417 return val; 418 } 419 } 420 421 /** 422 * 選択用のチェックボックスと行番号と変更タイプ(A,C,D)を表示します。 423 * 424 * @param ckboxTD チェックボックスのタグ(マルチカラム時のrowspan対応) 425 * @param row 行番号 426 * @param blc バックラインカウント(先頭へ戻るリンク間隔) 427 * 428 * @return tdタグで囲まれたチェックボックスのHTML文字列 429 */ 430 @Override 431 protected String makeCheckbox( final String ckboxTD,final int row,final int blc ) { 432 StringBuilder out = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 433 434 out.append( ckboxTD ).append("</td>"); 435 out.append( ckboxTD ).append("</td>"); 436 out.append( ckboxTD ); 437 // 3.5.1.0 (2003/10/03) Noカラムに、numberType 属性を追加 438 if( blc != 0 && (row+1) % blc == 0 ) { 439 out.append( "<a href=\"#top\">" ).append( (row+1) ).append( "</a>" ); 440 } else { 441 out.append( (row+1) ); 442 } 443 out.append("</td>"); 444 445 return out.toString(); 446 } 447 448 /** 449 * ヘッダー繰り返し部を、getTableHead()メソッドから分離。 450 * 451 * @og.rev 3.5.4.5 (2004/01/23) 実装をgetHeadLine( String thTag )に移動 452 * @og.rev 3.5.5.0 (2004/03/12) No 欄そのものの作成判断ロジックを追加 453 * @og.rev 5.0.0.3 (2009/09/17) 合計行を出力する位置をfirstClmGokeiで変える 454 * @og.rev 5.2.2.0 (2010/11/01) 集計部の ColumnDisplay/NoDisplay 対応 455 * @og.rev 5.5.5.0 (2012/07/28) useHeaderResource利用時のヘッダのラベル/コードリソース対応 456 * @og.rev 5.7.4.2 (2014/03/20) ヘッダーのリソース適用見直し 457 * @og.rev 5.7.4.3 (2014/03/28) useHeaderResource 単独でリソース適用します。 458 * 459 * @return テーブルのタグ文字列 460 */ 461 @Override 462 protected String getHeadLine() { 463 if( headerLine != null ) { return headerLine; } // キャッシュを返す。 464 465 String rowspan = ""; 466 if( sumCount > 1 ) { rowspan = " rowspan=\"2\""; } 467 468 String thTag = "<th" + rowspan; 469 470 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_MIDDLE ); 471 472 buf.append("<tr").append( rowspan ).append(" class=\"row_h\"").append(" >").append( HybsSystem.CR ); 473 474 // 3.5.5.0 (2004/03/12) No 欄そのものの作成判断追加 475 if( isNumberDisplay() ) { 476 buf.append( thTag ).append(" colspan='3'>").append( getNumberHeader() ).append("</th>"); 477 } 478 479 buf.append( HybsSystem.CR ); 480 // ヘッダー部分は、そのまま表示します。 481 for(int column = 0; column < headCount; column++) { 482 if( isColumnDisplay( column ) ) { 483 buf.append( thTag ).append(">"); 484 buf.append( getColumnLabel(column) ); 485 buf.append("</th>").append( HybsSystem.CR ); 486 } 487 } 488 489 // ヘッダー部分(上段)は、カラム配列を利用します。 490 String colspan = ""; 491 if( sumCount > 1 ) { colspan = " colspan='" + sumCount + "'"; } 492 493 // 5.2.2.0 (2010/11/01) 集計部の ColumnDisplay/NoDisplay 対応 494// String gokeiClm = "<th" + colspan + ">" + temp + "</th>" + HybsSystem.CR ; 495 String gokeiClm = null; 496 if( isColumnDisplay( headCount+(clmKeys.length-1)*sumCount ) ) { 497 String temp = clmKeys[clmKeys.length-1]; 498 if( temp == null || temp.length() == 0 ) { 499 temp = gokeiLabel; 500 } 501 502 gokeiClm = "<th" + colspan + ">" + temp + "</th>" + HybsSystem.CR ; 503 } 504 505 // 5.2.2.0 (2010/11/01) 最後のカラムが、合計行。 506 // 5.0.0.3 (2009/09/22) firstClmGokei が trueの場合はcubeの先頭に出すようにします。 507// if( firstClmGokei && isColumnDisplay( headCount+clmKeys.length-1 ) ) { 508 if( firstClmGokei && gokeiClm != null ) { 509 buf.append( gokeiClm ); 510 } 511 512// // 5.0.0.3 (2009/09/22) trueの場合はcubeの先頭に出すようにします。 513// if( firstClmGokei ){ 514// String temp = clmKeys[clmKeys.length-1]; 515// if( temp == null || temp.length() == 0 ) { 516// temp = gokeiLabel; 517// } 518// buf.append( "<th").append( colspan ).append( ">" ); 519// buf.append( temp ); 520// buf.append("</th>").append( HybsSystem.CR ); 521// } 522 523 // 3.7.0.4 (2005/03/18) カラム配列は、カラム番号と別物 524 ResourceManager resource = getResourceManager(); 525 Selection selection = null; 526 if( headerCode != null && headerCode.length() > 0 && resource != null ){ 527 DBColumn clmTmp = resource.getDBColumn( headerCode ); 528 //selection = new Selection_CODE(resource.getCodeData( headerCode )); code直の場合 529 if ( clmTmp != null ){ 530 selection = clmTmp.getSelection(); 531 } 532 } 533 534 // 5.7.4.2 (2014/03/20) ヘッダーのリソース適用見直し 535 // 5.7.4.3 (2014/03/28) useHeaderResource 単独でリソース適用します。 536 DBColumn colClm = null; 537// if( useHeaderResource && useHeaderColumn ) { 538 if( useHeaderResource ) { 539 colClm = table2.getDBColumn( colClmNo ); 540 } 541 542 for( int keyNo = 0; keyNo < clmKeys.length-1; keyNo++ ) { 543 // 5.2.2.0 (2010/11/01) ColumnDisplay/NoDisplay 対応 544 if( isColumnDisplay( headCount+keyNo ) ) { 545 buf.append( "<th").append( colspan ).append( ">" ); 546// if( headerCode != null && headerCode.length() > 0 && selection != null ){ 547 if( selection != null ){ 548 buf.append( selection.getValueLabel( clmKeys[keyNo] ) ); 549 } 550 // 5.7.4.2 (2014/03/20) ヘッダーのリソース適用見直し 551// else if( useHeaderResource && useHeaderColumn ){ // 5.5.5.0 (2012/07/28) ヘッダのリソース対応 552// buf.append( getColumnLabel(headCount+keyNo) ); 553// } 554 // 5.7.4.3 (2014/03/28) ヘッダーのリソース適用は、CLMカラムのカラム番号のみとします。 555 else if( colClm != null ) { 556 buf.append( colClm.getRendererValue( clmKeys[keyNo] ) ); 557 } 558 else{ 559 buf.append( clmKeys[keyNo] ); 560 } 561 buf.append("</th>").append( HybsSystem.CR ); 562 } 563 } 564 565 // 5.2.2.0 (2010/11/01) 最後のカラムが、合計行。 566 // 5.0.0.3 (2009/09/22) firstClmGokei が trueの場合はcubeの先頭に出すようにします。 567// if( ! firstClmGokei && isColumnDisplay( headCount+clmKeys.length-1 ) ) { 568 if( ! firstClmGokei && gokeiClm != null ) { 569 buf.append( gokeiClm ); 570 } 571 572 // 3.5.6.2 (2004/07/05) makeCrossTable( DBTableModel table ) に移動 573 // 最終カラム(列)がNULL(空文字列)の場合、列合計です。 574// if( ! firstClmGokei ){ // 5.0.0.3 (2009/09/22) falseの場合は最終列に出す 575// String temp = clmKeys[clmKeys.length-1]; 576// if( temp == null || temp.length() == 0 ) { 577// temp = gokeiLabel; 578// } 579// buf.append( "<th").append( colspan ).append( ">" ); 580// buf.append( temp ); 581// buf.append("</th>").append( HybsSystem.CR ); 582// } 583 584 buf.append("</tr>").append( HybsSystem.CR ); 585 586 if( sumCount > 1 ) { 587 buf.append("<tr").append(" class=\"row_h\"").append(" >").append( HybsSystem.CR ); 588 int clmCnt = getColumnCount(); // 3.5.5.7 (2004/05/10) 589 for(int column = headCount; column < clmCnt; column++) { 590 if( isColumnDisplay( column ) ) { 591 buf.append( "<th>"); 592 buf.append( getColumnLabel(column) ); 593 buf.append("</th>").append( HybsSystem.CR ); 594 } 595 } 596 buf.append("</tr>").append( HybsSystem.CR ); 597 } 598 599 headerLine = buf.toString(); 600 return headerLine; 601 } 602 603 /** 604 * クロス集計結果の DBTableModelオブジェクトを作成します。 605 * 606 * @og.rev 3.5.4.8 (2004/02/23) paramInit メソッドで、初期化を行います。 607 * @og.rev 3.5.6.3 (2004/07/12) 列ヘッダーのソート可否の指定を追加 608 * @og.rev 4.0.0.0 (2007/11/27) ヘッダーカラムのエディター、レンデラー適用対応 609 * @og.rev 4.3.5.7 (2008/03/22) ↑リソースが存在しない場合は、ラベルのみ入れ替え 610 * @og.rev 5.2.2.0 (2010/11/01) useHeaderColumn,useClassAdd 属性の追加 611 * @og.rev 5.7.4.3 (2014/03/28) useHeaderColumn の適用条件を、最初の集計カラムのみに変更。 612 * 613 * @param table 入力もとの DBTableModelオブジェクト 614 * 615 * @return DBTableModelオブジェクト 616 */ 617 private DBTableModel makeCrossTable( final DBTableModel table ) { 618 Set<String> clmData = gatSortAlgorithmSet(); 619 620 // 列のキーとなるカラムの値を取得します。 621 int rowCnt = table.getRowCount(); // 3.5.5.7 (2004/05/10) 622 for( int row=0; row<rowCnt; row++ ) { 623 String clm = table.getValue( row,colClmNo ); 624 if( clm.length() > 0 ) { clmData.add( clm ); } 625 } 626 // ゼロストリングは、合計行になりますので、最後に追加します。 627 628 // 3.5.6.3 (2004/07/12) ゼロストリングは、合計行になりますので、最後に追加します。 629// clmKeys = new String[ clmData.size() + 1 ]; 630// clmKeys = clmData.toArray( clmKeys ) ; 631 clmKeys = clmData.toArray( new String[clmData.size() + 1] ) ; 632 633 clmKeys[clmKeys.length-1] = "" ; 634 635 int numberOfColumns = headCount + clmKeys.length * sumCount ; 636 637 DBTableModel tableImpl = DBTableModelUtil.newDBTable(); 638 tableImpl.init( numberOfColumns ); 639 640 // ヘッダーカラム(ROWデータ含む)は、そのまま、設定します。 641 for(int column=0; column<headCount; column++) { 642 tableImpl.setDBColumn( column,table.getDBColumn(column) ); 643 } 644 645 // 列情報は、合計値のカラム定義を使用します。 646 DBColumn[] dbColumn = new DBColumn[sumCount]; 647 for( int i=0; i<sumCount; i++ ) { 648 dbColumn[i] = table.getDBColumn(headCount + 1 + i); 649 } 650 651 // 列情報は、列の名前をカラムの値に変えて、合計カラム列のコピー情報を設定します。 652 653 int sumId = 0; 654 ResourceManager resource = getResourceManager(); 655 useHeaderColumn = useHeaderColumn && resource != null ; // 5.2.2.0 (2010/11/01) 656 657 // 5.2.2.0 (2010/11/01) useClassAdd 属性の追加 658 659 clsAdd = new String[numberOfColumns]; 660 661 // 列情報カラムは、ヘッダー分に割り当てられる為、開始が、headCount からになります。 662 for(int column=headCount; column<numberOfColumns; column++) { 663 DBColumn dbClm = dbColumn[sumId]; 664 String clmKey = clmKeys[ (column-headCount)/sumCount ]; 665 666 // 5.2.2.0 (2010/11/01) useClassAdd 属性の追加 667 if( useClassAdd ) { 668 // ※ 特殊対応:cssなどで指定できるIDやCLASS属性は、先頭文字が数字の場合は、 669 // 無効になります。(つまり、効きません。) 670 // 表示ヘッダーは、年月や、社員番号(数字)などのケースもあります。そこで、先頭が数字の 671 // 場合は、"x"(小文字のx)を自動的に頭に追加します。 672 StringBuilder buf = new StringBuilder(); 673 if( clmKey != null && clmKey.length() > 0 ) { 674 char ch = clmKey.charAt(0); 675 if( ch >= '0' && ch <= '9' ) { 676 buf.append( "x" ); 677 } 678 buf.append( clmKey ); 679 } 680 681 String nm = dbClm.getName(); 682 if( nm != null && nm.length() > 0 ) { 683 buf.append( " " ); 684 char ch = nm.charAt(0); 685 if( ch >= '0' && ch <= '9' ) { 686 buf.append( "x" ); 687 } 688 buf.append( nm ); 689 } 690 clsAdd[column] = buf.toString(); 691 } 692 693 // 4.0.0.0 (2007/11/27) 694// if( resource != null && "true".equalsIgnoreCase( getParam( "useHeaderColumn", null ) ) ) { 695 // 5.2.2.0 (2010/11/01) 処理見直し。useHeaderColumn の前出しと、setName 対応 696 697 // 5.7.4.3 (2014/03/28) useHeaderColumn の適用条件を、最初の集計カラムのみに変更。 698// if( useHeaderColumn ) { 699 if( useHeaderColumn && sumId == 0 ) { 700// String clmKey = clmKeys[ (column-headCount)/sumCount ]; // 5.2.2.0 (2010/11/01) 上に移動 701 DBColumn clmTmp = resource.getDBColumn( clmKey ); 702 if( clmTmp == null ) { 703// tableImpl.setDBColumn( column,dbColumn[sumId] ); } 704 DBColumnConfig dbCfg2 = dbClm.getConfig(); 705 if( clmKey != null && clmKey.length() > 0 ) { // 5.2.2.0 (2010/11/01) 706 dbCfg2.setName( clmKey ); 707 dbCfg2.setLabelData( resource.getLabelData( clmKey ) ); 708 } 709 else { 710 dbCfg2.setLabelData( resource.getLabelData( gokeiLabel ) ); 711 } 712 dbClm = new DBColumn( dbCfg2 ); 713// tableImpl.setDBColumn( column, new DBColumn( dbCfg2 ) ); 714 } 715 else { 716// tableImpl.setDBColumn( column,dbClm ); 717 dbClm = clmTmp; 718 } 719 } 720// else { 721// tableImpl.setDBColumn( column,dbColumn[sumId] ); 722// } 723 724 tableImpl.setDBColumn( column,dbClm ); 725 726 sumId++; 727 if( sumId % sumCount == 0 ) { 728 sumId = 0; 729 } 730 } 731 732 // クロス集計データの作成 733 CrossMap cross = new CrossMap( clmKeys,headCount,sumCount ); 734 for( int row=0; row<rowCnt; row++ ) { 735 String[] data = table.getValues( row ); 736 cross.add( data ); 737 } 738 739 // データ部の設定 740 int size = cross.getSize(); 741 for( int row=0; row<size; row++ ) { 742 tableImpl.addValues( cross.get( row ), row ); 743 } 744 745 tableImpl.resetModify(); 746 747 final DBTableModel model ; 748 if( gokeiSortDir != null ) { 749 DBTableModelSorter temp = new DBTableModelSorter(); 750 temp.setModel( tableImpl ); 751 752 boolean direction = Boolean.valueOf( gokeiSortDir ).booleanValue(); 753 temp.sortByColumn( numberOfColumns-1,direction ); 754 model = temp ; 755 } 756 else { 757 model = tableImpl; 758 } 759 return model ; 760 } 761 762 /** 763 * 列ヘッダーのソート方法に応じた、Setオブジェクトを返します。 764 * ここでは、NUMBER , STRING , LOAD の3種類用意しています。 765 * 766 * @og.rev 3.5.6.3 (2004/07/12) 新規作成 767 * 768 * @return ソート方法に応じたSetオブジェクト 769 */ 770 private Set<String> gatSortAlgorithmSet() { 771 final Set<String> rtnSet ; 772 773 if( "LOAD".equalsIgnoreCase( cubeSortType ) ) { 774 rtnSet = new LinkedHashSet<String>(); 775 } 776 else if( "NUMBER".equalsIgnoreCase( cubeSortType ) ) { 777 rtnSet = new TreeSet<String>( numberSort ); 778 } 779 else if( "STRING".equalsIgnoreCase( cubeSortType ) ) { 780 rtnSet = new TreeSet<String>(); 781 } 782 else { 783 String errMsg = "cubeSortType は、NUMBER,STRING,LOAD 以外指定できません。" + 784 " cubeSortType=[" + cubeSortType + "]"; 785 throw new HybsSystemException( errMsg ); 786 } 787 788 return rtnSet ; 789 } 790 791 /** 792 * 表示不可カラム名を、カンマ区切りで与えます。 793 * 例:"OYA,KO,HJO,SU,DYSET,DYUPD" 794 * null を与えた場合は,なにもしません。 795 * 796 * 注意:このクラスでは、DBTableModel を作り直すタイミングが、 797 * create メソッド実行時です。(パラメータの初期化が必要な為) 798 * よって、このメソッドは、初期が終了後に、再セットします。 799 * 800 * @og.rev 3.7.0.4 (2005/03/18) 新規作成 801 * 802 * @param columnName カラム名 803 */ 804 @Override 805 public void setNoDisplay( final String columnName ) { 806 noDisplayKeys = columnName; 807 } 808 809 /** 810 * 表示可能カラム名を、カンマ区切りで与えます。 811 * 例:"OYA,KO,HJO,SU,DYSET,DYUPD" 812 * setColumnDisplay( int column,boolean rw ) の簡易版です。 813 * null を与えた場合は,なにもしません。 814 * また、全カラムについて、有効にする場合は、columnName="*" を設定します。 815 * 816 * @og.rev 5.2.2.0 (2010/11/01) 新規追加 817 * 818 * @param columnName カラム名 819 */ 820 @Override 821 public void setColumnDisplay( final String columnName ) { 822 columnDisplayKeys = columnName; 823 } 824 825 /** 826 * NUMBER ソート機能(整数限定) 内部クラス 827 * これは通常のソートではなく、ヘッダーに使うラベルのソートなので、 828 * 整数のみと限定します。実数の場合は、桁合わせ(小数点以下の桁数) 829 * されているという前提です。 830 * 831 * @og.rev 3.5.6.3 (2004/07/12) 新規作成 832 * 833 */ 834 private static final Comparator<String> numberSort = new NumberComparator(); 835 836 private static class NumberComparator implements Comparator<String>,Serializable { 837 private static final long serialVersionUID = 4000 ; // 4.0.0 (2005/01/31) 838 839 public int compare( final String s1, final String s2 ) { 840 if( s1.length() > s2.length() ) { return 1; } 841 else if( s1.length() < s2.length() ) { return -1; } 842 else { 843 return s1.compareTo( s2 ); 844 } 845 } 846 } 847 848 /** 849 * 表示項目の編集(並び替え)が可能かどうかを返します 850 * 851 * @og.rev 5.1.6.0 (2010/05/01) 新規追加 852 * 853 * @return 表示項目の編集(並び替え)が可能かどうか(false:不可能) 854 */ 855 @Override 856 public boolean isEditable() { 857 return false; 858 } 859}