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.html; 017 018import java.util.concurrent.ConcurrentMap; // 6.4.3.3 (2016/03/04) 019import java.util.concurrent.ConcurrentHashMap; // 6.4.3.1 (2016/02/12) refactoring 020import java.util.List; 021import java.util.ArrayList; 022import java.util.Arrays; 023 024import org.opengion.hayabusa.common.HybsSystemException; // 6.4.3.3 (2016/03/04) 025import org.opengion.fukurou.util.StringUtil ; 026import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 027 028/** 029 * String[] 型キーにカラム列の連想記憶を用いた、クロス集計データを管理するクラスです。 030 * 031 * クロス集計では、カラム列が、データとして与えられる為、このクラス内部で、 032 * 一旦カラム列の連想記憶(Map)データを作成し、実際の行データ登録時にデータを 033 * 設定しています。 034 * 取り出すときは、一気に取り出すことを考慮して、配列(ArrayList)データに 035 * 共有しているオブジェクトを取り出します。 036 * 037 * この実装は同期化されません。 038 * 039 * @og.rev 3.5.4.0 (2003/11/25) 新規作成 040 * @og.group 画面表示 041 * 042 * @version 4.0 043 * @author Kazuhiko Hasegawa 044 * @since JDK5.0, 045 */ 046public final class CrossMap { 047 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 048 private final ConcurrentMap<String,String[]> rowMap = new ConcurrentHashMap<>() ; 049 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 050 private final ConcurrentMap<String,Integer> clmMap = new ConcurrentHashMap<>() ; 051 private final List<String[]> list = new ArrayList<>(); 052 private final int headCount; 053 private final int sumCount; 054 private final int totalCols; 055 056 /** 057 * カラム部(クロス集計部)を与えて新しく作成するコンストラクター 058 * 059 * クロス集計を行うカラム部のみセットします。 060 * 行のクロス集計部のヘッダーキーは、引数の配列の順番で、設定されます。 061 * この行のヘッダー部となるデータは、addData 時にセットします。 062 * 063 * @param clmData クロス集計部のカラム名配列 064 * @param headCount HEADカラムの数 065 * @param sumCount 合計カラムの数 066 */ 067 public CrossMap( final String[] clmData, final int headCount, final int sumCount ) { 068 if( headCount <= 0 ) { 069 final String errMsg = "headCount は、ROWカラムを含むため、最低1以上必要です。"; 070 throw new IllegalArgumentException( errMsg ); 071 } 072 073 this.headCount = headCount; 074 this.sumCount = sumCount; 075 final int clmNum = clmData.length; 076 totalCols = headCount + clmNum * sumCount; 077 for( int i=0; i<clmNum; i++ ) { 078 // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing 079// clmMap.put( clmData[i],Integer.valueOf( i ) ); 080 clmMap.put( clmData[i],i ); 081 } 082 } 083 084 /** 085 * クロス集計の元となる検索結果の行データを登録します。 086 * 087 * クロス集計を行うカラム部のみセットします。 088 * 行のヘッダー部となるデータは、rowKeys と headCount で指定します。 089 * 行のクロス集計部のヘッダーキーは、clmKey で指定し、内部で、列カラムとして 090 * 割り当てられます。割り当て順(カラム順)は、コンストラクタでの clmData の 091 * 配列の順番です。 092 * 093 * @og.rev 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。 094 * @og.rev 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限のチェック追加 095 * 096 * @param rowKeys 行データの配列(可変長引数)( 0~headCount の値が行のキーとなります。) 097 */ 098 public void add( final String... rowKeys ) { 099 if( rowKeys.length < headCount + 1 + sumCount ) { 100 final String errMsg = "指定の rowKeys の個数が不正です。 rowKeys には、clmKey と data が必要です。" 101 + " rowKeys=" + StringUtil.array2csv( rowKeys ) ; // 5.1.8.0 (2010/07/01) errMsg 修正 102 throw new ArrayIndexOutOfBoundsException( errMsg ); 103 } 104 105 // 3.5.6.6 (2004/08/23) 修正 106// final String clmKey = rowKeys[headCount]; // カラム列のクロス集計部のキー(ヘッダー) 107 final String[] data = new String[sumCount]; // クロス集計表の値 // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 108 for( int i=0; i<sumCount; i++ ) { 109 data[i] = rowKeys[headCount+1+i]; 110 } 111 112 final String rowKey ; 113 // 3.5.6.6 (2004/08/23) 修正 114 if( headCount == 1 ) { rowKey = rowKeys[0]; } 115 else { 116 final StringBuilder rKey = new StringBuilder( BUFFER_MIDDLE ); 117 for( int i=0; i<headCount; i++ ) { 118 rKey.append( rowKeys[i] ).append( '_' ); // 6.0.2.5 (2014/10/31) char を append する。 119 } 120 rowKey = rKey.toString(); 121 } 122 123 // 6.4.3.3 (2016/03/04) ConcurrentHashMap の not null制限 124 if( rowKey == null ) { 125 final String errMsg = "指定の rowKey が、null です。"; 126 throw new HybsSystemException( errMsg ); 127 } 128 129 String[] clmData = rowMap.get( rowKey ); 130 if( clmData == null ) { 131 // 行データ+クロス行データ 132 clmData = new String[totalCols]; 133 Arrays.fill( clmData,"" ); 134 // 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。 135 System.arraycopy( rowKeys,0,clmData,0,headCount ); // 6.3.6.0 (2015/08/16) 136 list.add( clmData ); // 生成順にArrayList にセーブします。 137 } 138 139 final String clmKey = rowKeys[headCount]; // カラム列のクロス集計部のキー(ヘッダー) 6.9.7.0 (2018/05/14) PMD before a possible exit point. 140 for( int i=0; i<sumCount; i++ ) { 141 // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing 142// final int no = headCount + clmMap.get( clmKey ).intValue()*sumCount+i; // 列番号 143 final int no = headCount + clmMap.get( clmKey ) * sumCount + i; // 列番号 144 clmData[no] = data[i]; 145 } 146 rowMap.put( rowKey,clmData ); // ArrayList と同じオブジェクトを検索用のMapにもセットする。 147 } 148 149 /** 150 * クロス集計結果の指定行の列データを返します。 151 * 152 * @param row 指定の行番号( 0 .. getSize()-1 ) 153 * 154 * @return 列データ配列 155 */ 156 public String[] get( final int row ) { 157 return list.get( row ); 158 } 159 160 /** 161 * クロス集計結果の行数を返します。 162 * 163 * @return 行数を返します。 164 */ 165 public int getSize() { 166 return list.size(); 167 } 168}