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