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