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