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.hayabusa.report2;
017
018import java.io.File;
019import java.util.Map;
020import java.util.HashMap;
021import java.util.Locale;
022// import java.util.List;                                                                               // 8.0.3.0 (2021/12/17) 【保留】
023import java.nio.file.Files;                                                                             // 8.1.0.3 (2022/01/21)
024import java.nio.file.Paths;                                                                             // 8.1.0.3 (2022/01/21)
025import java.nio.file.Path;                                                                              // 8.1.0.3 (2022/01/21)
026import java.nio.file.StandardCopyOption;                                                // 8.1.0.3 (2022/01/21)
027
028import org.opengion.fukurou.system.HybsConst;                                   // 8.0.1.0 (2021/10/29)
029// import org.opengion.fukurou.model.ExcelModel;                                // 8.0.3.0 (2021/12/17)【保留】
030import org.opengion.fukurou.util.FileUtil;
031import org.opengion.fukurou.util.StringUtil;
032import org.opengion.hayabusa.common.HybsSystem;
033import org.opengion.hayabusa.common.HybsSystemException;
034import static org.opengion.fukurou.system.HybsConst.CR ;                // 6.1.0.0 (2014/12/26)
035// import static org.opengion.fukurou.system.HybsConst.FS ;             // 8.0.3.0 (2021/12/17)
036
037import com.sun.star.beans.PropertyValue;
038import com.sun.star.frame.XComponentLoader;
039import com.sun.star.frame.XController;
040import com.sun.star.frame.XDispatchHelper;
041import com.sun.star.frame.XDispatchProvider;
042import com.sun.star.frame.XModel;
043import com.sun.star.frame.XStorable;
044import com.sun.star.io.IOException;
045import com.sun.star.lang.EventObject;
046import com.sun.star.lang.IllegalArgumentException;
047import com.sun.star.lang.XComponent;
048import com.sun.star.uno.UnoRuntime;
049import com.sun.star.util.CloseVetoException;
050import com.sun.star.util.XCloseable;
051import com.sun.star.view.PrintJobEvent;
052import com.sun.star.view.PrintableState;
053import com.sun.star.view.XPrintJobBroadcaster;
054import com.sun.star.view.XPrintJobListener;
055import com.sun.star.view.XPrintable;
056
057/**
058 * OpenOfficeを利用して様々な形式のファイルを読み込み、出力・印刷を行うための変換クラスです。
059 *
060 * 変換を行うことのできる入出力のフォーマット以下の通りです。
061 *
062 * [対応フォーマット]
063 *  入力[Calc(ods)   ,Excel(xls)     ] ⇒ 出力[Calc(ods)   ,Excel(xls,xlsx)      ,PDF]
064 *  入力[Writer(odt) ,Word(doc)      ] ⇒ 出力[Writer(odt) ,Word(doc,docx)       ,PDF]
065 *  入力[Impress(odp),PowerPoint(ppt)] ⇒ 出力[Impress(odp),PowerPoint(ppt,pptx) ,PDF]
066 *  入力[ * 上記の全て               ] ⇒ 印刷
067 *
068 * ※ xlsx,docx,pptx は、MS 2007形式の為、LibreOffice のみ利用できます。
069 *
070 * 変換を行うには、以下の2通りの方法があります。
071 * <del>
072 * (1)簡易的な変換メソッドを利用する場合
073 *   #convert(String, String)を利用して、変換を行います。
074 *   この場合、出力形式は、出力ファイルの拡張子に従って自動的に決定されます。
075 *   このため、印刷処理などを行う場合は、(2)の方法で出力して下さい。
076 * </del>
077 * (2)段階的に各メソッドを呼び出して変換する場合
078 *   オブジェクトを生成した後、{@link #open()}、#(各種変換メソッド)、{@link #clone()}を
079 *   順番に呼び出して変換を行います。
080 *   この場合、出力形式は、それに対応するメソッドを呼び出すことで決定されます。
081 *
082 *   また、変換を行うための、各種メソッドは、例外としてThrowableを投げるように定義されています。
083 *   このクラスを利用する場合は、このThrowableをcatchし、catch句で、必ず{@link #close( boolean )}に、
084 *   "true"(エラー発生時のクローズ処理)を指定して、終了処理を行って下さい。
085 *   (これを行わない場合、OpenOfficeの不要なプロセスが残ってしまう可能性があります)
086 *
087 * また、出力ファイルが既に存在する場合、出力ファイルは一旦削除された後、処理されます。
088 * なお、入力ファイルと出力ファイルが同じ場合、何も処理されません。(例外も発行されません)
089 *
090 * 入力ファイルを、CSV形式で複数指定した場合、複数の入力ファイルをマージして出力します。
091 * ※1 現状は、ファイルのマージは、入力ファイルがExcelまたはCalcの場合のみ対応しています。
092 *
093 * ※ DocConverter は、クラウド対応されていません。変換時はローカルファイル間で行ってください。
094 *
095 * @og.group 帳票システム
096 *
097 * @version  4.0
098 * @author   Hiroki.Nakamura
099 * @since    JDK1.6
100 */
101public class DocConverter_OOO {
102        private static final String OFFICE_VER = HybsConst.getenv( "OPENOFFICE_VER","NULL" );   // 8.0.1.0 (2021/10/29) 環境変数から取得
103
104        private final boolean                   isOnline;                       // オンライン処理かどうか(オンライン処理の場合、プロセスはファクトリクラス経由で生成されます)
105        private final String[]                  mergeFile;
106
107        private String                                  inputName;
108        private String                                  origName;
109
110        private XComponent                              xComp;                          // 5.1.8.0 (2010/07/01) メソッドと重なる変数名の変更
111        private SOfficeProcess                  soffice;
112
113        // 入力、出力の拡張子とこれに対応するフィルター名
114        // share¥registry¥calc.xcd 形式のファイル <node oor:name="****" 部分?
115
116        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
117        private static final Map<String,String> FILTER_MAP      = new HashMap<>();              // 6.4.1.1 (2016/01/16) filterMap → FILTER_MAP refactoring
118        static {
119                // 8.1.0.3 (2022/01/21) ファイルの拡張子が同じ場合は、copyで対応する。
120        //      FILTER_MAP.put( "ods_ods", "calc8" );
121                FILTER_MAP.put( "xls_ods", "calc8" );
122                FILTER_MAP.put( "ods_xls", "MS Excel 97" );
123        //      FILTER_MAP.put( "xls_xls", "MS Excel 97" );
124                FILTER_MAP.put( "ods_pdf", "calc_pdf_Export" );
125                FILTER_MAP.put( "xls_pdf", "calc_pdf_Export" );
126        //      FILTER_MAP.put( "odt_odt", "writer8" );
127                FILTER_MAP.put( "doc_odt", "writer8" );
128                FILTER_MAP.put( "odt_doc", "MS Word 97" );
129        //      FILTER_MAP.put( "doc_doc", "MS Word 97" );
130                FILTER_MAP.put( "odt_pdf", "writer_pdf_Export" );
131                FILTER_MAP.put( "doc_pdf", "writer_pdf_Export" );
132        //      FILTER_MAP.put( "odp_odp", "impress8" );
133                FILTER_MAP.put( "ppt_odp", "impress8" );
134                FILTER_MAP.put( "odp_ppt", "MS PowerPoint 97" );
135        //      FILTER_MAP.put( "ppt_ppt", "MS PowerPoint 97" );
136                FILTER_MAP.put( "odp_pdf", "impress_pdf_Export" );
137                FILTER_MAP.put( "ppt_pdf", "impress_pdf_Export" );
138
139                if( OFFICE_VER.contains( "LibreOffice" ) ) {                                            // 8.0.1.0 (2021/10/29) LibreOffice の時のみ使用可能
140        //              FILTER_MAP.put( "ods_xlsx", "Calc MS Excel 2007 XML" );                 // 8.0.1.0 (2021/10/29)
141        //              FILTER_MAP.put( "xls_xlsx", "Calc MS Excel 2007 XML" );                 // 8.0.1.0 (2021/10/29)
142
143                        FILTER_MAP.put( "ods_xlsx", "Calc Office Open XML" );                   // 8.0.1.0 (2021/10/29)
144                        FILTER_MAP.put( "xls_xlsx", "Calc Office Open XML" );                   // 8.0.1.0 (2021/10/29)
145
146                        // share¥registry¥writer.xcd
147                        FILTER_MAP.put( "odt_docx", "MS Word 2007 XML" );                               // 8.0.1.0 (2021/10/29)
148                        FILTER_MAP.put( "doc_docx", "MS Word 2007 XML" );                               // 8.0.1.0 (2021/10/29)
149                        // ※ writer_MS_Word_2007 は、failed: 0x81a(Error Area:Io Class:Parameter Code:26)
150
151                        // share¥registry¥impress.xcd
152                        FILTER_MAP.put( "odp_pptx", "Impress MS PowerPoint 2007 XML" ); // 8.0.1.0 (2021/10/29)
153                        FILTER_MAP.put( "ppt_pptx", "Impress MS PowerPoint 2007 XML" ); // 8.0.1.0 (2021/10/29)
154                        // ※ MS PowerPoint 2007 XML は、failed: 0x81a(Error Area:Io Class:Parameter Code:26)
155                        // ※ Office Open XML Presentation は、failed: 0x81a(Error Area:Io Class:Parameter Code:26)
156                }
157        };
158
159        /**
160         * コンストラクタです。
161         *
162         * #DocConverter(input, true)と同じです。
163         *
164         * @param input ファイル一覧(CSV形式)
165         * @see #DocConverter_OOO(String[])
166         */
167        public DocConverter_OOO( final String input ) {
168                this( StringUtil.csv2Array( input ), true );
169        }
170
171        /**
172         * コンストラクタです。
173         *
174         * #DocConverter(input, true)と同じです。
175         *
176         * @param input ファイル一覧(配列)
177         * @see #DocConverter_OOO(String[], boolean)
178         */
179        public DocConverter_OOO( final String input[] ) {
180                this( input, true );
181        }
182
183        /**
184         * コンストラクタです。
185         *
186         * isOnline(isOl)がtrueに指定された場合、soffice.binのプロセスをファクトリークラス経由で生成し、
187         * キャッシュします。
188         * 但し、システムリソースが読み込まれないような、バッチファイルから起動した場合は、この方法は
189         * 利用できないため、isOnlineをfalseに指定する必要があります。
190         *
191         * @param input ファイル一覧(配列)
192         * @param isOl オンライン(Web環境での使用)かどうか
193         */
194        public DocConverter_OOO( final String input[], final boolean isOl ) {
195                if( input == null || input.length == 0 || input[0].isEmpty() ) {
196                        throw new HybsSystemException( "入力ファイルが指定されていません。" );
197                }
198                origName = input[0];
199                final File inFile = new File( origName );
200                if( !inFile.exists() ) {
201                        throw new HybsSystemException( "入力ファイルが存在しません。[file=" + input[0] + "]" );
202                }
203                isOnline  = isOl;
204                inputName = origName;
205//              origName  = input[0];
206
207                if( input.length == 1 ) {
208                        mergeFile = null;
209                }
210                else {
211                        if( !"xls".equals( getSuffix( origName ) ) && !"ods".equals( getSuffix( origName ) ) ) {
212                                throw new HybsSystemException( "ファイルのマージを行う場合、入力ファイルは、ExcelまたはCacl形式である必要があります。" );
213                        }
214
215                        mergeFile = new String[input.length-1];
216                        for( int i=0; i<mergeFile.length; i++ ) {
217                                final String mrgFile = input[i+1];
218                                if( mrgFile.isEmpty() || !( new File( mrgFile ) ).exists() ) {
219                                        throw new HybsSystemException( "マージファイルが指定されていないか、または存在しません。[file=" + mrgFile + "]" );
220                                }
221                                if( inputName.equals( mrgFile ) ) {
222                                        throw new HybsSystemException( "マージファイルに入力ファイルと同じファイルが指定されてます。[file=" + mrgFile + "]" );
223                                }
224                                if( !"xls".equals( getSuffix( mrgFile ) ) && !"ods".equals( getSuffix( mrgFile ) ) ) {
225                                        throw new HybsSystemException( "ファイルのマージを行う場合、マージファイルは、ExcelまたはCacl形式である必要があります。" );
226                                }
227                                mergeFile[i] = mrgFile;
228                        }
229                }
230        }
231
232        /**
233         * SOficeのコンポーネントを起動します。
234         *
235         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
236         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
237         *
238         * @og.rev 5.1.7.0 (2010/06/01) マージ処理対応
239         *
240         * @throws Throwable 何らかのエラーが発生した場合。
241         * @see #close()
242         * @see #close(boolean)
243         */
244        public void open() throws Throwable {
245                // プロセスの取得
246                if( soffice == null ) {
247                        if( isOnline ) {
248                                soffice = ProcessFactory.newInstance();
249                        }
250                        else {
251                                soffice = new SOfficeProcess( "docconverter.class" );
252                                soffice.bootstrap();
253                        }
254
255                        // マージする場合は、マージ対象のファイルをテンポラリにコピーする(readOnly回避のため)
256                        // テンプレート(無題)として上げると、シートコピー先として特定できなくなるため
257                        if( mergeFile != null ) {
258                                final File origFile = new File( origName );
259                                inputName = soffice.getTempPath() + System.currentTimeMillis() + "_" + origFile.getName();
260                                FileUtil.copy( origFile, new File( inputName ) );
261                        }
262                }
263
264                // 5.1.7.0 (2010/06/01) マージ処理対応
265                xComp = getComponent( inputName, ( mergeFile == null ? true : false ), false );
266
267                if( mergeFile != null ) {
268                        for( int i=0; i<mergeFile.length; i++ ) {
269                                merge( mergeFile[i] );
270                        }
271                }
272        }
273
274        /**
275         * ドキュメントコンポーネントを取得します。
276         *
277         * @param       input                   ファイル名
278         * @param       isHidden                隠し属性[true/false]
279         * @param       isAsTemplate    OpenOffice上のTemplate属性[true/false]
280         *
281         * @og.rev 8.1.0.2 (2022/01/14) odsフォーマットエラー時には、rtnDoc が null になる
282         *
283         * @return      ドキュメントコンポーネント
284         */
285//      @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
286        private XComponent getComponent( final String input, final boolean isHidden, final boolean isAsTemplate ) {
287                final PropertyValue[] calcProps = new PropertyValue[2];
288                calcProps[0] = new PropertyValue();
289                calcProps[0].Name = "Hidden";
290                calcProps[0].Value = isHidden;
291                calcProps[1] = new PropertyValue();
292                calcProps[1].Name = "AsTemplate";
293                calcProps[1].Value = isAsTemplate;
294
295                final String url = "file:///" + input.replace( '\\', '/' );
296
297                XComponent rtnDoc;
298//              final XComponentLoader cloader = (XComponentLoader) UnoRuntime.queryInterface( XComponentLoader.class, soffice.getDesktop() );
299                final XComponentLoader cloader = UnoRuntime.queryInterface( XComponentLoader.class, soffice.getDesktop() );
300                try {
301                        rtnDoc = cloader.loadComponentFromURL( url, "_blank", 0, calcProps );
302                }
303                catch( final IOException ex ) {
304                        throw new HybsSystemException( "OpenOfficeの立ち上げ時にエラーが発生しました(入出力エラー)。", ex );
305                }
306                catch( final IllegalArgumentException ex ) {
307                        throw new HybsSystemException( "OpenOfficeの立ち上げ時にエラーが発生しました(パラメーター不正)。", ex );
308                }
309
310                // 8.1.0.2 (2022/01/14)
311                if( rtnDoc == null ) {
312                        throw new HybsSystemException( "OpenOffice.ods ファイルが不正です(例えば、印刷範囲の繰り返しに {@FORMALINE}が含まれるなど)" );
313                }
314
315                return rtnDoc;
316        }
317
318        /**
319         * ドキュメント(xls,ods)のマージを行います。
320         *
321         * @param mergeInputName マージ対象のファイル名
322         */
323//      @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
324        private void merge( final String mergeInputName ) {
325                // マージする副ファイルは、テンプレート(無題)として上げる(readOnly回避のため)
326                final XComponent subDoc = getComponent( mergeInputName, false, true );
327
328                final XDispatchProvider dispatchProvider
329//                      = (XDispatchProvider)UnoRuntime.queryInterface( XDispatchProvider.class
330                        = UnoRuntime.queryInterface( XDispatchProvider.class
331//                              ,((XController)UnoRuntime.queryInterface( XController.class
332                                ,(UnoRuntime.queryInterface( XController.class
333//                                      ,((XModel)UnoRuntime.queryInterface( XModel.class
334                                        ,(UnoRuntime.queryInterface( XModel.class
335                                                ,subDoc
336                                        )).getCurrentController()
337                                )).getFrame()
338                        );
339
340                final XDispatchHelper xDispatchHelper = soffice.getDispatcher();
341                xDispatchHelper.executeDispatch(dispatchProvider, ".uno:TableSelectAll", "", 0, new PropertyValue[0]);
342
343                String title = new File( inputName ).getName() ;
344                title = title.substring( 0, title.indexOf( '.' ) );
345
346                final PropertyValue[] moveProps = new PropertyValue[3];
347                moveProps[0] = new PropertyValue();
348                moveProps[0].Name = "DocName";
349                moveProps[0].Value = title;
350                moveProps[1] = new PropertyValue();
351                moveProps[1].Name = "Index";
352                moveProps[1].Value = 32767;
353                moveProps[2] = new PropertyValue();
354                moveProps[2].Name = "Copy";
355                moveProps[2].Value = true;
356                xDispatchHelper.executeDispatch(dispatchProvider, ".uno:Move", "", 0, moveProps);
357
358                closeComponent( subDoc );
359        }
360
361        /**
362         * Calcコンポーネントをクローズします。
363         *
364         * このクローズ処理は、処理が正常終了した場合に呼び出しする必要があります。
365         * 例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
366         *
367         * このメソッドは#close(false)と同じです。
368         *
369         * @throws Throwable 何らかのエラーが発生した場合。
370         * @see #close(boolean)
371         */
372        public void close() throws Throwable {
373                close( false );
374        }
375
376        /**
377         * Calcコンポーネントをクローズします。
378         *
379         * 引数のisErrがtrueの場合、この変換オブジェクトで生成されたプロセスは強制的に破棄されます。
380         * falseの場合は、プロセスは、ファクトリクラスを経由して、キャッシュに戻されます。
381         * (バッチ処理の場合は、いずれの場合も、プロセスは強制的に破棄されます)
382         *
383         * 起動から変換、クローズまでの書く処理で例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
384         *
385         * #close(false)は#close()と同じであるため、通常利用することはありません。
386         *
387         * @og.rev 4.2.4.1 (2008/07/07 ) 終了処理を60回で終わるように修正
388         * @og.rev 4.3.0.0 (2008/07/15 ) ↑は6秒しか待っていなかったので、60秒待つように修正
389         *
390         * @param       isErr   trueの場合、この変換オブジェクトで生成されたプロセスは強制的に破棄されます。
391         */
392//      @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
393        public void close( final boolean isErr ) {
394                if( xComp != null ) {
395                        closeComponent( xComp );
396                }
397
398                if( soffice != null ) {
399                        if( isOnline ) {
400                                if( isErr ) {
401                                        ProcessFactory.remove( soffice );
402                                }
403                                else {
404                                        ProcessFactory.release( soffice );
405                                }
406                        }
407                        else {
408                                soffice.close();
409                        }
410                }
411
412                // マージした場合は、テンポラリにコピーしたファイルを削除
413                // 6.0.0.1 (2014/04/25) These nested if statements could be combined
414                if( mergeFile != null && ! ( new File( inputName ) ).delete() ) {
415                        System.err.println( "テンポラリにコピーしたファイルを削除できませんでした。[" + inputName + "]" );
416                }
417        }
418
419        /**
420         * ドキュメントコンポーネントをクローズします。
421         *
422         * @param comp ドキュメントコンポーネント
423         */
424//      @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
425        private void closeComponent( final XComponent comp ) {
426                XCloseable closeable = null;
427                for( int i=0;; ++i ) {
428                        try {
429//                              closeable = (XCloseable) UnoRuntime.queryInterface( XCloseable.class, comp );
430                                closeable = UnoRuntime.queryInterface( XCloseable.class, comp );
431                                closeable.close( true );
432                                break;
433                        }
434                        catch( final CloseVetoException ex ) {
435                                // 4.2.4.1 (2008/07/07 )
436                                // 4.3.4.4 (2009/01/01)
437                                if( i == 600 ) { throw new HybsSystemException( "sofficeプロセスに接続できません。", ex ); }
438                                try {
439                                        Thread.sleep( 100 );
440                                }
441                                catch( final InterruptedException ex2 ) {
442                        //              throw new HybsSystemException( ex2 );
443                                }
444                        }
445                }
446        }
447
448        /**
449         * 印刷を行います。
450         *
451         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
452         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
453         *
454         * @og.rev 4.3.0.0 (2008/07/16) スプールが終わるまでwaitし、さらにプリンタ発行の状況を監視し、正常終了かどうかを判断
455         * @og.rev 4.3.7.3 (2009/06/22) 存在しないプリンターを指定した場合のエラーハンドリングを追加
456         * @og.rev 5.1.2.0 (2010/01/01) CentOS等は、OS_INFOがLinux UNKNOWNとなるため、判定条件を変更
457         *
458         * @param       printer プリンター名
459         * @throws Throwable 何らかのエラーが発生した場合。
460         */
461        public void print( final String printer ) throws Throwable {
462                if( printer == null || printer.isEmpty() ) {
463                        throw new HybsSystemException( "プリンターが指定されていません。" );
464                }
465
466        //      if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい(1)" ); }
467//              @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
468//              final XPrintable xprintable = (XPrintable) UnoRuntime.queryInterface( XPrintable.class, xComp );
469                final XPrintable xprintable = UnoRuntime.queryInterface( XPrintable.class, xComp );
470//              @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
471//              final XPrintJobBroadcaster selection = (XPrintJobBroadcaster) UnoRuntime.queryInterface(XPrintJobBroadcaster.class, xprintable);
472                final XPrintJobBroadcaster selection = UnoRuntime.queryInterface(XPrintJobBroadcaster.class, xprintable);
473                final MyPrintJobListener listener = new MyPrintJobListener();
474                selection.addPrintJobListener( listener );
475
476                final PropertyValue[] tmpProps = new PropertyValue[1];
477                tmpProps[0] = new PropertyValue();
478                tmpProps[0].Name = "Name";
479                // 5.1.2.0 (2010/01/01) CentOS等は、OS_INFOがLinux UNKNOWNとなるため、判定条件を変更
480                // OSがLinuxの場合は、プリンタ名称の前後に"<",">"を付加
481                tmpProps[0].Value = "LINUX".indexOf( HybsSystem.sys( "OS_INFO" ).toUpperCase( Locale.JAPAN ) ) >= 0 ? "<" + printer + ">" : printer;
482
483                // 4.3.4.4 (2009/01/01)
484                try {
485                        xprintable.setPrinter( tmpProps );
486                }
487                catch( final IllegalArgumentException ex ) {
488                        throw new HybsSystemException( "印刷時にエラーが発生しました。", ex );
489                }
490
491                // 4.3.7.3 (2009/06/22) 存在しないプリンタを指定した場合は、PropertyValueに
492                // デフォルトプリンターが入るため、引数の値と合致しているかで正しく設定されたかを確認
493                String curPrinter = null;
494                for( final PropertyValue chkProp : xprintable.getPrinter() ) {
495                        if( "Name".equals( chkProp.Name) ) {
496                                curPrinter = (String)chkProp.Value;
497                                break;
498                        }
499                }
500
501//              final PropertyValue[] chkProps = xprintable.getPrinter();
502//              for( int i=0; i<chkProps.length; i++ ) {
503//                      if( "Name".equals( chkProps[i].Name) ) {
504//                              curPrinter = (String)chkProps[i].Value;
505//                              break;
506//                      }
507//              }
508                if( !(printer.equalsIgnoreCase( curPrinter ) ) ) {
509                        final String errMsg = "プリンター[" + printer + "]を発行先に指定できませんでした。" + CR
510                                                        + "存在しないプリンタ名が指定されている可能性があります。";
511                        throw new HybsSystemException( errMsg );
512                }
513
514                // 4.3.0.0 (2008/07/16)
515                final PropertyValue[] printProps = new PropertyValue[1];
516                printProps[0] = new PropertyValue();
517                printProps[0].Name = "Wait";
518                printProps[0].Value = true;
519
520                // 4.3.4.4 (2009/01/01)
521                try {
522                        xprintable.print( printProps );
523                }
524                catch( final IllegalArgumentException ex ) {
525                        throw new HybsSystemException( "印刷時にエラーが発生しました。", ex );
526                }
527
528                // 4.3.0.0 (2008/07/16)
529                // 6.9.7.0 (2018/05/14) PMD Useless parentheses.
530//              if( listener.getStatus() == null
531//                              || listener.getStatus() != PrintableState.JOB_COMPLETED && listener.getStatus() != PrintableState.JOB_SPOOLED ) {
532                if( listener.isError() ) {
533                        throw new HybsSystemException ( "Error Occured while spooling print job. Check Spooler-Service!!!");
534                }
535        }
536
537        /**
538         * プリンタジョブの状況を監視するリスナーです。
539         *
540         * @author Hiroki.Nakamura
541         */
542        private static final class MyPrintJobListener implements XPrintJobListener {
543                private PrintableState status   ;
544
545                /**
546                 * PrintJobEventのステータスを内部変数にセットします。
547                 *
548                 * @param       event   PrintJobEventオブジェクト
549                 */
550                @Override
551                public void printJobEvent( final PrintJobEvent event ) {
552                        status = event.State;
553                }
554
555                /**
556                 * EventObjectの処理を実施します。(ここでは何も行いません。)
557                 *
558                 * @param       event   EventObjectオブジェクト
559                 */
560                @Override
561                public void disposing( final EventObject event ) {
562                        // 何もありません。(PMD エラー回避)
563                }
564
565//              /**
566//               * PrintableStateオブジェクトを返します。
567//               *
568//               * @og.rev 8.0.3.0 (2021/12/17) Delete
569//               *
570//               * @return      PrintableStateオブジェクト
571//               */
572//              public PrintableState getStatus() {
573//                      return status;
574//              }
575
576                /**
577                 * PrintableStateオブジェクトの状態を返します。
578                 *
579                 * statusが nullか、COMPLETEDでなく、SPOOLEDでない場合にエラーと判断します。
580                 *
581                 * @return      エラーの場合、true
582                 */
583                public boolean isError() {
584                        return status == null || status != PrintableState.JOB_COMPLETED && status != PrintableState.JOB_SPOOLED ;
585                }
586        }
587
588        /**
589         * Calc(ods)出力を行います。
590         *
591         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
592         *
593         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
594         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
595         *
596         * @param       outputName      出力ファイル名
597         * @throws Throwable 何らかのエラーが発生した場合。
598         */
599//      public void ods( final String outputName ) throws Throwable {
600//      //      saveDoc( outputName, getFilterName( getSuffix( inputName ), "ods" ) );
601//              saveDoc( outputName, getFilterName( inputName, "ods" ) );
602//      }
603
604//      /**
605//       * Excel(xls)出力を行います。
606//       *
607//       * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
608//       *
609//       * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
610//       * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
611//       *
612//       * @param       outputName      出力ファイル名
613//       * @throws Throwable 何らかのエラーが発生した場合。
614//       */
615//      public void xls( final String outputName ) throws Throwable {
616//      //      saveDoc( outputName, getFilterName( getSuffix( inputName ), "xls" ) );
617//              saveDoc( outputName, getFilterName( inputName, "xls" ) );
618//      }
619
620//      /**
621//       * Writer(ods)出力を行います。
622//       *
623//       * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
624//       *
625//       * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
626//       * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
627//       *
628//       * @param       outputName      出力ファイル名
629//       * @throws Throwable 何らかのエラーが発生した場合。
630//       */
631//      public void odt( final String outputName ) throws Throwable {
632//      //      saveDoc( outputName, getFilterName( getSuffix( inputName ), "odt" ) );
633//              saveDoc( outputName, getFilterName( inputName, "odt" ) );
634//      }
635
636//      /**
637//       * Word(doc)出力を行います。
638//       *
639//       * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
640//       *
641//       * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
642//       * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
643//       *
644//       * @param       outputName      出力ファイル名
645//       * @throws Throwable 何らかのエラーが発生した場合。
646//       */
647//      public void doc( final String outputName ) throws Throwable {
648//      //      saveDoc( outputName, getFilterName( getSuffix( inputName ), "doc" ) );
649//              saveDoc( outputName, getFilterName( inputName, "doc" ) );
650//      }
651
652//      /**
653//       * Impress(odp)出力を行います。
654//       *
655//       * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
656//       *
657//       * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
658//       * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
659//       *
660//       * @param       outputName      出力ファイル名
661//       * @throws Throwable 何らかのエラーが発生した場合。
662//       */
663//      public void odp( final String outputName ) throws Throwable {
664//      //      saveDoc( outputName, getFilterName( getSuffix( inputName ), "odp" ) );
665//              saveDoc( outputName, getFilterName( inputName, "odp" ) );
666//      }
667
668//      /**
669//       * PowerPoint(ppt)出力を行います。
670//       *
671//       * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
672//       *
673//       * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
674//       * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
675//       *
676//       * @param       outputName      出力ファイル名
677//       * @throws Throwable 何らかのエラーが発生した場合。
678//       */
679//      public void ppt( final String outputName ) throws Throwable {
680//      //      saveDoc( outputName, getFilterName( getSuffix( inputName ), "ppt" ) );
681//              saveDoc( outputName, getFilterName( inputName, "ppt" ) );
682//      }
683
684        /**
685         * PDF出力を行います。
686         *
687         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
688         *
689         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
690         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
691         *
692         * @og.rev 8.1.0.3 (2022/01/21) checkOutputメソッドは呼出元で判定する。
693         *
694         * @param       outputName      出力ファイル名
695         * @param       pdfPasswd       PDFパスワード
696         * @throws Throwable 何らかのエラーが発生した場合。
697         */
698        public void pdf( final String outputName, final String pdfPasswd ) throws Throwable {
699                if( !checkOutput( outputName ) ){ return; }                     // 8.1.0.3 (2022/01/21)
700
701        //      savePdf( outputName, getFilterName( getSuffix( inputName ), "pdf" ), pdfPasswd );
702                savePdf( outputName, getFilterName( inputName, "pdf" ), pdfPasswd );
703        }
704
705        //      /**
706        //       * 【保留】Excel2007(xlsx)出力を行います。
707        //       *
708        //       * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
709        //       *
710        //       * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
711        //       * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
712        //       *
713        //       * @og.rev 8.0.3.0 (2021/12/17) ods→xlsx変換時のシート毎の行数
714        //       *
715        //       * @param       outputName      出力ファイル名
716        //       * @param       sheetRows       シート毎の行数List
717        //       * @throws Throwable 何らかのエラーが発生した場合。
718        //       */
719        //      public void xlsx( final String outputName,final List<Integer> sheetRows ) throws Throwable {
720        //              saveDoc( outputName, getFilterName( inputName, "xlsx" ) );
721        //
722        //              // 余分な行やカラムを除去(ただし、ものすごく遅い)
723        //              final File outputFile = new File( outputName );
724        //              final ExcelModel excel = new ExcelModel( outputFile , true );
725        //              excel.activeWorkbook( sheetRows );
726        //              excel.saveFile( outputFile );
727        //      }
728
729        /**
730         * 出力ファイルから出力形式を自動判別し、変換を行います。
731         *
732         * 入出力形式で未対応の場合(形式は入出力ファイルの拡張子で判別)、例外が発行されます。
733         *
734         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
735         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
736         *
737         * @og.rev 8.1.0.3 (2022/01/21) checkOutputメソッドは呼出元で判定する。
738         *
739         * @param       outputName      出力ファイル名
740         * @throws Throwable 何らかのエラーが発生した場合。
741         */
742        public void auto( final String outputName ) throws Throwable {
743                if( !checkOutput( outputName ) ){ return; }                     // 8.1.0.3 (2022/01/21)
744
745                final String outSuffix = getSuffix( outputName );
746                if( "pdf".equalsIgnoreCase( outSuffix ) ) {
747//                      savePdf( outputName, getFilterName( getSuffix( inputName ), outSuffix ), null );
748                        savePdf( outputName, getFilterName( inputName, outSuffix ), null );
749                }
750                else {
751//                      saveDoc( outputName, getFilterName( getSuffix( inputName ), outSuffix ) );
752                        saveDoc( outputName, getFilterName( inputName, outSuffix ) );
753                }
754        }
755
756        /**
757         * フィルター名を指定して、各種ファイル形式に出力を行います。
758         *
759         * @og.rev 8.1.0.3 (2022/01/21) checkOutputメソッドは呼出元で判定する。
760         *
761         * @param       outputName      出力ファイル名
762         * @param       filter          フィルター名
763         */
764        private void saveDoc( final String outputName, final String filter ) {
765//              if( !checkOutput( outputName ) ){ return; }
766
767                final PropertyValue[] storeProps = new PropertyValue[1];
768                storeProps[0] = new PropertyValue();
769                storeProps[0].Name = "FilterName";
770                storeProps[0].Value = filter;
771
772                final String url = "file:///" + outputName.replace( '\\', '/' );
773        //      if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい(2)" ); }
774//              @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
775//              final XStorable xstorable = (XStorable) UnoRuntime.queryInterface( XStorable.class, xComp );
776                final XStorable xstorable = UnoRuntime.queryInterface( XStorable.class, xComp );
777                try {
778                        xstorable.storeAsURL( url, storeProps );
779                }
780                catch( final Throwable th ) {
781                        throw new HybsSystemException( "ファイルへの変換時にエラーが発生しました。[filter=" + filter + "]", th );
782                }
783        }
784
785        /**
786         * フィルターを指定してPDF出力を行います。
787         *
788         * @og.rev 8.1.0.3 (2022/01/21) checkOutputメソッドは呼出元で判定する。
789         *
790         * @param       outputName      出力ファイル名
791         * @param       filter          フィルター名
792         * @param       pdfPasswd       PDFパスワード
793         */
794        private void savePdf( final String outputName, final String filter, final String pdfPasswd ) {
795//              if( !checkOutput( outputName ) ){ return; }
796
797                final PropertyValue[] storeProps;
798                if( pdfPasswd == null || pdfPasswd.isEmpty() ) {
799                        storeProps = new PropertyValue[1];
800                        storeProps[0] = new PropertyValue();
801                        storeProps[0].Name = "FilterName";
802                        storeProps[0].Value = filter;
803                }
804                // 帳票要求テーブルでPDFパスワードが設定されている場合
805                else {
806                        final PropertyValue[] filterProps = new PropertyValue[2];
807                        filterProps[0] = new PropertyValue();
808                        filterProps[0].Name = "EncryptFile";
809                        filterProps[0].Value = true;
810                        filterProps[1] = new PropertyValue();
811                        filterProps[1].Name = "DocumentOpenPassword";
812                        filterProps[1].Value = pdfPasswd;
813
814                        storeProps = new PropertyValue[2];
815                        storeProps[0] = new PropertyValue();
816                        storeProps[0].Name = "FilterName";
817                        storeProps[0].Value = "calc_pdf_Export";
818                        storeProps[1] = new PropertyValue();
819                        storeProps[1].Name = "FilterData";
820                        storeProps[1].Value = filterProps;
821                }
822
823                final String url = "file:///" + outputName.replace( '\\', '/' );
824        //      if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい(3)" ); }
825//              @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
826//              final XStorable xstorable = (XStorable) UnoRuntime.queryInterface( XStorable.class, xComp );
827                final XStorable xstorable = UnoRuntime.queryInterface( XStorable.class, xComp );
828                try {
829                        xstorable.storeToURL( url, storeProps );
830                }
831                catch( final Throwable th ) {
832                        final String err = "PDFファイルへの変換時にエラーが発生しました。[filter=" + filter + "]"
833                                                        + " URL=" + url + " , storeProps=" + storeProps + " , xComp=" + xComp ;
834
835//                      throw new HybsSystemException( "PDFファイルへの変換時にエラーが発生しました。[filter=" + filter + "]", th );
836                        throw new HybsSystemException( err, th );
837                }
838        }
839
840        /**
841         * 出力ファイルのチェックを行います。
842         *
843         * @og.rev 8.1.0.3 (2022/01/21) ファイルの拡張子が同じ場合は、copyで対応する。
844         *
845         * @param       outputName      出力ファイル名
846         *
847         * @return      処理対象かどうか(入力ファイルと出力ファイルが同じ場合は、falseが返ります)
848         */
849        private boolean checkOutput( final String outputName ) {
850                if( outputName == null || outputName.isEmpty() ) {
851                        throw new HybsSystemException( "出力ファイルが指定されていません。" );
852                }
853
854                try {
855                        // 8.1.0.3 (2022/01/21) 出力ファイルがあれば、無条件削除
856                        final Path outPath = Paths.get( outputName );
857                        if( Files.exists( outPath ) ) { Files.delete( outPath ); }
858//                      if( Files.exists( outPath ) && !Files.deleteIfExists( outPath ) ) {
859//                              throw new HybsSystemException( "出力先の既存ファイルが削除できません。[file=" + outputName + "]" );
860//                      }
861
862                        // 8.1.0.3 (2022/01/21) 拡張子が同じなら、copyする。
863                        if( getSuffix( inputName ).equals( getSuffix( outputName ) ) ) {
864                                final Path inPath = Paths.get( inputName );
865                                Files.copy( inPath , outPath , StandardCopyOption.REPLACE_EXISTING );
866                                return false;
867                        }
868                }
869//              catch( final IOException ex ) {         // 意味不明なエラーが出る。
870                catch( final Throwable ex ) {
871                        throw new HybsSystemException( "ファイルコピーが失敗しました。[in=" + inputName + "],out=" + outputName + "]" ,ex );
872                }
873
874                return true;
875
876//              if( outFile.exists() ) {
877//                      if( inFile.getAbsoluteFile().equals( outFile.getAbsoluteFile() ) ) {
878//                              // 入力と出力が同じファイルの場合な何もしない
879//                              return false;
880//                      }
881//                      else if( !outFile.delete() ) {
882//                              throw new HybsSystemException( "出力先の既存ファイルが削除できません。[file=" + outputName + "]" );
883//                      }
884//              }
885//              return true;
886        }
887
888        /**
889         * 入出力の形式(拡張子)からフィルター名を取得します。
890         *
891         * 入力ファイル名からサフィックスを取り出して、FILTER_MAP からフィルター名を取り出します。
892         *
893         * @og.rev 8.0.1.0 (2021/10/29) メッセージ修正
894         *
895//       * @param       inSuffix        入力拡張子
896         * @param       inputName       入力ファイル名
897         * @param       outSuffix       出力拡張子
898         *
899         * @return      フィルター名
900         */
901//      private static String getFilterName( final String inSuffix, final String outSuffix ) {
902        private static String getFilterName( final String inputName, final String outSuffix ) {
903                final String inSuffix = getSuffix( inputName );
904
905                final String filterName = FILTER_MAP.get( inSuffix + "_" + outSuffix );
906                if( filterName == null ) {
907                        final String errMsg = "入力ファイル=[" + inputName + "] , 出力形式=[" + outSuffix + "]" + CR
908                                                        + "入力形式、出力形式は、以下の対応表に基づき、設定して下さい。" + CR
909                                                        + "入力[Calc(ods)   ,Excel(xls)     ] ⇒ 出力[Calc(ods)   ,Excel(xls,xlsx)     ,PDF]" + CR
910                                                        + "入力[Writer(odt) ,Word(doc)      ] ⇒ 出力[Writer(odt) ,Word(doc,docx)      ,PDF]" + CR
911                                                        + "入力[Impress(odp),PowerPoint(ppt)] ⇒ 出力[Impress(odp),PowerPoint(ppt,pptx),PDF]" + CR
912                                                        + "xlsx,docx,pptx は、MS 2007形式の為、LibreOffice のみ利用できます。" + CR ;
913                        throw new HybsSystemException( errMsg );
914                }
915                return filterName;
916        }
917
918        /**
919         * ファイル名から拡張子(小文字)を求めます。
920         *
921         * @param       fileName        ファイル名
922         *
923         * @return      拡張子(小文字)…存在しない場合は、空文字列
924         */
925        private static String getSuffix( final String fileName ) {
926//              String suffix = null;
927                String suffix = "";
928                if( fileName != null ) {
929                        final int sufIdx = fileName.lastIndexOf( '.' );
930                        if( sufIdx >= 0 ) {
931                                suffix = fileName.substring( sufIdx + 1 ).toLowerCase( Locale.JAPAN );
932                        }
933                }
934                return suffix;
935        }
936
937//      /**
938//       * ドキュメントの変換を行うための簡易メソッドです。
939//       *
940//       * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
941//       *
942//       * @og.rev 8.0.3.0 (2021/12/17) ファイル連結の簡易メソッドは、使用しません。
943//       *
944//       * @param       inputFile       入力ファイル名
945//       * @param       outputFile      出力ファイル名
946//       * @see #convert(String[], String, boolean)
947//       */
948//      public static final void convert( final String inputFile, final String outputFile ) {
949////            convert( StringUtil.csv2Array( inputFile ), outputFile );
950//              convert( StringUtil.csv2Array( inputFile ), outputFile, true ); // 8.0.3.0 (2021/12/17)
951//      }
952
953//      /**
954//       * ドキュメントの変換を行うための簡易メソッドです。
955//       *
956//       * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
957//       *
958//       * @og.rev 8.0.3.0 (2021/12/17) ファイル連結の簡易メソッドは、使用しません。
959//       *
960//       * @param       inputFile       入力ファイル名配列
961//       * @param       outputFile      出力ファイル名
962//       * @see #convert(String[], String, boolean)
963//       */
964//      public static final void convert( final String[] inputFile, final String outputFile ) {
965//              convert( inputFile, outputFile, true );
966//      }
967
968        /**
969         * ドキュメントの変換を行うための簡易メソッドです。
970         *
971         * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
972         *
973         * isOnlineがtrueに指定された場合、soffice.binのプロセスをファクトリークラス経由で生成し、
974         * キャッシュします。
975         * 但し、システムリソースが読み込まれないような、バッチファイルから起動した場合は、この方法は
976         * 利用できないため、isOnlineをfalseに指定する必要があります。
977         *
978         * @og.rev 8.0.3.0 (2021/12/17) ファイル連結の簡易メソッドは、使用しません。
979         *
980         * @param       inputFile       入力ファイル名配列
981         * @param       outputFile      出力ファイル名
982         * @param       isOnline        オンライン(Web環境での使用)かどうか
983         */
984        public static final void convert( final String inputFile[], final String outputFile, final boolean isOnline ) {
985                final DocConverter_OOO dc = new DocConverter_OOO( inputFile, isOnline );
986                try {
987                        dc.open();
988                        dc.auto( outputFile );
989                        dc.close();
990                }
991                catch( final Throwable th ) {
992                        dc.close( true );
993                        throw new HybsSystemException( th );
994                }
995        }
996
997        /**
998         * ドキュメントの変換を行います。
999         *
1000         * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
1001         *
1002         * @og.rev 4.3.1.1 (2008/08/23) mkdirs の戻り値判定
1003         * @og.rev 6.3.9.1 (2015/11/27) A method/constructor shouldnt explicitly throw java.lang.Exception(PMD)。
1004         * @og.rev 8.0.3.0 (2021/12/17) ファイル連結の簡易メソッドは、使用しません。
1005         *
1006         * @param       args    コマンド引数配列
1007         */
1008        public static void main( final String[] args ) {
1009                if( args.length < 2 ) {
1010                        System.out.println( "usage : OdsConverter [inputFile] [outputFile]" );
1011                        return;
1012                }
1013
1014                DocConverter_OOO.convert( new String[] {args[0]}, args[1], false );
1015
1016//              final File input  = new File( args[0] );
1017//              final File output = new File( args[1] );
1018//
1019//              // 4.3.1.1 (2008/08/23) mkdirs の戻り値判定
1020//              if( outPath.mkdirs() ) {
1021//                      System.err.println( args[1] + " の ディレクトリ作成に失敗しました。" );
1022//              }
1023//
1024//              final String absPath = output.getAbsolutePath() + FS ;          // 8.0.3.0 (2021/12/17)
1025//              if( input.isDirectory() ) {
1026//                      final File[] inputFiles = input.listFiles();
1027//                      // 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値を利用している(findbugs)
1028//                      if( inputFiles != null ) {
1029//                              for( final File file : inputFiles ) {
1030//                                      final String inputFile = file.getAbsolutePath();
1031////                                    final String outputFile = output.getAbsolutePath() + File.separator + file.getName().replace( ".xls", ".ods" );
1032//                                      final String outputFile = absPath + file.getName().replace( ".xls", ".ods" );
1033//                                      convert( StringUtil.csv2Array( inputFile ), outputFile, false );
1034//                              }
1035//                      }
1036//              }
1037//              else {
1038//                      final String inputFile = input.getAbsolutePath();
1039////                    final String outputFile = output.getAbsolutePath() + File.separator + input.getName().replace( ".xls", ".ods" );
1040//                      final String outputFile = absPath + input.getName().replace( ".xls", ".ods" );
1041//                      convert( StringUtil.csv2Array( inputFile ), outputFile, false );
1042//              }
1043        }
1044}