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.fukurou.db;
017    
018    import java.io.IOException;
019    import java.io.Reader;
020    import java.sql.Clob;
021    import java.sql.ResultSet;
022    import java.sql.ResultSetMetaData;
023    import java.sql.SQLException;
024    import java.sql.Types;
025    import java.sql.Date;
026    import java.sql.Timestamp;
027    import java.util.Locale;
028    
029    import org.opengion.fukurou.util.Closer;
030    import org.opengion.fukurou.util.HybsDateUtil;
031    import static org.opengion.fukurou.util.HybsConst.CR;                           // 6.1.0.0 (2014/12/26) refactoring
032    
033    /**
034     * ResultSet のãƒ??タ処ç�?‚’まとめたクラスですã?
035     * ここでは、ResultSetMetaData から、カラãƒ?•°、カラãƒ?��(NAMEåˆ?ã€?
036     * Type属æ?を取得し、ResultSet で、å?を求める時に、Object型ã?
037     * 処ç�?‚’行いますã?
038     * Object型としては、CLOB、ROWID、TIMESTAMP 型ã?み取り扱ってã�?�¾すã?
039     *
040     * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
041     * @og.group ?¤?¢制御
042     *
043     * @version  6.0
044     * @author   Kazuhiko Hasegawa
045     * @since    JDK6.0,
046     */
047    public class ResultSetValue {
048    
049    //      /** シスãƒ?ƒ 依存ã?改行記号をセãƒ?ƒˆしますã?4.0.0.0(2007/10/17) */
050    //      private static final String CR = System.getProperty( "line.separator" );
051    
052            private final ResultSet resultSet ;                     // å†?ƒ¨で管ç�?�™ã‚?ResultSet オブジェクãƒ?
053            private final int               clmSize ;                       // カラãƒ?‚µイズ
054            private final String[]  names ;                         // カラãƒ?��(ResultSetMetaData#getColumnLabel(int) の toUpperCase)
055            private final int[]             type ;                          // java.sql.Types の定数定義
056            private final boolean   useObj ;                        // オブジェクト型の Type が存在するかどã�?�‹ã€?
057    
058            private final int[]             size ;                          // カラãƒ?‚µイズ(ResultSetMetaData#getColumnDisplaySize(int))
059            private final boolean[] isWrit ;                        // 書き込み許可(ResultSetMetaData#isWritable(int))
060    
061            /**
062             * ResultSet を引数にとるコンストラクタ
063             *
064             * ここで、カラãƒ?‚µイズ、カラãƒ?��、java.sql.Types の定数定義 を取得しますã?
065             *
066             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
067             *
068             * @param       res å†?ƒ¨で管ç�?�™るResultSetオブジェクãƒ?
069             * @throws      java.sql.SQLException ãƒ??タベã?ス・アクセス・エラーが発生したå?å�?
070             */
071            public ResultSetValue( final ResultSet res ) throws SQLException {
072                    resultSet = res;
073    
074                    final ResultSetMetaData metaData  = resultSet.getMetaData();
075                    clmSize = metaData.getColumnCount();
076                    names   = new String[clmSize];
077                    type    = new int[clmSize];
078                    size    = new int[clmSize];
079                    isWrit  = new boolean[clmSize];
080    
081                    boolean tempUseObj = false;                                             // そもそも、オブジェクト系のカラãƒ?�Œあるかどã�?�‹
082                    for( int i=0; i<clmSize; i++ ) {
083                            final int tp = metaData.getColumnType( i+1 );
084                            type[i] = tp ;
085                            if( tp == Types.CLOB || tp == Types.ROWID || tp == Types.TIMESTAMP ) { tempUseObj = true; }
086                            names[i]  = metaData.getColumnLabel(i+1).toUpperCase(Locale.JAPAN) ;
087                            size[i]   = metaData.getColumnDisplaySize(i+1) ;
088                            isWrit[i] = metaData.isWritable(i+1) ;
089                    }
090                    useObj = tempUseObj ;
091            }
092    
093            /**
094             * ResultSetMetaData で求めたã?カラãƒ?•°を返しますã?
095             *
096             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
097             *
098             * @return  カラãƒ?•°(ãƒ??タの列数)
099             */
100            public int getColumnCount() { return clmSize ; }
101    
102            /**
103             * カラãƒ?��配å?を返しますã?
104             *
105             * 配å?はã€?から始まりã?カラãƒ?•°-1 までのæ–?­—型配å?に設定されますã?
106             * カラãƒ?��は、ResultSetMetaData#getColumnLabel(int) ã‚?toUpperCase した
107             * 大æ–?­—が返されますã?
108             *
109             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
110             *
111             * @return      カラãƒ?��配å?
112             * @og.rtnNotNull
113             */
114            public String[] getNames() {
115                    return names.clone();
116            }
117    
118            /**
119             * æŒ?®šã?カラãƒ?•ª号のカラãƒ?��を返しますã?
120             *
121             * カラãƒ?��を取得するã?カラãƒ?•ª号はã€?から始まりã?カラãƒ?•°-1 までの数字でæŒ?®šしますã?
122             * ãƒ??タベã?ス上ã?ã€?から始まる番号とは、異なりますã?
123             * カラãƒ?��は、ResultSetMetaData#getColumnLabel(int) ã‚?toUpperCase した
124             * 大æ–?­—が返されますã?
125             *
126             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
127             *
128             * @param   clmNo  カラãƒ?•ª号 (0から始まりã?カラãƒ?•°-1までの数å­?
129             * @return  æŒ?®šã?カラãƒ?•ª号のカラãƒ?��
130             */
131            public String getColumnName( final int clmNo ) {
132                    return names[clmNo];
133            }
134    
135    //      /**
136    //       * java.sql.Types の定数定義配å?を返しますã?
137    //       *
138    //       * 配å?はã€?から始まりã?カラãƒ?•°-1 までのint型é?列に設定されますã?
139    //       * Types の定数定義は、ResultSetMetaData#getColumnType(int) の値ですã?
140    //       *
141    //       * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
142    //       *
143    //       * @return      カラãƒ?0?žカラãƒ?•°-1)に対応したã?Types定数定義配å?
144    //       */
145    //      public int[] getSqlTypes() {
146    //              return type.clone();
147    //      }
148    
149            /**
150             * æŒ?®šã?カラãƒ?•ª号のjava.sql.Types の定数定義を返しますã?
151             *
152             * 配å?はã€?から始まりã?カラãƒ?•°-1 までのint型é?列に設定されますã?
153             * Types の定数定義は、ResultSetMetaData#getColumnType(int) の値ですã?
154             *
155             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
156             *
157             * @param   clmNo  カラãƒ?•ª号 (0から始まりã?カラãƒ?•°-1までの数å­?
158             * @return      æŒ?®šã?カラãƒ?•ª号のTypes定数
159             */
160            public int getSqlType( final int clmNo ) {
161                    return type[clmNo];
162            }
163    
164    //      /**
165    //       * オブジェクト型の Type が存在するかどã�?�‹[true:存在する/false:存在しない]を返しますã?
166    //       *
167    //       * 基本çš?�«は、å?を求めるå?ç�??å†?ƒ¨ロジãƒ?‚¯でのみ使用しますã?
168    //       * Types.CLOB 、Types.ROWID、Types.TIMESTAMP がã?Types定義に含まれてã�?‚‹場合ã?
169    //       * true を返しますã?
170    //       * 含まれなã�??合ã?、false ですã?
171    //       * オブジェクト型 が存在する場合ã?値を求めるå?ç�?�«、専用のメソãƒ?ƒ‰がå¿?¦�になりますã?
172    //       * 含まれなã�??合ã?、String.valueOf( Object ) で求めることが可能ですã?
173    //       * こã?値を参è€?�«、呼び出すメソãƒ?ƒ‰を変えることで、å?ç�??高é?化を図ろうと
174    //       * è€?�ˆてã�?�¾すã?(効果があるかどã�?�‹は、判りませんã€?
175    //       *
176    //       * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
177    //       *
178    //       * @return  オブジェクト型の Type が存在するかどã�?�‹[true:存在する/false:存在しない]
179    //       */
180    //      public boolean useObjectType() {
181    //              return useObj;
182    //      }
183    
184            /**
185             * カラãƒ??サイズのint配å?を返しますã?
186             *
187             * 配å?はã€?から始まりã?カラãƒ?•°-1 までのint型é?列に設定されますã?
188             * カラãƒ??サイズは、ResultSetMetaData#getColumnDisplaySize(int) の値ですã?
189             *
190             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
191             *
192             * @return      カラãƒ??サイズのint配å?
193             * @og.rtnNotNull
194             */
195            public int[] getColumnDisplaySizes() {
196                    return size.clone();
197            }
198    
199            /**
200             * æŒ?®šã?カラãƒ?•ª号のサイズを返しますã?
201             *
202             * カラãƒ??サイズは、ResultSetMetaData#getColumnDisplaySize(int) の値ですã?
203             *
204             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
205             *
206             * @param   clmNo  カラãƒ?•ª号 (0から始まりã?カラãƒ?•°-1までの数å­?
207             * @return      æŒ?®šã?カラãƒ?•ª号のサイズ
208             */
209            public int getColumnDisplaySize( final int clmNo ) {
210                    return size[clmNo];
211            }
212    
213            /**
214             * カラãƒ??書き込み可能かどã�?�‹のboolean配å?を返しますã?
215             *
216             * 配å?はã€?から始まりã?カラãƒ?•°-1 までのint型é?列に設定されますã?
217             * カラãƒ??書き込み可能かどã�?�‹は、ResultSetMetaData#isWritable(int) の値ですã?
218             *
219             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
220             *
221             * @return      書き込み可能かどã�?�‹のboolean配å?
222             * @og.rtnNotNull
223             */
224            public boolean[] isWritable() {
225                    return isWrit.clone();
226            }
227    
228            /**
229             * æŒ?®šã?書き込み可能かどã�?�‹を返しますã?
230             *
231             * カラãƒ??書き込み可能かどã�?�‹は、ResultSetMetaData#isWritable(int) の値ですã?
232             *
233             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
234             *
235             * @param   clmNo  カラãƒ?•ª号 (0から始まりã?カラãƒ?•°-1までの数å­?
236             * @return      書き込み可能かどã�?�‹
237             */
238            public boolean isWritable( final int clmNo ) {
239                    return isWrit[clmNo];
240            }
241    
242            /**
243             * カーソルを現在の位置からé ?–¹向に1行移動しますã?
244             *
245             * ResultSet#next() を呼び出してã�?�¾すã?
246             * 結果は,すべてæ–?­—å?に変換されて格納されますã?
247             *
248             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
249             *
250             * @return  新しい現在の行が有効である場合ã?true、行がそれ以上存在しなã�??合ã?false
251             * @see         java.sql.ResultSet#next()
252             * @throws      java.sql.SQLException ãƒ??タベã?ス・アクセス・エラーが発生したå?合ã?またã?こã?メソãƒ?ƒ‰がクローズされた結果セãƒ?ƒˆで呼び出されたå?å�?
253             */
254            public boolean next() throws SQLException {
255                    return resultSet.next() ;
256            }
257    
258            /**
259             * 現在のカーソル位置にあるレコードã?カラãƒ?•ª号のãƒ??タを取得しますã?
260             *
261             * ResultSet#getObject( clmNo+1 ) を呼び出してã�?�¾すã?
262             * 引数のカラãƒ?•ª号はã€?から始まりますが、ResultSet のカラãƒ??はã€?から始まりますã?
263             * æŒ?®šã?ã€?から始まるカラãƒ?•ª号ですã?
264             * 結果は,すべてæ–?­—å?に変換されて返されますã?
265             * またã?null オブジェクトã?場合も、ゼロæ–?­—å?に変換されて返されますã?
266             *
267             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?: org.opengion.hayabusa.db.DBUtil#getValue( ResultSet , int , int ) から移å‹?
268             *
269             * @param   clmNo  カラãƒ?•ª号 (0から始まりã?カラãƒ?•°-1までの数å­?
270             * @return  現在行ã?カラãƒ?•ª号のãƒ??タ(æ–?­—å?)
271             * @throws      java.sql.SQLException ãƒ??タベã?ス・アクセス・エラーが発生したå?å�?
272             */
273            public String getValue( final int clmNo ) throws SQLException {
274                    final String val ;
275                    final Object obj = resultSet.getObject( clmNo+1 );
276                    if( obj == null ) {
277                            val = "";
278                    }
279                    else if( useObj ) {
280                            switch( type[clmNo] ) {
281                                    case Types.CLOB :               val = getClobData( (Clob)obj ) ;
282                                                                                    break;
283                                    case Types.ROWID:               val = resultSet.getString(clmNo+1);
284                                                                                    break;
285                                    case Types.TIMESTAMP :  val = HybsDateUtil.getDate( ((Timestamp)obj).getTime() , "yyyyMMddHHmmss" );
286                                                                                    break;
287                                    default :                               val = String.valueOf( obj );
288                                                                                    break;
289                            }
290                    }
291                    else {
292                            val = String.valueOf( obj );
293                    }
294    
295                    return val ;
296            }
297    
298            /**
299             * 現在のカーソル位置にあるレコードã?全カラãƒ?ƒ‡ータを取得しますã?
300             *
301             * #getValue( clmNo ) をã?0から、カラãƒ?•°-1 まで呼び出して求めた文字å?配å?を返しますã?
302             *
303             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?
304             *
305             * @return  現在行ã?全カラãƒ?ƒ‡ータのæ–?­—å?配å?
306             * @throws      java.sql.SQLException ãƒ??タベã?ス・アクセス・エラーが発生したå?å�?
307             */
308            public String[] getValues() throws SQLException {
309                    final String[] vals = new String[clmSize];
310    
311                    for( int i=0; i<clmSize; i++ ) {
312                            vals[i] = getValue( i );
313                    }
314    
315                    return vals ;
316            }
317    
318            /**
319             * タイプに応じて変換されたã?Numberオブジェクトを返しますã?
320             *
321             * 条件に当てはまらなã�??合ã?、null を返しますã?
322             * org.opengion.hayabusa.io.HybsJDBCCategoryDataset2 から移動してきましたã€?
323             * これは、検索結果をグラフ化する為の 値を取得する為のメソãƒ?ƒ‰ですã?でã€?
324             * 数値に変換できなã�??合ã?、エラーになりますã?
325             *
326             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?: org.opengion.hayabusa.db.DBUtil#getNumber( int , Object ) から移å‹?
327             *
328             * @param   clmNo  カラãƒ?•ª号 (0から始まりã?カラãƒ?•°-1までの数å­?
329             * @return      Numberオブジェクãƒ?条件に当てはまらなã�??合ã?、null)
330             * @see         java.sql.Types
331             * @throws      java.sql.SQLException ãƒ??タベã?ス・アクセス・エラーが発生したå?å�?
332             * @throws      RuntimeException 数字変換できなかったå?合ã?
333             */
334            public Number getNumber( final int clmNo ) throws SQLException {
335                    final Object obj = resultSet.getObject( clmNo+1 );
336                    Number value = null;
337    
338                    switch( type[clmNo] ) {
339                            case Types.TINYINT:
340                            case Types.SMALLINT:
341                            case Types.INTEGER:
342                            case Types.BIGINT:
343                            case Types.FLOAT:
344                            case Types.DOUBLE:
345                            case Types.DECIMAL:
346                            case Types.NUMERIC:
347                            case Types.REAL: {
348                                    value = (Number)obj;
349                                    break;
350                            }
351                            case Types.DATE:
352                            case Types.TIME:  {
353                                    value = Long.valueOf( ((Date)obj).getTime() );
354                                    break;
355                            }
356                            // 5.6.2.1 (2013/03/08) Types.DATE と Types.TIMESTAMP で処ç�?‚’åˆ?�‘ますã?
357                            case Types.TIMESTAMP: {
358                                    value = Long.valueOf( ((Timestamp)obj).getTime() );
359                                    break;
360                            }
361                            case Types.CHAR:
362                            case Types.VARCHAR:
363                            case Types.LONGVARCHAR: {
364                                    final String str = (String)obj;
365                                    try {
366                                            value = Double.valueOf(str);
367                                    }
368                                    catch (NumberFormatException ex) {
369                                            final String errMsg = "数字変換できませんでした。in=" + str
370                                                                            + CR + ex.getMessage() ;
371                                            throw new RuntimeException( errMsg,ex );
372                                            // suppress (value defaults to null)
373                                    }
374                                    break;
375                            }
376                            default:
377                                    // not a value, can't use it (defaults to null)
378                                    break;
379                    }
380    
381                    return value;
382            }
383    
384            /**
385             * カラãƒ??タイプを表現するæ–?­—å?値を返しますã?
386             *
387             * こã?æ–?­—å?を用ã�?�¦、CCSファイルでタイプごとの表示方法を
388             * æŒ?®šすることができますã?
389             * 現時点では、VARCHAR2,LONG,NUMBER,DATE,CLOB,NONE のどれかにあてはめますã?
390             *
391             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?: org.opengion.hayabusa.db.DBUtil#type2ClassName( int ) から移å‹?
392             *
393             * @param   clmNo  カラãƒ?•ª号 (0から始まりã?カラãƒ?•°-1までの数å­?
394             * @return      カラãƒ??タイプを表現するæ–?­—å?値
395             * @see         java.sql.Types
396             */
397            public String getClassName( final int clmNo ) {
398                    final String rtn ;
399    
400                    switch( type[clmNo] ) {
401                            case Types.CHAR:
402                            case Types.VARCHAR:
403                            case Types.BIT:
404                                    rtn = "VARCHAR2"; break;
405                            case Types.LONGVARCHAR:
406                                    rtn = "LONG"; break;
407                            case Types.TINYINT:
408                            case Types.SMALLINT:
409                            case Types.INTEGER:
410                            case Types.NUMERIC:
411                            case Types.BIGINT:
412                            case Types.FLOAT:
413                            case Types.DOUBLE:
414                            case Types.REAL:
415                            case Types.DECIMAL:
416                                    rtn = "NUMBER"; break;
417                            case Types.DATE:
418                            case Types.TIME:
419                            case Types.TIMESTAMP:
420                                    rtn = "DATE"; break;
421                            case Types.CLOB:
422                                    rtn = "CLOB"; break;
423                            default:
424                                    rtn = "NONE"; break;
425                    }
426    
427                    return rtn;
428            }
429    
430            /**
431             * Clob オブジェクトから文字å?を取りå?しますã?
432             *
433             * @og.rev 6.0.4.0 (2014/11/28) 新規作æ?: org.opengion.hayabusa.db.DBUtil#getClobData( Clob ) から移å‹?
434             *
435             * @param       clobData Clobオブジェクãƒ?
436             * @return      Clobオブジェクトから取りå?したæ–?­—å?
437             * @throws      SQLException ãƒ??タベã?スアクセスエラー
438             * @throws      RuntimeException 入出力エラーが発生したå?å�?
439             * @og.rtnNotNull
440             */
441            private String getClobData( final Clob clobData ) throws SQLException {
442                    if( clobData == null ) { return ""; }
443    
444                    final StringBuilder buf = new StringBuilder( 10000 );
445    
446                    Reader reader = null;
447                    try {
448                            reader = clobData.getCharacterStream();
449                            final char[] ch = new char[10000];
450                            int  len ;
451                            while( (len = reader.read( ch )) >= 0 ) {
452                                    buf.append( ch,0,len );
453                            }
454                    }
455                    catch( IOException ex ) {
456                            final String errMsg = "CLOBãƒ??タの読み込みに失敗しましたã€?
457                                                                    + ex.getMessage() ;
458                            throw new RuntimeException( errMsg,ex );
459                    }
460                    finally {
461                            Closer.ioClose( reader );
462                    }
463                    return buf.toString();
464            }
465    }