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
023import org.opengion.fukurou.util.FileUtil;
024import org.opengion.fukurou.util.StringUtil;
025import org.opengion.hayabusa.common.HybsSystem;
026import org.opengion.hayabusa.common.HybsSystemException;
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.Exception;
040import com.sun.star.uno.UnoRuntime;
041import com.sun.star.util.CloseVetoException;
042import com.sun.star.util.XCloseable;
043import com.sun.star.view.PrintJobEvent;
044import com.sun.star.view.PrintableState;
045import com.sun.star.view.XPrintJobBroadcaster;
046import com.sun.star.view.XPrintJobListener;
047import com.sun.star.view.XPrintable;
048
049/**
050 * OpenOfficeを利用して様々な形式のファイルを読み込み、出力・印刷を行うための変換クラスです。
051 *
052 * 変換を行うことのできる入出力のフォーマット以下の通りです。
053 *
054 * [対応フォーマット]
055 *  入力[Calc(ODS)   ,Excel(XLS)     ] ⇒ 出力[Calc(ODS)   ,Excel(XLS)     ,PDF]
056 *  入力[Writer(ODT) ,Word(DOC)      ] ⇒ 出力[Writer(ODT) ,Word(DOC)      ,PDF]
057 *  入力[Impress(ODP),PowerPoint(PPT)] ⇒ 出力[Impress(ODP),PowerPoint(PPT),PDF]
058 *  入力[ * 上記の全て               ] ⇒ 印刷
059 *
060 * 変換を行うには、以下の2通りの方法があります。
061 * (1)簡易的な変換メソッドを利用する場合
062 *   {@link #convert(String, String)}を利用して、変換を行います。
063 *   この場合、出力形式は、出力ファイルの拡張子に従って自動的に決定されます。
064 *   このため、印刷処理などを行う場合は、(2)の方法で出力して下さい。
065 * (2)段階的に書くメソッドを呼び出して変換する場合
066 *   オブジェクトを生成した後、{@link #open()}、#(各種変換メソッド)、{@link #clone()}を
067 *   順番に呼び出して変換を行います。
068 *   この場合、出力形式は、それに対応するメソッドを呼び出ることで決定されます。
069 *
070 *   また、変換を行うための、各種メソッドは、例外としてThrowableを投げるように定義されています。
071 *   このクラスを利用する場合は、このThrowableをcatchし、catch句で、必ず{@link #close( boolean )}に、
072 *   "true"(エラー発生時のクローズ処理)を指定して、終了処理を行って下さい。
073 *   (これを行わない場合、OpenOfficeの不要なプロセスが残ってしまう可能性があります)
074 *
075 * また、出力ファイルが既に存在する場合、出力ファイルは一旦削除された後、処理されます。
076 * なお、入力ファイルと出力ファイルが同じ場合、何も処理されません。(例外も発行されません)
077 *
078 * 入力ファイルを、カンマ区切りで複数指定した場合、複数の入力ファイルをマージして出力します。
079 * ※1 現状は、ファイルのマージは、入力ファイルがExcelまたはCalcの場合のみ対応しています。
080 *
081 * @og.group 帳票システム
082 *
083 * @version  4.0
084 * @author   Hiroki.Nakamura
085 * @since    JDK1.6
086 */
087public class DocConverter_OOO {
088
089        final private boolean                   isOnline;                       // オンライン処理かどうか(オンライン処理の場合、プロセスはファクトリクラス経由で生成されます)
090        final private String[]                  mergeFile;
091
092        private String                                  inputName;
093        private String                                  origName;
094
095        private XComponent                              xComp;                          // 5.1.8.0 (2010/07/01) メソッドと重なる変数名の変更
096        private SOfficeProcess                  soffice;
097
098        // 入力、出力の拡張子とこれに対応するフィルター名
099        static final private Map<String,String> filterMap       = new HashMap<String,String>();
100        static {
101                filterMap.put( "ods_ods", "calc8" );
102                filterMap.put( "xls_ods", "calc8" );
103                filterMap.put( "ods_xls", "MS Excel 97" );
104                filterMap.put( "xls_xls", "MS Excel 97" );
105                filterMap.put( "ods_pdf", "calc_pdf_Export" );
106                filterMap.put( "xls_pdf", "calc_pdf_Export" );
107                filterMap.put( "odt_odt", "writer8" );
108                filterMap.put( "doc_odt", "writer8" );
109                filterMap.put( "odt_doc", "MS Word 97" );
110                filterMap.put( "doc_doc", "MS Word 97" );
111                filterMap.put( "odt_pdf", "writer_pdf_Export" );
112                filterMap.put( "doc_pdf", "writer_pdf_Export" );
113                filterMap.put( "odp_odp", "impress8" );
114                filterMap.put( "ppt_odp", "impress8" );
115                filterMap.put( "odp_ppt", "MS PowerPoint 97" );
116                filterMap.put( "ppt_ppt", "MS PowerPoint 97" );
117                filterMap.put( "odp_pdf", "impress_pdf_Export" );
118                filterMap.put( "ppt_pdf", "impress_pdf_Export" );
119        };
120
121        /**
122         * コンストラクタです。
123         *
124         * #DocConverter(input, true)と同じです。
125         *
126         * @param input ファイル一覧(カンマ区切り)
127         * @see #DocConverter_OOO(String[])
128         */
129        public DocConverter_OOO( final String input ) {
130                this( StringUtil.csv2Array( input ), true );
131        }
132
133        /**
134         * コンストラクタです。
135         *
136         * #DocConverter(input, true)と同じです。
137         *
138         * @param input ファイル一覧(配列)
139         * @see #DocConverter_OOO(String[], boolean)
140         */
141        public DocConverter_OOO( final String input[] ) {
142                this( input, true );
143        }
144
145        /**
146         * コンストラクタです。
147         *
148         * isOnline(isOl)がtrueに指定された場合、soffice.binのプロセスをファクトリークラス経由で生成し、
149         * キャッシュします。
150         * 但し、システムリソースが読み込まれないような、バッチファイルから起動した場合は、この方法は
151         * 利用できないため、isOnlineをfalseに指定する必要があります。
152         *
153         * @param input ファイル一覧(配列)
154         * @param isOl オンライン(Web環境での使用)かどうか
155         */
156        public DocConverter_OOO( final String input[], final boolean isOl ) {
157                if( input == null || input.length == 0 || input[0].length() == 0 ) {
158                        throw new HybsSystemException( "入力ファイルが指定されていません。" );
159                }
160                File inFile = new File( input[0] );
161                if( !inFile.exists() ) {
162                        throw new HybsSystemException( "入力ファイルが存在しません。[file=" + input[0] + "]" );
163                }
164                isOnline = isOl;
165                inputName = input[0];
166                origName = input[0];
167
168                if( input.length == 1 ) {
169                        mergeFile = null;
170                }
171                else {
172                        if( !"xls".equals( getSuffix( input[0] ) ) && !"ods".equals( getSuffix( input[0] ) ) ) {
173                                throw new HybsSystemException( "ファイルのマージを行う場合、入力ファイルは、ExcelまたはCacl形式である必要があります。" );
174                        }
175
176                        mergeFile = new String[input.length-1];
177                        for( int i=0; i<mergeFile.length; i++ ) {
178                                if( input[i+1].length() == 0 || !( new File( input[i+1] ) ).exists() ) {
179                                        throw new HybsSystemException( "マージファイルが指定されていないか、または存在しません。[file=" + input[i+1] + "]" );
180                                }
181                                if( inputName.equals( input[i] + 1 ) ) {
182                                        throw new HybsSystemException( "マージファイルに入力ファイルと同じファイルが指定されてます。[file=" + input[i+1] + "]" );
183                                }
184                                if( !"xls".equals( getSuffix( input[i+1] ) ) && !"ods".equals( getSuffix( input[i+1] ) ) ) {
185                                        throw new HybsSystemException( "ファイルのマージを行う場合、マージファイルは、ExcelまたはCacl形式である必要があります。" );
186                                }
187                                mergeFile[i] = input[i+1];
188                        }
189                }
190        }
191
192        /**
193         * SOficeのコンポーネントを起動します。
194         *
195         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
196         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
197         *
198         * @og.rev 5.1.7.0 (2010/06/01) マージ処理対応
199         *
200         * @throws Throwable 何らかのエラーが発生した場合。
201         * @see #close()
202         * @see #close(boolean)
203         */
204        public void open() throws Throwable {
205                // プロセスの取得
206                if( soffice == null ) {
207                        if( isOnline ) {
208                                soffice = ProcessFactory.newInstance();
209                        }
210                        else {
211                                soffice = new SOfficeProcess( "docconverter.class" );
212                                soffice.bootstrap();
213                        }
214
215                        // マージする場合は、マージ対象のファイルをテンポラリにコピーする(readOnly回避のため)
216                        // テンプレート(無題)として上げると、シートコピー先として特定できなくなるため
217                        if( mergeFile != null ) {
218                                File origFile = new File( origName );
219                                inputName = soffice.getTempPath() + System.currentTimeMillis() + "_" + origFile.getName();
220                                FileUtil.copy( origFile, new File( inputName ) );
221                        }
222                }
223
224                // 5.1.7.0 (2010/06/01) マージ処理対応
225                xComp = getComponent( inputName, ( mergeFile == null ? true : false ), false );
226
227                if( mergeFile != null ) {
228                        for( int i=0; i<mergeFile.length; i++ ) {
229                                merge( mergeFile[i] );
230                        }
231                }
232        }
233
234        /**
235         * ドキュメントコンポーネントを取得します。
236         *
237         * @param       input                   ファイル名
238         * @param       isHidden                隠し属性[true/false]
239         * @param       isAsTemplate    OpenOffice上のTemplate属性[true/false]
240         *
241         * @return      ドキュメントコンポーネント
242         */
243        @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
244        private XComponent getComponent( final String input, final boolean isHidden, final boolean isAsTemplate ) {
245                PropertyValue[] calcProps = new PropertyValue[2];
246                calcProps[0] = new PropertyValue();
247                calcProps[0].Name = "Hidden";
248                calcProps[0].Value = isHidden;
249                calcProps[1] = new PropertyValue();
250                calcProps[1].Name = "AsTemplate";
251                calcProps[1].Value = isAsTemplate;
252
253                String url = "file:///" + input.replace( '\\', '/' );
254
255                XComponent rtnDoc;
256                XComponentLoader cloader = (XComponentLoader) UnoRuntime.queryInterface( XComponentLoader.class, soffice.getDesktop() );
257                try {
258                        rtnDoc = cloader.loadComponentFromURL( url, "_blank", 0, calcProps );
259                }
260                catch( IOException ex ) {
261                        throw new HybsSystemException( "OpenOfficeの立ち上げ時にエラーが発生しました(入出力エラー)。", ex );
262                }
263                catch( IllegalArgumentException ex ) {
264                        throw new HybsSystemException( "OpenOfficeの立ち上げ時にエラーが発生しました(パラメーター不正)。", ex );
265                }
266                return rtnDoc;
267        }
268
269        /**
270         * ドキュメント(xls,ods)のマージを行います。
271         *
272         * @param mergeInputName マージ対象のファイル名
273         */
274        @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
275        private void merge( final String mergeInputName ) {
276                // マージする副ファイルは、テンプレート(無題)として上げる(readOnly回避のため)
277                XComponent subDoc = getComponent( mergeInputName, false, true );
278
279                XDispatchProvider dispatchProvider
280                        = (XDispatchProvider)UnoRuntime.queryInterface( XDispatchProvider.class
281                                ,((XController)UnoRuntime.queryInterface( XController.class
282                                        ,((XModel)UnoRuntime.queryInterface( XModel.class
283                                                ,subDoc
284                                        )).getCurrentController()
285                                )).getFrame()
286                        );
287
288                XDispatchHelper xDispatchHelper = soffice.getDispatcher();
289                xDispatchHelper.executeDispatch(dispatchProvider, ".uno:TableSelectAll", "", 0, new PropertyValue[0]);
290
291                String title = new File( inputName ).getName() ;
292                title = title.substring( 0, title.indexOf( '.' ) );
293
294                PropertyValue[] moveProps = new PropertyValue[3];
295                moveProps[0] = new PropertyValue();
296                moveProps[0].Name = "DocName";
297                moveProps[0].Value = title;
298                moveProps[1] = new PropertyValue();
299                moveProps[1].Name = "Index";
300                moveProps[1].Value = 32767;
301                moveProps[2] = new PropertyValue();
302                moveProps[2].Name = "Copy";
303                moveProps[2].Value = true;
304                xDispatchHelper.executeDispatch(dispatchProvider, ".uno:Move", "", 0, moveProps);
305
306                closeComponent( subDoc );
307        }
308
309        /**
310         * Calcコンポーネントをクローズします。
311         *
312         * このクローズ処理は、処理が正常終了した場合に呼び出しする必要があります。
313         * 例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
314         *
315         * このメソッドは#close(false)と同じです。
316         *
317         * @throws Throwable 何らかのエラーが発生した場合。
318         * @see #close(boolean)
319         */
320        public void close() throws Throwable  {
321                close( false );
322        }
323
324        /**
325         * Calcコンポーネントをクローズします。
326         *
327         * 引数のisErrがtrueの場合、この変換オブジェクトで生成されたプロセスは強制的に破棄されます。
328         * falseの場合は、プロセスは、ファクトリクラスを経由して、キャッシュに戻されます。
329         * (バッチ処理の場合は、いずれの場合も、プロセスは強制的に破棄されます)
330         *
331         * 起動から変換、クローズまでの書く処理で例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
332         *
333         * #close(false)は#close()と同じであるため、通常利用することはありません。
334         *
335         * @og.rev 4.2.4.1 (2008/07/07 ) 終了処理を60回で終わるように修正
336         * @og.rev 4.3.0.0 (2008/07/15 ) ↑は6秒しか待っていなかったので、60秒待つように修正
337         *
338         * @param       isErr   trueの場合、この変換オブジェクトで生成されたプロセスは強制的に破棄されます。
339         */
340        @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
341        public void close( final boolean isErr ) {
342                if( xComp != null ) {
343                        closeComponent( xComp );
344                }
345
346                if( soffice != null ) {
347                        if( isOnline ) {
348                                if( isErr ) {
349                                        ProcessFactory.remove( soffice );
350                                }
351                                else {
352                                        ProcessFactory.release( soffice );
353                                }
354                        }
355                        else {
356                                soffice.close();
357                        }
358                }
359
360                // マージした場合は、テンポラリにコピーしたファイルを削除
361                // 6.0.0.1 (2014/04/25) These nested if statements could be combined
362                if( mergeFile != null && ! ( new File( inputName ) ).delete() ) {
363                        System.err.println( "テンポラリにコピーしたファイルを削除できませんでした。[" + inputName + "]" );
364                }
365        }
366
367        /**
368         * ドキュメントコンポーネントをクローズします。
369         *
370         * @param comp ドキュメントコンポーネント
371         */
372        @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
373        private void closeComponent( final XComponent comp ) {
374                XCloseable closeable = null;
375                for( int i = 0;; ++i ) {
376                        try {
377                                closeable = (XCloseable) UnoRuntime.queryInterface( XCloseable.class, comp );
378                                closeable.close( true );
379                                break;
380                        }
381                        catch( CloseVetoException ex ) {
382                                // 4.2.4.1 (2008/07/07 )
383                                // 4.3.4.4 (2009/01/01)
384                                if( i == 600 ) { throw new HybsSystemException( "sofficeプロセスに接続できません。", ex ); }
385                                try {
386                                        Thread.sleep( 100 );
387                                }
388                                catch( InterruptedException ex2 ) {
389                        //              throw new HybsSystemException( ex2 );
390                                }
391                        }
392                }
393        }
394
395        /**
396         * 印刷を行います。
397         *
398         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
399         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
400         *
401         * @og.rev 4.3.0.0 (2008/07/16) スプールが終わるまでwaitし、さらにプリンタ発行の状況を監視し、正常終了かどうかを判断
402         * @og.rev 4.3.7.3 (2009/06/22) 存在しないプリンターを指定した場合のエラーハンドリングを追加
403         * @og.rev 5.1.2.0 (2010/01/01) CentOS等は、OS_INFOがLinux UNKNOWNとなるため、判定条件を変更
404         *
405         * @param       printer プリンター名
406         * @throws Throwable 何らかのエラーが発生した場合。
407         */
408        public void print( final String printer ) throws Throwable {
409                if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい" ); }
410
411                if( printer == null || printer.length() == 0 ) {
412                        throw new HybsSystemException( "プリンターが指定されていません。" );
413                }
414
415                @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
416                XPrintable xprintable = (XPrintable) UnoRuntime.queryInterface( XPrintable.class, xComp );
417                @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
418                XPrintJobBroadcaster selection = (XPrintJobBroadcaster) UnoRuntime.queryInterface(XPrintJobBroadcaster.class, xprintable);
419                MyPrintJobListener listener = new MyPrintJobListener ();
420                selection.addPrintJobListener( listener );
421
422                PropertyValue[] tmpProps = new PropertyValue[1];
423                tmpProps[0] = new PropertyValue();
424                tmpProps[0].Name = "Name";
425                // 5.1.2.0 (2010/01/01) CentOS等は、OS_INFOがLinux UNKNOWNとなるため、判定条件を変更
426                // OSがLinuxの場合は、プリンタ名称の前後に"<",">"を付加
427                tmpProps[0].Value = "LINUX".indexOf( HybsSystem.sys( "OS_INFO" ).toUpperCase( Locale.JAPAN ) ) >= 0 ? "<" + printer + ">" : printer;
428
429                // 4.3.4.4 (2009/01/01)
430                try {
431                        xprintable.setPrinter( tmpProps );
432                }
433                catch ( IllegalArgumentException ex ) {
434                        throw new HybsSystemException( "印刷時にエラーが発生しました。", ex );
435                }
436
437                // 4.3.7.3 (2009/06/22) 存在しないプリンタを指定した場合は、PropertyValueに
438                // デフォルトプリンターが入るため、引数の値と合致しているかで正しく設定されたかを確認
439                String curPrinter = null;
440                PropertyValue[] chkProps = xprintable.getPrinter();
441                for( int i=0; i<chkProps.length; i++ ) {
442                        if( "Name".equals( chkProps[i].Name) ) {
443                                curPrinter = (String)chkProps[i].Value;
444                                break;
445                        }
446                }
447                if( !(printer.equalsIgnoreCase( curPrinter ) ) ) {
448                        String errMsg = "プリンター[" + printer + "]を発行先に指定できませんでした。" + HybsSystem.CR
449                                                        + "存在しないプリンタ名が指定されている可能性があります。";
450                        throw new HybsSystemException( errMsg );
451                }
452
453                // 4.3.0.0 (2008/07/16)
454                PropertyValue[] printProps = new PropertyValue[1];
455                printProps[0] = new PropertyValue();
456                printProps[0].Name = "Wait";
457                printProps[0].Value = true;
458
459                // 4.3.4.4 (2009/01/01)
460                try {
461                        xprintable.print( printProps );
462                }
463                catch ( IllegalArgumentException ex ) {
464                        throw new HybsSystemException( "印刷時にエラーが発生しました。", ex );
465                }
466
467                // 4.3.0.0 (2008/07/16)
468                if( listener.getStatus() == null
469                                || ( listener.getStatus() != PrintableState.JOB_COMPLETED && listener.getStatus() != PrintableState.JOB_SPOOLED ) ){
470                        throw new HybsSystemException ( "Error Occured while spooling print job. Check Spooler-Service!!!");
471                }
472        }
473
474        /**
475         * プリンタジョブの状況を監視するリスナーです。
476         *
477         * @author Hiroki.Nakamura
478         */
479        private static class MyPrintJobListener implements XPrintJobListener {
480                private PrintableState status = null;
481
482                @Override
483                public void printJobEvent( final PrintJobEvent event ) {
484                        status = event.State;
485                }
486
487                @Override
488                public void disposing( final EventObject event ) {
489                        // 何もありません。(PMD エラー回避)
490                }
491
492                public PrintableState getStatus() {
493                        return status;
494                }
495        }
496
497        /**
498         * PDF出力を行います。
499         *
500         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
501         *
502         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
503         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
504         *
505         * @param       outputName      出力ファイル名
506         * @param       pdfPasswd       PDFパスワード
507         * @throws Throwable 何らかのエラーが発生した場合。
508         */
509        public void pdf( final String outputName, final String pdfPasswd ) throws Throwable  {
510                savePdf( outputName, getFilterName( getSuffix( inputName ), "pdf" ), pdfPasswd );
511        }
512
513        /**
514         * Calc(ods)出力を行います。
515         *
516         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
517         *
518         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
519         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
520         *
521         * @param       outputName      出力ファイル名
522         * @throws Throwable 何らかのエラーが発生した場合。
523         */
524        public void ods( final String outputName ) throws Throwable  {
525                saveDoc( outputName, getFilterName( getSuffix( inputName ), "ods" ) );
526        }
527
528        /**
529         * Excel(xls)出力を行います。
530         *
531         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
532         *
533         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
534         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
535         *
536         * @param       outputName      出力ファイル名
537         * @throws Throwable 何らかのエラーが発生した場合。
538         */
539        public void xls( final String outputName ) throws Throwable {
540                saveDoc( outputName, getFilterName( getSuffix( inputName ), "xls" ) );
541        }
542
543        /**
544         * Writer(ods)出力を行います。
545         *
546         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
547         *
548         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
549         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
550         *
551         * @param       outputName      出力ファイル名
552         * @throws Throwable 何らかのエラーが発生した場合。
553         */
554        public void odt( final String outputName ) throws Throwable {
555                saveDoc( outputName, getFilterName( getSuffix( inputName ), "odt" ) );
556        }
557
558        /**
559         * Word(doc)出力を行います。
560         *
561         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
562         *
563         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
564         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
565         *
566         * @param       outputName      出力ファイル名
567         * @throws Throwable 何らかのエラーが発生した場合。
568         */
569        public void doc( final String outputName ) throws Throwable {
570                saveDoc( outputName, getFilterName( getSuffix( inputName ), "doc" ) );
571        }
572
573        /**
574         * Impress(odp)出力を行います。
575         *
576         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
577         *
578         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
579         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
580         *
581         * @param       outputName      出力ファイル名
582         * @throws Throwable 何らかのエラーが発生した場合。
583         */
584        public void odp( final String outputName ) throws Throwable {
585                saveDoc( outputName, getFilterName( getSuffix( inputName ), "odp" ) );
586        }
587
588        /**
589         * PowerPoint(ppt)出力を行います。
590         *
591         * 入力形式で未対応の場合(形式は入力ファイルの拡張子で判別)、例外が発行されます。
592         *
593         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
594         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
595         *
596         * @param       outputName      出力ファイル名
597         * @throws Throwable 何らかのエラーが発生した場合。
598         */
599        public void ppt( final String outputName ) throws Throwable {
600                saveDoc( outputName, getFilterName( getSuffix( inputName ), "ppt" ) );
601        }
602
603        /**
604         * 出力ファイルから出力形式を自動判別し、変換を行います。
605         *
606         * 入出力形式で未対応の場合(形式は入出力ファイルの拡張子で判別)、例外が発行されます。
607         *
608         * 正常に処理が終了した場合は、#close()を発行し、終了処理を行って下さい。
609         * また、例外が発生した場合は、#close(true)を発行し、終了処理を行って下さい。
610         *
611         * @param       outputName      出力ファイル名
612         * @throws Throwable 何らかのエラーが発生した場合。
613         */
614        public void auto( final String outputName ) throws Throwable {
615                String outSuffix = getSuffix( outputName );
616                if( "pdf".equalsIgnoreCase( outSuffix ) ) {
617                        savePdf( outputName, getFilterName( getSuffix( inputName ), outSuffix ), null );
618                }
619                else {
620                        saveDoc( outputName, getFilterName( getSuffix( inputName ), outSuffix ) );
621                }
622        }
623
624        /**
625         * フィルター名を指定して、各種ファイル形式に出力を行います。
626         *
627         * @param       outputName      出力ファイル名
628         * @param       filter          フィルター名
629         */
630        private void saveDoc(  final String outputName, final String filter ) {
631                if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい" ); }
632                if( !checkOutput( outputName ) ){ return; }
633
634                PropertyValue[] storeProps = new PropertyValue[1];
635                storeProps[0] = new PropertyValue();
636                storeProps[0].Name = "FilterName";
637                storeProps[0].Value = filter;
638
639                String url = "file:///" + outputName.replace( '\\', '/' );
640                @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
641                XStorable xstorable = (XStorable) UnoRuntime.queryInterface( XStorable.class, xComp );
642                try {
643                        xstorable.storeAsURL( url, storeProps );
644                }
645                catch ( Throwable th ) {
646                        throw new HybsSystemException( "ファイルへの変換時にエラーが発生しました。[filter=" + filter + "]", th );
647                }
648        }
649
650        /**
651         * フィルターを指定してPDF出力を行います。
652         *
653         * @param       outputName      出力ファイル名
654         * @param       filter          フィルター名
655         * @param       pdfPasswd       PDFパスワード
656         */
657        private void savePdf( final String outputName, final String filter, final String pdfPasswd ) {
658                if( xComp == null ) { throw new HybsSystemException( "初めに、#open()を実行して下さい" ); }
659                if( !checkOutput( outputName ) ){ return; }
660
661                PropertyValue[] storeProps;
662                if( pdfPasswd == null || pdfPasswd.length() == 0 ) {
663                        storeProps = new PropertyValue[1];
664                        storeProps[0] = new PropertyValue();
665                        storeProps[0].Name = "FilterName";
666                        storeProps[0].Value = filter;
667                }
668                // 帳票要求テーブルでPDFパスワードが設定されている場合
669                else {
670                        PropertyValue[] filterProps = new PropertyValue[2];
671                        filterProps[0] = new PropertyValue();
672                        filterProps[0].Name = "EncryptFile";
673                        filterProps[0].Value = true;
674                        filterProps[1] = new PropertyValue();
675                        filterProps[1].Name = "DocumentOpenPassword";
676                        filterProps[1].Value = pdfPasswd;
677
678                        storeProps = new PropertyValue[2];
679                        storeProps[0] = new PropertyValue();
680                        storeProps[0].Name = "FilterName";
681                        storeProps[0].Value = "calc_pdf_Export";
682                        storeProps[1] = new PropertyValue();
683                        storeProps[1].Name = "FilterData";
684                        storeProps[1].Value = filterProps;
685                }
686
687                String url = "file:///" + outputName.replace( '\\', '/' );
688                @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
689                XStorable xstorable = (XStorable) UnoRuntime.queryInterface( XStorable.class, xComp );
690                try {
691                        xstorable.storeToURL( url, storeProps );
692                }
693                catch ( Throwable th ) {
694                        throw new HybsSystemException( "PDFファイルへの変換時にエラーが発生しました。[filter=" + filter + "]", th );
695                }
696        }
697
698        /**
699         * 出力ファイルのチェックを行います。
700         *
701         * @param       outputName      出力ファイル名
702         *
703         * @return      処理対象かどうか(入力ファイルと出力ファイルが同じ場合は、falseが返ります)
704         */
705        private boolean checkOutput( final String outputName ) {
706                if( outputName == null || outputName.length() == 0 ) {
707                        throw new HybsSystemException( "出力ファイルが指定されていません。" );
708                }
709
710                File inFile = new File( inputName );
711                File outFile = new File( outputName );
712
713                if( outFile.exists() ) {
714                        if( inFile.getAbsoluteFile().equals( outFile.getAbsoluteFile() ) ) {
715                                // 入力と出力が同じファイルの場合な何もしない
716                                return false;
717                        }
718                        else if( !outFile.delete() ) {
719                                throw new HybsSystemException( "出力先の既存ファイルが削除できません。[file=" + outputName + "]" );
720                        }
721                }
722                return true;
723        }
724
725        /**
726         * 入出力の形式(拡張子)からフィルター名を取得します。
727         *
728         * @param       inSuffix        入力拡張子
729         * @param       outSuffix       出力拡張子
730         *
731         * @return      フィルター名
732         */
733        private static String getFilterName( final String inSuffix, final String outSuffix ) {
734                String filterName = filterMap.get( inSuffix + "_" + outSuffix );
735                if( filterName == null ) {
736                        String errMsg = "入力形式、出力形式は、以下の対応表に基づき、設定して下さい。" + HybsSystem.CR
737                                                        + "入力[Calc(ods)   ,Excel(xls)     ] ⇒ 出力[Calc(ods)   ,Excel(xls)     ,PDF]" + HybsSystem.CR
738                                                        + "入力[Writer(odt) ,Word(doc)      ] ⇒ 出力[Writer(odt) ,Word(doc)      ,PDF]" + HybsSystem.CR
739                                                        + "入力[Impress(odp),PowerPoint(ppt)] ⇒ 出力[Impress(odp),PowerPoint(ppt),PDF]" + HybsSystem.CR;
740                        throw new HybsSystemException( errMsg );
741                }
742                return filterName;
743        }
744
745        /**
746         * ファイル名から拡張子(小文字)を求めます。
747         *
748         * @param       fileName        ファイル名
749         *
750         * @return      拡張子(小文字)
751         */
752        private static String getSuffix( final String fileName ) {
753                String suffix = null;
754                if( fileName != null ) {
755                        int sufIdx = fileName.lastIndexOf( '.' );
756                        if( sufIdx >= 0 ) {
757                                suffix = fileName.substring( sufIdx + 1 ).toLowerCase( Locale.JAPAN );
758                        }
759                }
760                return suffix;
761        }
762
763        /**
764         * ドキュメントの変換を行うための簡易メソッドです。
765         *
766         * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
767         *
768         * @param       inputFile       入力ファイル名
769         * @param       outputFile      出力ファイル名
770         * @see #convert(String[], String, boolean)
771         */
772        public static final void convert( final String inputFile, final String outputFile ) {
773                convert( StringUtil.csv2Array( inputFile ), outputFile );
774        }
775
776        /**
777         * ドキュメントの変換を行うための簡易メソッドです。
778         *
779         * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
780         *
781         * @param       inputFile       入力ファイル名配列
782         * @param       outputFile      出力ファイル名
783         * @see #convert(String[], String, boolean)
784         */
785        public static final void convert( final String[] inputFile, final String outputFile ) {
786                convert( inputFile, outputFile, true );
787        }
788
789        /**
790         * ドキュメントの変換を行うための簡易メソッドです。
791         *
792         * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
793         *
794         * isOnlineがtrueに指定された場合、soffice.binのプロセスをファクトリークラス経由で生成し、
795         * キャッシュします。
796         * 但し、システムリソースが読み込まれないような、バッチファイルから起動した場合は、この方法は
797         * 利用できないため、isOnlineをfalseに指定する必要があります。
798         *
799         * @param       inputFile       入力ファイル名配列
800         * @param       outputFile      出力ファイル名
801         * @param       isOnline        オンライン(Web環境での使用)かどうか
802         */
803        public static final void convert( final String inputFile[], final String outputFile, final boolean isOnline ) {
804                DocConverter_OOO dc = new DocConverter_OOO( inputFile, isOnline );
805                try {
806                        dc.open();
807                        dc.auto( outputFile );
808                        dc.close();
809                }
810                catch ( Throwable th ) {
811                        dc.close( true );
812                        throw new HybsSystemException( th );
813                }
814        }
815
816        /**
817         * ドキュメントの変換を行います。
818         *
819         * 変換方法は、出力ファイルの拡張子により自動的に決定されます。
820         *
821         * @og.rev 4.3.1.1 (2008/08/23) mkdirs の戻り値判定
822         *
823         * @param       args    コマンド引数配列
824         * @throws Exception 何らかのエラーが発生したとき。
825         */
826        public static void main( final String[] args ) throws Exception {
827                if( args.length < 2 ) {
828                        System.out.println( "usage : OdsConverter [inputFile or inputDir] [outputDir]" );
829                        return;
830                }
831
832                File input = new File( args[0] );
833                File output = new File( args[1] );
834
835                // 4.3.1.1 (2008/08/23) mkdirs の戻り値判定
836                if( output.mkdirs() ) {
837                        System.err.println( args[1] + " の ディレクトリ作成に失敗しました。" );
838                }
839
840                if( input.isDirectory() ) {
841                        File[] inputFiles = input.listFiles();
842                        for( int i = 0; i<inputFiles.length; i++ ) {
843                                String inputFile = inputFiles[i].getAbsolutePath();
844                                String outputFile = output.getAbsolutePath() + File.separator + inputFiles[i].getName().replace( ".xls", ".ods" );
845                                convert( StringUtil.csv2Array( inputFile ), outputFile, false );
846                        }
847                }
848                else {
849                        String inputFile = input.getAbsolutePath();
850                        String outputFile = output.getAbsolutePath() + File.separator + input.getName().replace( ".xls", ".ods" );
851                        convert( StringUtil.csv2Array( inputFile ), outputFile, false );
852                }
853        }
854}