    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.table;
    017
    018import java.util.HashMap;
    019import java.util.Map;
    020
    021import org.opengion.fukurou.util.StringUtil;
    022import org.opengion.hayabusa.db.AbstractTableFilter;
    023import org.opengion.hayabusa.db.DBColumn;
    024import org.opengion.hayabusa.db.DBColumnConfig;
    025import org.opengion.hayabusa.db.DBTableModel;
    026import org.opengion.hayabusa.db.DBTableModelUtil;
    027import org.opengion.hayabusa.resource.ResourceManager;
    028
    029/**
    030 * TableFilter_ROTATE は、TableFilter インターフェースを継承した、DBTableModel 処理用の
    031 * 実装クラスです。
    032 *
    033 * ここではテーブルの回転、及びその逆回転を行います。
    034 *
    035 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。
    036 * 【パラメータ】
    037 *  {
    038 *       KEY_CLM    : キーカラム(複数指定可)
    039 *       ROTATE_CLM : 回転するカラム
    040 *       VALUE_CLM  : 回転カラムの値
    041 *       REVERSE    : 回転(false)・逆回転(true) (任意指定 初期値:false)
    042 *       MUST_CLM   : 必須属性を定義するカラム  (任意指定 初期値:false)
    043 *       DEF_CLM    : 初期値を定義するカラム    (任意指定)
    044 *  }
    045 *
    046 *  ※ それぞれに指定されたカラム名が存在しない場合は、処理されませんのでご注意下さい。
    047 *
    048 * ①回転
    049 *  キーカラムに指定された値が同じN行を1行として回転します。
    050 *  (キーカラムの値がブレイクしたタイミングで、行を変更します)
    051 *  このN行に含まれる回転カラムの値がカラム名に、回転カラム値が各カラムの値になります。
    052 *  キーカラムは、カンマ区切りで複数指定可能です。
    053 *
    054 *  生成されたテーブルモデルのカラムは、始めのMカラムがキーカラムに、その後ろのNカラムが
    055 *  回転されたカラムになります。
    056 *
    057 *  また、元テーブルにMUST_CLMにより、各カラムの必須属性を定義することが
    058 *  できます。(MUST属性は、'1'又は'true'の場合に必須になります。)
    059 *
    060 * ②逆回転
    061 *  回転時の逆の挙動になります。
    062 *  "キーカラムに指定されたカラム以外"を回転カラムで指定されたカラムの値として分解します。
    063 *  各回転カラムの値は、回転カラム値に指定されたカラムに格納されます。
    064 *
    065 *  分解後のカラム数は、キーカラム数 + 2 (回転カラム、回転カラム値)になります。
    066 *  また、行数は、(分解前の行数) x (回転カラム数)になります。
    067 *
    068 * @og.formSample
    069 * ●形式：
    070 *      ① &lt;og:tableFilter classId="ROTATE" keys="KEY_CLM,REVERSE" vals='"AA,BB,CC",true' /&gt;
    071 *
    072 *      ② &lt;og:tableFilter classId="ROTATE" &gt;
    073 *               {
    074 *                   KEY_CLM    : キーカラム(複数指定可)
    075 *                   ROTATE_CLM : 回転するカラム
    076 *                   VALUE_CLM  : 回転カラムの値
    077 *                   REVERSE    : 回転(false)・逆回転(true) (任意指定 初期値:false)
    078 *                   MUST_CLM   : 必須属性を定義するカラム  (任意指定 初期値:false)
    079 *                   DEF_CLM    : 初期値を定義するカラム    (任意指定)
    080 *               }
    081 *         &lt;/og:tableFilter&gt;
    082 *
    083 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加
    084 *
    085 * @version  0.9.0  2000/10/17
    086 * @author   Hiroki Nakamura
    087 * @since    JDK1.1,
    088 */
    089public class TableFilter_ROTATE extends AbstractTableFilter {
    090        // * このプログラムのVERSION文字列を設定します。 {@value} */
    091        private static final String VERSION = "5.6.6.1 (2013/07/12)" ;
    092
    093        /**
    094         * keys の整合性チェックを行うための初期設定を行います。
    095         *
    096         * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェック対応
    097         *
    098         * @param       keysMap keys の整合性チェックを行うための Map
    099         */
    100        @Override
    101        protected void init( final Map<String,String> keysMap ) {
    102                keysMap.put( "KEY_CLM"          , "キーカラム(複数指定可)"                                        );
    103                keysMap.put( "ROTATE_CLM"       , "回転するカラム"                                                     );
    104                keysMap.put( "VALUE_CLM"        , "回転カラムの値"                                                     );
    105                keysMap.put( "REVERSE"          , "回転(false)/逆回転(true) (初期値:false)"     );
    106                keysMap.put( "MUST_CLM"         , "必須属性を定義するカラム (初期値:false)"    );
    107                keysMap.put( "DEF_CLM"          , "初期値を定義するカラム"                                 );
    108        }
    109
    110        private DBTableModel    table    = null;                        // 5.5.2.6 (2012/05/25) 共通に使うため、変数定義
    111        private ResourceManager resource = null;                        // 5.5.2.6 (2012/05/25) 共通に使うため、変数定義
    112
    113        /**
    114         * DBTableModel処理を実行します。
    115         *
    116         * @og.rev 4.3.7.4 (2009/07/01) 新規追加
    117         * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更
    118         *
    119         * @return 処理結果のDBTableModel
    120         */
    121        public DBTableModel execute() {
    122                boolean reverse = StringUtil.nval( getValue( "REVERSE" ), false );
    123
    124                table    = getDBTableModel();           // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
    125                resource = getResource();                       // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
    126
    127                // 逆回転
    128                if ( reverse ) {
    129                        return getRecoverdTable();
    130                }
    131                // 回転
    132                else {
    133                        return getRotateTable();
    134                }
    135        }
    136
    137        /**
    138         * 回転後のDBTableModelを返します。
    139         *
    140         * @og.rev 5.1.8.0 (2010/07/01) メソッド名変更(setDefValue ⇒ setDefault)
    141         *
    142         * @return 回転後のDBTableModel
    143         */
    144        private DBTableModel getRotateTable() {
    145                String[] keyClm = StringUtil.csv2Array( getValue( "KEY_CLM" ) );
    146                int rotateNo = table.getColumnNo( getValue( "ROTATE_CLM" ), false );
    147                int valNo = table.getColumnNo( getValue( "VALUE_CLM" ), false );
    148
    149                if ( keyClm == null || keyClm.length == 0 || rotateNo < 0 || valNo < 0 ) {
    150                        return table;
    151                }
    152
    153                int mustNo = table.getColumnNo( getValue( "MUST_CLM"), false );
    154                int defNo = table.getColumnNo( getValue( "DEF_CLM"), false );
    155
    156                int clmCount = 0; // 回転後のカラム数
    157                // キーカラムのカラム番号を求め、カラム数としてカウントします。
    158                Map<String, Integer> clmMap = new HashMap<String, Integer>();
    159                int[] keyNos = new int[keyClm.length];
    160                for ( int i = 0; i < keyNos.length; i++ ) {
    161                        keyNos[i] = table.getColumnNo( keyClm[i], false );
    162                        if ( keyNos[i] < 0 ) {
    163                                return table;
    164                        }
    165                        clmMap.put( keyClm[i], clmCount );
    166                        clmCount++;
    167                }
    168
    169                int rowCount = 0; // 回転後の行数
    170                // 回転カラムの値から回転後のカラム数を求めます。
    171                // また同時に、キーカラムの値のブレイク数により行数を求めます。
    172                Map<String, Integer> rowMap = new HashMap<String, Integer>();
    173                Map<String, Boolean> mustMap = new HashMap<String, Boolean>();
    174                Map<String, String> defaultMap = new HashMap<String, String>();
    175                for ( int i = 0; i < table.getRowCount(); i++ ) {
    176                        String clmKey = table.getValue( i, rotateNo );
    177                        if ( clmMap.get( clmKey ) == null ) {
    178                                clmMap.put( clmKey, clmCount );
    179                                clmCount++;
    180                        }
    181                        // 必須カラム抜き出し
    182                        if( mustNo > -1 && StringUtil.nval( table.getValue( i, mustNo ), false ) ) {
    183                                mustMap.put( clmKey, true );
    184                        }
    185                        // デフォルト値を書き換えるカラムの抜き出し
    186                        if( defNo > -1 && table.getValue( i, defNo ) != null && table.getValue( i, defNo ).length() > 0 ) {
    187                                defaultMap.put( clmKey, table.getValue( i, defNo ) );
    188                        }
    189
    190                        String rowKey = getSeparatedValue( i, keyNos );
    191                        if ( rowKey != null && rowKey.length() > 0 ) {
    192                                if ( rowMap.get( rowKey ) == null ) {
    193                                        rowMap.put( rowKey, rowCount );
    194                                        rowCount++;
    195                                }
    196                        }
    197                }
    198
    199                // 回転後のカラム一覧よりDBTableModelを初期化します。
    200                String names[] = new String[clmMap.size()];
    201                for ( Map.Entry<String, Integer> entry : clmMap.entrySet() ) {
    202                        names[entry.getValue()] = entry.getKey();
    203                }
    204                DBTableModel nTable = DBTableModelUtil.newDBTable();
    205                nTable.init( names.length );
    206                for ( int i = 0; i < names.length; i++ ) {
    207                        if( mustMap.get( names[i] ) != null ) {
    208                                table.addMustType( i, "must" );
    209                        }
    210                        DBColumn column = resource.makeDBColumn( names[i] );
    211                        if( defaultMap.get( names[i] ) != null ) {
    212                                DBColumnConfig dbConfig = column.getConfig();
    213//                              dbConfig.setDefValue( defaultMap.get( names[i] ) );
    214                                dbConfig.setDefault( defaultMap.get( names[i] ) );              // 5.1.8.0 (2010/07/01)
    215                                column = new DBColumn( dbConfig );
    216                        }
    217//                      nTable.setDBColumn( i, resource.makeDBColumn( names[i] ) );
    218                        nTable.setDBColumn( i, column );                                                        // 5.1.8.0 (2010/07/01)
    219                }
    220
    221                // 値の一覧を作成し、DBTableModelに値をセットします。
    222                if( rowCount > 0 ) {
    223                        String[][] vals = new String[rowCount][names.length];
    224                        for ( int i = 0; i < table.getRowCount(); i++ ) {
    225                                int row = rowMap.get( getSeparatedValue( i, keyNos ) );
    226                                int clm = clmMap.get( table.getValue( i, rotateNo ) );
    227
    228                                for ( int j = 0; j < keyNos.length; j++ ) {
    229                                        vals[row][j] = table.getValue( i, keyNos[j] );
    230                                }
    231                                vals[row][clm] = table.getValue( i, valNo );
    232                        }
    233                        for ( int i = 0; i < vals.length; i++ ) {
    234                                nTable.addColumnValues( vals[i] );
    235                        }
    236                }
    237
    238                return nTable;
    239        }
    240
    241        /**
    242         * 各行のキーとなるキーカラムの値を連結した値を返します。
    243         *
    244         * @param       row             行番号
    245         * @param       clmNo   カラム番号配列
    246         *
    247         * @return      各行のキーとなるキーカラムの値を連結した値
    248         */
    249        private String getSeparatedValue( final int row, final int[] clmNo ) {
    250                StringBuilder buf = new StringBuilder();
    251                for ( int i = 0; i < clmNo.length; i++ ) {
    252                        String val = table.getValue( row, clmNo[i] );
    253                        if( val != null && val.length() > 0 ) {
    254                                if( i > 0 ) {
    255                                        buf.append( "__" );
    256                                }
    257                                buf.append( val );
    258                        }
    259                }
    260                return buf.toString();
    261        }
    262
    263        /**
    264         * 逆回転後のDBTableModelを返します。
    265         *
    266         * @return 逆回転後のDBTableModel
    267         */
    268        private DBTableModel getRecoverdTable() {
    269                String[] keyClm = StringUtil.csv2Array( getValue( "KEY_CLM" ) );
    270                String rotateClm = getValue( "ROTATE_CLM" );
    271                String valClm = getValue( "VALUE_CLM" );
    272
    273                if ( keyClm == null || keyClm.length == 0 || rotateClm == null || rotateClm.length() == 0
    274                                || valClm == null || valClm.length() == 0 ) {
    275                        return table;
    276                }
    277
    278                // キーカラムのカラム番号を求めます。
    279                int[] keyNos = new int[keyClm.length];
    280                for ( int i = 0; i < keyNos.length; i++ ) {
    281                        keyNos[i] = table.getColumnNo( keyClm[i], false );
    282                        if ( keyNos[i] < 0 ) {
    283                                return table;
    284                        }
    285                }
    286
    287                // キーカラム以外(回転カラム以外)のカラム番号を求めます。
    288                int clmIdx = 0;
    289                int[] clmNos = new int[table.getColumnCount() - keyNos.length];
    290                for ( int i = 0; i < table.getColumnCount(); i++ ) {
    291                        boolean isClm = true;
    292                        for ( int j = 0; j < keyNos.length; j++ ) {
    293                                if ( i == keyNos[j] ) {
    294                                        isClm = false;
    295                                }
    296                        }
    297                        if ( isClm ) {
    298                                clmNos[clmIdx] = i;
    299                                clmIdx++;
    300                        }
    301                }
    302
    303                // テーブルモデルを初期化します。
    304                DBTableModel nTable = DBTableModelUtil.newDBTable();
    305                nTable.init( keyNos.length + 2 );
    306                for ( int i = 0; i < keyNos.length; i++ ) {
    307                        nTable.setDBColumn( i, resource.makeDBColumn( keyClm[i] ) );
    308                }
    309                nTable.setDBColumn( keyNos.length, resource.makeDBColumn( rotateClm ) );
    310                nTable.setDBColumn( keyNos.length + 1, resource.makeDBColumn( valClm ) );
    311
    312                // 各行を作成し、DBTableModelに登録します。
    313                for ( int i = 0; i < table.getRowCount(); i++ ) {
    314                        for ( int j = 0; j < clmNos.length; j++ ) {
    315                                String[] vals = new String[keyNos.length + 2];
    316                                for ( int k = 0; k < keyNos.length; k++ ) {
    317                                        vals[k] = table.getValue( i, keyNos[k] );
    318                                }
    319                                vals[keyNos.length] = table.getColumnName( clmNos[j] );
    320                                vals[keyNos.length + 1] = table.getValue( i, clmNos[j] );
    321                                nTable.addColumnValues( vals );
    322                        }
    323                }
    324
    325                return nTable;
    326        }
    327
    328}