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