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の値 < row2の値 : ?
096             * row1の値 > 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の値 < row2の値 : ?           正
138             * row1の値 > 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    }