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 * {@LINE_COPY}が存在した場合に、テーブルモデル分だけ 108 * 行をコピーします。 109 * その際、{@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}