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     */
016    package org.opengion.hayabusa.db;
017    
018    import org.opengion.hayabusa.common.HybsSystem;
019    import org.opengion.fukurou.util.LogWriter;
020    import org.opengion.fukurou.util.StringUtil;
021    
022    import java.util.List;
023    import java.util.ArrayList;
024    
025    /**
026     * DBTableModelを継承した TableModelのソート機è?の実è£?‚¯ラスですã?
027     *
028     * ViewFormのヘッãƒ??リンクをクリãƒ?‚¯すると、そのé ?›®につã�?�¦再ソートしますã?
029     * これは、データベã?スではなくã?メモリのDBTableModelにソート用のModelã‚?
030     * 用意し、そのModelの行番号のみをソートし、行変換を行いますã?
031     * ソートを利用するかどã�?�‹は、シスãƒ?ƒ パラメータ の、VIEW_USE_TABLE_SORTER 属æ?で
032     * æŒ?®šしますã?(å†?ƒ¨ シスãƒ?ƒ パラメータ では、false 設å®?
033     * ヘッãƒ??部に表示するリンクは、command=VIEW&h_sortColumns=XXXXX で、カラãƒ?��を指定しますã?
034     * ※ h_sortColumns 部は、HybsSystemにて定義しますã?でä¸?ˆ¬のJSPでは使用しなã�?�§下さã�??
035     *
036     * DBTableModel インターフェースは?Œデータベã?スの検索結果(Resultset)をラãƒ??する
037     * インターフェースとして使用して下さã�??
038     *
039     * @og.rev 3.5.4.7 (2004/02/06) 新規登録
040     * @og.group ãƒ??ブル管ç�?
041     *
042     * @version  4.0
043     * @author   Kazuhiko Hasegawa
044     * @since    JDK5.0,
045     */
046    public class DBTableModelSorter extends DBTableModelImpl {
047            private int[]           indexes;
048            private int                     sortingColumn ;
049            private boolean         ascending = true;
050            private int                     lastColumNo     = -1;
051            private boolean         isNumberType = false;           // 3.5.6.3 (2004/07/12)
052    
053            /**
054             * DBTableModel を設定し、このオブジェクトを初期化しますã?
055             *
056             * @param   model DBTableModelオブジェクãƒ?
057             */
058            public void setModel( final DBTableModel model ) {
059                    DBTableModelImpl impl = (DBTableModelImpl)model;
060                    dbColumns       = impl.dbColumns;
061                    names           = impl.names;
062                    data            = impl.data;
063                    rowHeader       = impl.rowHeader;
064                    columnMap       = impl.columnMap;
065                    overflow        = impl.overflow;
066                    numberOfColumns = impl.numberOfColumns;
067    
068                    // 3.5.5.5 (2004/04/23) 整合æ?キー(オブジェクトã?作æ?時刻)追åŠ?
069                    consistencyKey  = impl.consistencyKey;
070    
071                    lastColumNo = -1;
072                    reallocateIndexes();
073            }
074    
075            /**
076             * 行番号インãƒ?ƒƒクスをå?期化しますã?
077             * 行番号をそのままã€??番に設定しますã?
078             *
079             */
080            private void  reallocateIndexes() {
081                    int rowCount = super.getRowCount();
082                    indexes = new int[rowCount];
083    
084                    for(int row = 0; row < rowCount; row++) {
085                            indexes[row] = row;
086                    }
087            }
088    
089            /**
090             * 同ä¸?‚«ラãƒ?•ª号に対する、行1と行2ã?値の大小を比è¼?�—ますã?
091             * 比è¼?™‚に、そのカラãƒ?�Œ、NUMBERタイプã?場合ã?、Double に変換後ã?数字として
092             * 比è¼?�—ますã?それ以外ã?場合ã?、文字å?の比è¼? row1の値.compareTo(s2) )の
093             * 値を返しますã?
094             *
095             * row1の値 &lt; row2の値 : è²?
096             * row1の値 &gt; row2の値 : 正
097             * row1の値 == row2の値 : 0
098             *
099             * @og.rev 3.5.6.3 (2004/07/12) isNumberType 属æ?を使用するã€?
100             *
101             * @param   row1        比è¼??の行番号
102             * @param   row2        比è¼??の行番号
103             * @param   column      比è¼?�™るカラãƒ?•ª号
104             *
105             * @return      比è¼?µ�果[è²?0/正]
106             */
107            private int compareRowsByColumn( final int row1, final int row2, final int column ) {
108    
109                    String s1 = super.getValue(row1, column);
110                    String s2 = super.getValue(row2, column);
111    
112                    if( isNumberType ) {
113                            // 3.5.6.3 (2004/07/12) 数字型で ゼロæ–?­—å?時ã?処ç�?
114                            if( s1.length() == 0 || s2.length() == 0 ) {
115                                    return ( s1.length() - s2.length() );
116                            }
117    
118                            double d1 = StringUtil.parseDouble( s1 );
119                            double d2 = StringUtil.parseDouble( s2 );
120    
121                            // 注意:引き算をすると、桁あふれする可能性があるためã?比è¼?�™るã?
122                            if(d1 < d2) {                return -1; }
123                            else if(d1 > d2) { return 1;  }
124                            else {                          return 0;  }
125                    }
126                    else {
127                            return s1.compareTo(s2);
128                    }
129            }
130    
131            /**
132             * å†?ƒ¨æŒ?®šã?カラãƒ?sortingColumn)に対する、行1と行2ã?値の大小を比è¼?�—ますã?
133             * 比è¼??ç�??、compareRowsByColumn( int,int,int ) を使用しますã?
134             * ascending フラグ[true:æ˜??/false:降é?] にしたがって、結果を反転しますã?
135             *
136             * ascending == true のæ™?       ascending == false のæ™?
137             * row1の値 &lt; row2の値 : è²?           正
138             * row1の値 &gt; row2の値 : 正            è²?
139             * row1の値 == row2の値 : 0             0
140             *
141             * @param       row1    比è¼??の行番号
142             * @param       row2    比è¼??の行番号
143             *
144             * @return      比è¼?µ�果[è²?0/正]
145             * @see     #compareRowsByColumn( int,int,int )
146             */
147            private int compare( final int row1, final int row2 ) {
148                    int result = compareRowsByColumn(row1, row2, sortingColumn);
149    
150                    if(result != 0) {
151                            return ascending ? result : -result;
152                    }
153                    return 0;
154            }
155    
156            /**
157             * ソートするå?部ãƒ??タが不整合を起こしてã�?‚‹かチェãƒ?‚¯しますã?
158             * å†?ƒ¨行番号と、テーブルオブジェクトã?件数を比è¼?�—ますã?
159             *
160             * @og.rev 3.5.6.3 (2004/07/12) チェãƒ?‚¯エラー時にアベンドせずに再設定するã?
161             */
162            private void checkModel() {
163                    if(indexes.length != super.getRowCount()) {
164                            String errMsg = "å†?ƒ¨行番号と、テーブルオブジェクトã?件数が不ä¸??ですã? " + HybsSystem.CR
165                                            + "Index Length=[" + indexes.length + "] , Table Row Count=[" + super.getRowCount() + "]";
166                            LogWriter.log( errMsg );
167                            reallocateIndexes();
168                    }
169            }
170    
171            /**
172             * ソートå?ç�??トップメソãƒ?ƒ‰ですã?
173             *
174             */
175            private void  sort() {
176                    checkModel();
177    
178                    reallocateIndexes();
179                    shuttlesort(indexes.clone(), indexes, 0, indexes.length);
180    
181                    int rowCount = indexes.length;
182    
183                    List<String[]>            newData          = new ArrayList<String[]>( rowCount );
184                    List<DBRowHeader> newRowHeader = new ArrayList<DBRowHeader>( rowCount );
185    
186                    for( int row=0; row<rowCount; row++ ) {
187                            newData.add( row,data.get( indexes[row] ) );
188                            newRowHeader.add( row,rowHeader.get( indexes[row] ) );
189                    }
190                    data      = newData;
191                    rowHeader = newRowHeader;
192            }
193    
194            /**
195             * シャトルソートを行いますã?
196             *
197             * @param       from    ソートå?配å?
198             * @param       to              ソートå?配å?
199             * @param       low             ç¯?›²(下ä½?
200             * @param       high    ç¯?›²(上ä½?
201             */
202            private void shuttlesort( final int[] from, final int[] to, final int low, final int high ) {
203                    if(high - low < 2) {
204                            return;
205                    }
206                    int middle = (low + high) >>> 1;       // widely publicized the bug pattern.
207                    shuttlesort(to, from, low, middle);
208                    shuttlesort(to, from, middle, high);
209    
210                    int pp = low;
211                    int qq = middle;
212    
213                    if(high - low >= 4 && compare(from[middle-1], from[middle]) <= 0) {
214                            for(int i = low; i < high; i++) {
215                                    to[i] = from[i];
216                            }
217                            return;
218                    }
219    
220                    for(int i = low; i < high; i++) {
221                            if(qq >= high || (pp < middle && compare(from[pp], from[qq]) <= 0)) {
222                                    to[i] = from[pp++];
223                            }
224                            else {
225                                    to[i] = from[qq++];
226                            }
227                    }
228            }
229    
230            /**
231             * カラãƒ?¯Žソートã?トップメソãƒ?ƒ‰ですã?
232             * ãƒ?ƒ•ォルトで、æ?é ?‚½ートを行いますã?
233             * æœ?¾Œにソートしたカラãƒ?�¨同ä¸??カラãƒ?�ŒæŒ?®šされた場合ã?æ˜??と降é?ã‚?
234             * 反転させて、å?度ソートを行いますã?(シャトルソーãƒ?
235             *
236             * @param column    カラãƒ?•ª号
237             */
238            public void sortByColumn( final int column ) {
239                    if( lastColumNo == column ) {
240                            ascending = !ascending ;
241                    }
242                    else {
243                            ascending = true;
244                    }
245                    sortByColumn( column,ascending );
246            }
247    
248            /**
249             * カラãƒ?¯Žソートã?トップメソãƒ?ƒ‰ですã?
250             * ascending フラグ[true:æ˜??/false:降é?]を指定しますã?
251             *
252             * @og.rev 3.5.6.3 (2004/07/12) isNumberType 属æ?を設定するã?
253             * @og.rev 4.0.0.0 (2005/01/31) getColumnClassName å»?­¢ã€?BColumから取得するã?
254             *
255             * @param column    カラãƒ?•ª号
256             * @param ascending  ソートã?方向[true:æ˜??/false:降é?]
257             */
258            public void sortByColumn( final int column, final boolean ascending ) {
259                    this.ascending = ascending;
260                    sortingColumn = column;
261                    isNumberType = "NUMBER".equals( getDBColumn(sortingColumn).getClassName() );
262                    sort();
263                    lastColumNo = column;
264            }
265    
266            /**
267             * ソートã?方å�?æ˜??:true/降é?:false)を取得しますã?
268             *
269             * @return  ソートã?方å�?[true:æ˜??/false:降é?]
270             */
271            public boolean isAscending() {
272                    return ascending;
273            }
274    }