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