OSDN Git Service

Ver8.5.2.0
[opengion/opengionV8.git] / uap / webapps / gf / src / org / opengion / fukurou / model / ExcelStyleFormat.java
1 /*
2  * Copyright (c) 2009 The openGion Project.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific language
14  * governing permissions and limitations under the License.
15  */
16 package org.opengion.fukurou.model;
17
18 import java.util.concurrent.ConcurrentMap;                                              // 6.4.3.4 (2016/03/11)
19 import java.util.concurrent.ConcurrentHashMap;                                  // 6.4.3.1 (2016/02/12) refactoring
20
21 import org.apache.poi.ss.usermodel.BuiltinFormats;                              // 6.2.0.0 (2015/02/27)
22 import org.apache.poi.xssf.model.StylesTable;                                   // 6.2.0.0 (2015/02/27)
23 import org.apache.poi.xssf.usermodel.XSSFCellStyle;                             // 6.2.0.0 (2015/02/27)
24
25 import java.util.Locale;                                                                                // 6.2.0.0 (2015/02/27)
26 import java.util.Date;                                                                                  // 6.2.0.0 (2015/02/27)
27 import java.text.DateFormat;                                                                    // 6.2.0.0 (2015/02/27)
28 import java.text.SimpleDateFormat;                                                              // 6.2.0.0 (2015/02/27)
29 import org.apache.poi.ss.usermodel.DateUtil;                                    // 6.2.0.0 (2015/02/27)
30 import org.apache.poi.ss.util.NumberToTextConverter;                    // 6.2.0.0 (2015/02/27)
31 import org.apache.poi.hssf.record.ExtendedFormatRecord;                 // 6.2.0.0 (2015/02/27)
32 import org.apache.poi.hssf.record.FormatRecord;                                 // 6.2.0.0 (2015/02/27)
33 import org.apache.poi.hssf.record.NumberRecord;                                 // 6.2.0.0 (2015/02/27)
34
35 /**
36  * POI による、Excel(xlsx)の読み取りクラスです。
37  *
38  * xlsx形式のEXCELを、イベント方式でテキストデータを読み取ります。
39  * このクラスでは、XSSF(.xlsx)形式のファイルを、TableModelHelper を介したイベントで読み取ります。
40  * TableModelHelperイベントは、openGion形式のファイル読み取りに準拠した方法をサポートします。
41  * ※ openGion形式のEXCELファイルとは、#NAME 列に、カラム名があり、#で始まる
42  *    レコードは、コメントとして判断し、読み飛ばす処理の事です。
43  *
44  * @og.rev 6.0.3.0 (2014/11/13) 新規作成
45  * @og.rev 6.2.0.0 (2015/02/27) パッケージ変更(util → model),クラス名変更(ExcelReader_XSSF → EventReader_XLSX)
46  * @og.group ファイル入力
47  *
48  * @version  6.0
49  * @author   Kazuhiko Hasegawa
50  * @since    JDK7.0,
51  */
52 public final class ExcelStyleFormat {
53         /** このプログラムのVERSION文字列を設定します。       {@value} */
54         private static final String VERSION = "6.4.3.3 (2016/03/04)" ;
55
56         // 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
57         private static final String[] DATE_TYPE = { "yyyyMMdd" , "yyyyMMddHHmmss" , "HHmmss" };
58         // 6.3.1.0 (2015/06/28) 外部からも使えるように、static化します。
59         private static final DateFormat[] DT_FORMAT = new DateFormat[DATE_TYPE.length];                 // 6.4.1.1 (2016/01/16) dtFormat → DT_FORMAT refactoring
60         private final StylesTable                       stylesTable ;
61
62         // 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
63         // private final List<Integer>                  extFmtIdx = new ArrayList<>();  // ExtendedFormatRecord のアドレス(順番) の順番に、FormatIndexを設定する。
64         /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */
65         private final ConcurrentMap<Integer,String> fmtStrMap = new ConcurrentHashMap<>();      // FormatIndex をキーに、Format文字列 を管理
66         /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */
67         private final ConcurrentMap<Integer,Integer> extFmtIdxMap = new ConcurrentHashMap<>();  // ExtendedFormatRecord のアドレス(順番) をキーに、FormatIndexを設定する。
68         private int   extFmtCnt ;
69
70         /**
71          * XSL系 コンストラクター
72          *
73          * XSL 処理では、HSSFListener のイベント処理のうち、NumberRecord の値取得に
74          * 必要な内部処理を、実行します。
75          * 具体的には、ExtendedFormatRecord レコードから、FormatIndex と 並び順を
76          * 管理するMapと、FormatRecord レコードから、IndexCode と フォーマット文字列を
77          * 管理するMap を作成し、NumberRecordレコードの XFIndex から、ExtendedFormatRecord を
78          * 経由して、FormatRecord のフォーマット文字列 を取得し、日付フォーマットの場合は、
79          * 日付文字列に、それ以外は、数字文字列に変換する手助けを行います。
80          *
81          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
82          */
83         public ExcelStyleFormat() {
84                 stylesTable = null;
85         }
86
87         /**
88          * XSLX系 コンストラクター
89          *
90          * StylesTable は、日付型をはじめとする、EXCELのスタイルのフォーマットを管理しています。
91          * XSLX形式のEXCELをパースする場合に、このコンストラクタを使用して、StylesTableオブジェクトを
92          * 設定します。
93          *
94          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
95          *
96          * @param       styles StylesTableオブジェクト
97          */
98         public ExcelStyleFormat( final StylesTable styles ) {
99                 stylesTable = styles;
100         }
101
102         /**
103          * XSL系 ExtendedFormatRecordレコードの設定。
104          *
105          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
106          *
107          * @param       extFmtRec ExtendedFormatRecordレコード
108          */
109         public void addExtFmtRec( final ExtendedFormatRecord extFmtRec ) {
110                 final short fmtIdx = extFmtRec.getFormatIndex();
111                 final short xfType = extFmtRec.getXFType();
112                 // Listに アドレス(順番) の順番に、FormatIndexを設定する。
113         //      extFmtIdx.add( Integer.valueOf( fmtIdx ) );
114
115                 // タイプを判別して、アドレス(順番)をキーに、Mapに登録することで、データ件数を削減します。
116                 if( xfType == ExtendedFormatRecord.XF_CELL ) {
117                         //                              アドレス(順番)               FormatIndex
118                         extFmtIdxMap.put( Integer.valueOf( extFmtCnt ),Integer.valueOf( fmtIdx ) );
119         //              System.out.println( "fmtIdx=[" + fmtIdx + "] , xfType=[" + xfType + "] , CNT=" + extFmtCnt );
120                 }
121                 extFmtCnt++;
122         }
123
124         /**
125          * XSL系 FormatRecordレコードの設定。
126          *
127          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
128          *
129          * @param       fmtRec FormatRecordレコード
130          */
131         public void addFmtRec( final FormatRecord fmtRec ) {
132                 final int    idxc = fmtRec.getIndexCode();
133                 final String fmt  = fmtRec.getFormatString();
134
135                 // IndexCode をキーに、Format文字列を登録する。
136                 fmtStrMap.put( Integer.valueOf( idxc ) , fmt );
137         //      System.out.println( "fmtRec=[" + idxc + "], fmt=[" + fmt + "]" );
138         }
139
140         /**
141          * XSLX系 セルスタイル文字列(スタイル番号)から、データフォーマットを取得します。
142          *
143          * StylesTable は、日付型をはじめとする、EXCELのスタイルのフォーマットを管理しています。
144          * XSLX形式のEXCELのフォーマット文字列を取得する場合に、使用します。
145          *
146          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
147          *
148          * @param       cellStyleStr    セルスタイル文字列(スタイル番号)
149          * @param       val                             endElement時の値文字列
150          * @return      日付データか、数値データかを判別した結果の文字列
151          */
152         public String getNumberValue( final String cellStyleStr , final String val ) {
153                 String fmtStr = null;
154
155                 if( stylesTable != null && cellStyleStr != null && !cellStyleStr.isEmpty() ) {
156                         final int stIdx = Integer.parseInt( cellStyleStr );
157                         final XSSFCellStyle style = stylesTable.getStyleAt( stIdx );
158                         fmtStr = style.getDataFormatString();
159
160                         // 必要かどうか不明。テスト時は、ユーザー定義フォーマットも、上記処理で取得できていた。
161                         if( fmtStr == null ) {
162                                 final int fmtIdx = style.getDataFormat();
163                                 fmtStr = BuiltinFormats.getBuiltinFormat( fmtIdx );
164                         }
165
166         //              if( fmtStr != null ) {
167         //                      System.out.println( "style=[" + cellStyleStr + "], stIdx=[" + stIdx + "],  fmtStr=[" + fmtStr + "]" );
168         //              }
169                 }
170
171                 return getNumberValue( fmtStr , Double.parseDouble( val ) ) ;
172         }
173
174         /**
175          * XSL系 Numberレコードから、日付データか、数値データかを判別して返します。
176          *
177          * 日付フォーマットの判定処理を #isDateFormat(String) で行い、日付の場合は、
178          * 各タイプ(日付、日時、時刻)に応じた、文字列を返します。
179          * 日付フォーマットでない場合は、数字化文字列を返します。
180          *
181          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
182          *
183          * @param       numrec  NumberRecordレコード
184          * @return      日付データか、数値データかを判別した結果の文字列
185          */
186         public String getNumberValue( final NumberRecord numrec ) {
187                 final int xfIdx  = numrec.getXFIndex();                                                         // extFmtCnt の事
188                 final int fmtIdx = extFmtIdxMap.get( Integer.valueOf( xfIdx ) );
189
190         //      final String fmtStr = fmtIdx < HSSFDataFormat.getNumberOfBuiltinBuiltinFormats()
191         //                                                      ? HSSFDataFormat.getBuiltinFormat( (short)fmtIdx )
192         //                                                      : fmtStrMap.get( Integer.valueOf( fmtIdx ) );
193
194                 final String fmtStr = fmtIdx < BuiltinFormats.FIRST_USER_DEFINED_FORMAT_INDEX
195                                                                 ? BuiltinFormats.getBuiltinFormat( fmtIdx )
196                                                                 : fmtStrMap.get( Integer.valueOf( fmtIdx ) );
197
198         //              if( fmtStr != null ) {
199         //                      System.out.println( "xfIdx=[" + xfIdx + "], fmtIdx=[" + fmtIdx + "],  fmtStr=[" + fmtStr + "]" );
200         //              }
201
202                 return getNumberValue( fmtStr , numrec.getValue() ) ;
203         }
204
205         /**
206          * フォーマット情報と値から、日付データか、数値データかを判別して返します。
207          *
208          * 日付フォーマットの判定処理を #isDateFormat(String) で行い、日付の場合は、
209          * 各タイプ(日付、日時、時刻)に応じた、文字列を返します。
210          * 日付フォーマットでない場合は、数字文字列を返します。
211          *
212          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
213          * @og.rev 6.3.1.0 (2015/06/28) 外部からも使えるように、static化します。
214          *
215          * @param       fmtStr  フォーマット情報
216          * @param       val             Numberレコードのデータ
217          * @return      日付データか、数値データかを判別した結果の文字列
218          */
219         public static String getNumberValue( final String fmtStr , final double val ) {
220                 return isDateFormat( fmtStr )
221                                         ? dateFormat( val )                                                     // 日付
222                                         : NumberToTextConverter.toText( val ) ;         // 数字
223         }
224
225         /**
226          * フォーマット文字列から、日付型フォーマットかどうかの判定を行います。
227          *
228          * ここでは、日本式のフォーマットや、ユーザー定義の日付フォーマットでも、
229          * ある程度判定できるように、処理しています。
230          * 以下の文字列を含む場合は、true を返し、それ以外は、false です。
231          *   "年","月","日","yy","y/m","m/d","h:m"
232          * ただし、General(標準) は、除外しています。
233          *
234          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
235          * @og.rev 6.3.1.0 (2015/06/28) 外部からも使えるように、static化します。
236          * @og.rev 6.4.3.3 (2016/03/04) PMD refactoring. These nested if statements could be combined
237          *
238          * @param       fmt     フォーマット文字列
239          * @return      判定結果 [true:日付型/false:それ以外]
240          */
241         private static boolean isDateFormat( final String fmt ) {
242                 return  fmt != null && !fmt.isEmpty() && !"General".equalsIgnoreCase( fmt ) &&
243                         (       fmt.contains( "年" ) || fmt.contains( "月"  ) || fmt.contains( "日"  ) ||
244                                 fmt.contains( "yy" ) || fmt.contains( "y/m" ) || fmt.contains( "m/d" ) ||
245                                 fmt.contains( "h:m" ) ) ;
246         }
247
248         /**
249          * 日付型の値を、最適なフォーマットで変換して返します。
250          *
251          * 日付データは、(DATE=0,DATETIME=1,TIME=2) に分類できます。
252          * DATE とは、日付のみの状態で、引数の val は、整数に変換できます。
253          * その場合、"yyyyMMdd" フォーマットで変換します。
254          * DATETIME とは、日付+時刻なので、"yyyyMMddHHmmss" に変換します。
255          * TIME は、日付情報を持っていないため、"HHmmss" に変換します。
256          *
257          * @og.rev 6.2.0.0 (2015/02/27) 日付型の処理(DATE=0,DATETIME=1,TIME=2)
258          * @og.rev 6.3.1.0 (2015/06/28) 外部からも使えるように、static化します。
259          *
260          * @param       val     日付型の値
261          * @return      日付型の変換結果
262          */
263         public static String dateFormat( final double val ) {
264                 int dtType = 0;         // 日付型の処理(DATE=0,DATETIME=1,TIME=2)
265                 if( val < 1.0d ) {                                                                                              // 日付部が無い → TIME=2
266                         dtType = 2;
267                 }
268                 // 6.3.9.0 (2015/11/06) Avoid if (x != y) ..; else ..; (PMD)
269                 else if( Double.compare( val , Math.floor( val ) ) == 0 ) {             // 整数(Long 相当) → DATE=0
270                         dtType = 0;
271                 }
272                 else {                                                                                                                  // 整数でない → DATETIME=1
273                         dtType = 1;
274                 }
275
276                 DateFormat dtfmt = DT_FORMAT[dtType];                   // 各タイプ別にキャッシュしている。
277                 if( dtfmt == null ) {
278                         dtfmt = new SimpleDateFormat( DATE_TYPE[dtType] , Locale.JAPAN );               // 初めての場合は新規作成
279                         DT_FORMAT[dtType] = dtfmt;
280                 }
281
282                 final Date dt = DateUtil.getJavaDate( val );
283                 return dtfmt.format( dt );
284         }
285 }