    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.util.ArrayList;
    019import java.util.List;
    020
    021import org.opengion.hayabusa.common.HybsSystemException;
    022
    023/**
    024 * シート単位のcontent.xmlを管理するためのクラスです。
    025 * シートのヘッダー、行の配列、フッター及びシート名を管理します。
    026 *
    027 * 7.0.1.5 (2018/12/10)
    028 *   LINECOPYでは、同一行をコピーするため、セルの選択行(A5とか$C7とか)までコピーされ
    029 *   行レベルの計算が出来ません。その場合は、INDIRECT(ADDRESS(ROW(),列番号))関数を
    030 *   使用することでセルのアドレスが指定可能です。
    031 *   列番号は、A=1 です。
    032 *
    033 * @og.group 帳票システム
    034 *
    035 * @version  4.0
    036 * @author   Hiroki.Nakamura
    037 * @since    JDK1.6
    038 */
    039class OdsSheet {
    040
    041        //======== content.xmlのパースで使用 ========================================
    042
    043        /* 行の開始終了タグ */
    044        private static final String ROW_START_TAG = "<table:table-row ";
    045        private static final String ROW_END_TAG = "</table:table-row>";
    046
    047        /* シート名を取得するための開始終了文字 */
    048        private static final String SHEET_NAME_START = "table:name=\"";
    049        private static final String SHEET_NAME_END = "\"";
    050
    051        /* 変数定義の開始終了文字及び区切り文字 */
    052        private static final String VAR_START = "{@";
    053        private static final String VAR_END = "}";
    054        private static final String VAR_CON = "_";
    055
    056        /* ラインコピー文字列 5.0.0.2 (2009/09/15) */
    057        private static final String LINE_COPY = "LINECOPY";
    058
    059        private final List<String>      sheetRows       = new ArrayList<>();
    060        private String                  sheetHeader;
    061        private String                  sheetFooter;
    062        private String                  sheetName;
    063        private String                  origSheetName;
    064        private String                  confSheetName;
    065
    066        /**
    067         * デフォルトコンストラクター
    068         *
    069         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
    070         */
    071        public OdsSheet() { super(); }          // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
    072
    073        /**
    074         * シートを行単位に分解します。
    075         *
    076         * @og.rev 5.0.0.2 (2009/09/15) ボディ部のカウントを引数に追加し、LINECOPY機能実装。
    077         * @og.rev 5.2.1.0 (2010/10/01) シート名定義対応
    078         *
    079         * @param sheet シート名
    080         * @param bodyRowCount 行番号
    081         */
    082        public void analyze( final String sheet, final int bodyRowCount ) {
    083                final String[] tags = TagParser.tag2Array( sheet, ROW_START_TAG, ROW_END_TAG );
    084                sheetHeader = tags[0];
    085                sheetFooter = tags[1];
    086                for( int i=2; i<tags.length; i++ ) {
    087                        sheetRows.add( tags[i] );
    088                        lineCopy( tags[i], bodyRowCount ); // 5.0.0.2 (2009/09/15)
    089                }
    090
    091                sheetName = TagParser.getValueFromTag( sheetHeader, SHEET_NAME_START, SHEET_NAME_END );
    092                origSheetName = sheetName;
    093
    094                confSheetName = null;
    095                if( sheetName != null ) {
    096                        final int cnfIdx = sheetName.indexOf( "__" );
    097                        if( cnfIdx > 0 && !sheetName.endsWith( "__" ) ) {
    098                                confSheetName = sheetName.substring( cnfIdx + 2 );
    099                                sheetName = sheetName.substring( 0, cnfIdx );
    100                        }
    101                }
    102        }
    103
    104        /**
    105         * ラインコピーに関する処理を行います。
    106         *
    107         * {&#064;LINE_COPY}が存在した場合に、テーブルモデル分だけ
    108         * 行をコピーします。
    109         * その際、{&#064;xxx_y}のyをカウントアップしてコピーします。
    110         *
    111         * 整合性等のエラーハンドリングはこのメソッドでは行わず、
    112         * 実際のパース処理中で行います。
    113         *
    114         * 7.0.1.5 (2018/12/10)
    115         *   LINECOPYでは、同一行をコピーするため、セルの選択行(A5とか$C7とか)までコピーされ
    116         *   行レベルの計算が出来ません。その場合は、INDIRECT(ADDRESS(ROW(),列番号))関数を
    117         *   使用することでセルのアドレスが指定可能です。
    118         *   列番号は、A=1 です。
    119         *
    120         * @og.rev 5.0.0.2 (2009/09/15) 追加
    121         * @og.rev 5.1.8.0 (2010/07/01) パース処理の内部実装を変更
    122         * @og.rev 7.0.1.5 (2018/12/10) LINECOPYでの注意(JavaDocのみ追記)
    123         *
    124         * @param row           行データ
    125         * @param rowCount      カウンタ
    126         */
    127        private void lineCopy( final String row, final int rowCount ) {
    128                // この段階で存在しなければ即終了
    129                final int lcStrOffset = row.indexOf( VAR_START + LINE_COPY );
    130                if( lcStrOffset < 0 ) { return; }
    131                final int lcEndOffset = row.indexOf( VAR_END, lcStrOffset );
    132                if( lcEndOffset < 0 ) { return; }
    133
    134                final StringBuilder lcStrBuf = new StringBuilder( row );
    135                final String lcKey = TagParser.checkKey( row.substring( lcStrOffset + VAR_START.length(), lcEndOffset ), lcStrBuf );
    136                if( lcKey == null || !LINE_COPY.equals( lcKey ) ) { return; }
    137
    138                // 存在すればテーブルモデル行数-1回ループ(自身を除くため)
    139                for( int i=1; i<rowCount; i++ ) {
    140                        final int cRow = i;
    141                        final String rowStr = new TagParser() {
    142                                /**
    143                                 * 開始タグから終了タグまでの文字列の処理を定義します。
    144                                 *
    145                                 * @param str 開始タグから終了タグまでの文字列(開始タグ・終了タグを含む)
    146                                 * @param buf 出力を行う文字列バッファ
    147                                 * @param offset 終了タグのオフセット(ここでは使っていません)
    148                                 */
    149                                @Override
    150                                protected void exec( final String str, final StringBuilder buf, final int offset ) {
    151                                        String key = TagParser.checkKey( str, buf );
    152                                        if( key.indexOf( '<' ) >= 0 ){
    153                                        final String errMsg = "[ERROR]PARSE:{@と}の整合性が不正です。変数内の特定の文字列に書式設定がされている可能性があります。キー=" + key;
    154                                                throw new HybsSystemException( errMsg );
    155                                        }
    156                                        buf.append( VAR_START ).append( incrementKey( key, cRow ) ).append( VAR_END );
    157                                }
    158                        }.doParse( lcStrBuf.toString(), VAR_START, VAR_END, false );
    159                        sheetRows.add( rowStr );
    160                }
    161        }
    162
    163        /**
    164         * xxx_yのy部分を引数分追加して返します。
    165         * yが数字でない場合や、_が無い場合はそのまま返します。
    166         *
    167         * @og.rev 5.0.0.2 LINE_COPYで利用するために追加
    168         *
    169         * @param key   キー文字列
    170         * @param inc   カウンタ部
    171         *
    172         * @return 変更後キー
    173         */
    174        private String incrementKey( final String key, final int inc ) {
    175                final int conOffset = key.lastIndexOf( VAR_CON );
    176                if( conOffset < 0 ) { return key; }
    177
    178                final String name = key.substring( 0, conOffset );
    179                int rownum = -1;
    180                try {
    181                        rownum = Integer.parseInt( key.substring( conOffset + VAR_CON.length(), key.length() ) );               // 6.0.2.4 (2014/10/17) メソッド間違い
    182                }
    183                // エラーが起きてもなにもしない。
    184                catch( final NumberFormatException ex ) {
    185                        // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid empty catch blocks
    186                        final String errMsg = "Not incrementKey. KEY=[" + key + "] " + ex.getMessage() ;
    187                        System.err.println( errMsg );
    188                }
    189
    190                // アンダースコア後が数字に変換できない場合はヘッダフッタとして認識
    191                if( rownum < 0 ){ return key; }
    192                else                    { return name + VAR_CON + rownum + inc ; }
    193        }
    194
    195        /**
    196         * シートのヘッダー部分を返します。
    197         *
    198         * @return ヘッダー
    199         */
    200        public String getHeader() {
    201                return sheetHeader;
    202        }
    203
    204        /**
    205         * シートのフッター部分を返します。
    206         *
    207         * @return フッター
    208         */
    209        public String getFooter() {
    210                return sheetFooter;
    211        }
    212
    213        /**
    214         * シート名称を返します。
    215         *
    216         * @return シート名称
    217         */
    218        public String getSheetName() {
    219                return sheetName;
    220        }
    221
    222        /**
    223         * 定義済シート名称を返します。
    224         *
    225         * @og.rev 5.2.1.0 (2010/10/01) シート名定義対応
    226         *
    227         * @return 定義済シート名称
    228         */
    229        public String getConfSheetName() {
    230                return confSheetName;
    231        }
    232
    233        /**
    234         * 定義名変換前のシート名称を返します。
    235         *
    236         * @og.rev 5.2.1.0 (2010/10/01) シート名定義対応
    237         *
    238         * @return 定義済シート名称
    239         */
    240        public String getOrigSheetName() {
    241                return origSheetName;
    242        }
    243
    244        /**
    245         * シートの各行を配列で返します。
    246         *
    247         * @og.rev 4.3.1.1 (2008/08/23) あらかじめ、必要な配列の長さを確保しておきます。
    248         *
    249         * @return シートの各行の配列
    250         * @og.rtnNotNull
    251         */
    252        public String[] getRows() {
    253                return sheetRows.toArray( new String[sheetRows.size()] );
    254        }
    255}