修訂 | 8941815bc2988c16c96eead6a3b03f45182e173a (tree) |
---|---|
時間 | 2014-11-22 03:02:17 |
作者 | noboru saitoh <msnobosan@gmal...> |
Commiter | noboru saitoh |
#34479 対応中
テンプレートパーサのユーザー拡張対応中
@@ -1,6 +1,6 @@ | ||
1 | 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
2 | 2 | <project basedir="." default="build" name="jaxcel"> |
3 | - <property name="varsion" value="1.00.00"/> | |
3 | + <property name="varsion" value="1.01.00"/> | |
4 | 4 | <property name="debuglevel" value="source,lines,vars"/> |
5 | 5 | <property name="target" value="1.7"/> |
6 | 6 | <property name="source" value="1.7"/> |
@@ -1,10 +1,10 @@ | ||
1 | -#EL\u5f0f\u5b9a\u7fa9 | |
1 | +#EL\u5f0f\u5b9a\u7fa9 \u30ab\u30b9\u30bf\u30e0\u30d7\u30ed\u30d1\u30c6\u30a3\u3067\u306e\u66f4\u65b0\u4e0d\u53ef | |
2 | 2 | el.class = org.hanei.jaxcel.parser.ELParser |
3 | 3 | el.directive = $ |
4 | 4 | el.bracket.start = { |
5 | 5 | el.bracket.end = } |
6 | 6 | |
7 | -#\u65e5\u4ed8\u30fb\u6642\u523b\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u5b9a\u7fa9 | |
7 | +#\u65e5\u4ed8\u30fb\u6642\u523b\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u5b9a\u7fa9 \u30ab\u30b9\u30bf\u30e0\u30d7\u30ed\u30d1\u30c6\u30a3\u3067\u306e\u62e1\u5f35\u53ef | |
8 | 8 | format.date = yyyy-MM-dd'T'HH:mm:ss.SSSXXX |
9 | 9 | format.date = yyyy-MM-dd'T'HH:mm:ss.SSXXX |
10 | 10 | format.date = yyyy-MM-dd'T'HH:mm:ss.SXXX |
@@ -32,7 +32,9 @@ format.time = HH:mm:ss.S | ||
32 | 32 | format.time = HH:mm:ss |
33 | 33 | format.time = HH:mm |
34 | 34 | |
35 | -#\u6307\u793a\u5b50\u5b9a\u7fa9 | |
35 | +#\u6307\u793a\u5b50\u5b9a\u7fa9 \u30ab\u30b9\u30bf\u30e0\u30d7\u30ed\u30d1\u30c6\u30a3\u3067\u306etl.directive\u62e1\u5f35\u53ef | |
36 | +#tl.directive\u3092\u62e1\u5f35\u3057\u305f\u5834\u5408\u3001\u5bfe\u3068\u306a\u308btl.[tl.directive].class \u30d7\u30ed\u30d1\u30c6\u30a3\u304c\u5fc5\u8981 | |
37 | +tl.symbol = # | |
36 | 38 | tl.directive = foreach |
37 | 39 | tl.directive = if |
38 | 40 | tl.bracket.start = ( |
@@ -31,8 +31,15 @@ import org.slf4j.LoggerFactory; | ||
31 | 31 | */ |
32 | 32 | public abstract class AbstractTLParser implements TLParser { |
33 | 33 | |
34 | - protected static final Logger log = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName()); | |
35 | - | |
34 | + protected final Logger log; | |
35 | + | |
36 | + /** | |
37 | + * コンストラクタ | |
38 | + */ | |
39 | + AbstractTLParser() { | |
40 | + log = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[2].getClassName()); | |
41 | + } | |
42 | + | |
36 | 43 | @Override |
37 | 44 | public abstract void parse(JaxcelContext context); |
38 | 45 |
@@ -18,11 +18,11 @@ | ||
18 | 18 | */ |
19 | 19 | package org.hanei.jaxcel.parser; |
20 | 20 | |
21 | -import java.io.IOException; | |
22 | 21 | import java.io.InputStream; |
23 | 22 | import java.text.CharacterIterator; |
24 | 23 | import java.text.StringCharacterIterator; |
25 | 24 | import java.util.ArrayDeque; |
25 | +import java.util.Arrays; | |
26 | 26 | import java.util.Deque; |
27 | 27 | import java.util.HashMap; |
28 | 28 | import java.util.Map; |
@@ -31,15 +31,15 @@ import java.util.regex.Pattern; | ||
31 | 31 | |
32 | 32 | import org.apache.commons.collections.ExtendedProperties; |
33 | 33 | import org.apache.poi.ss.usermodel.Cell; |
34 | -import org.apache.poi.ss.util.CellReference; | |
35 | 34 | import org.hanei.jaxcel.exception.JaxcelInputException; |
36 | 35 | import org.hanei.jaxcel.report.JaxcelContext; |
37 | 36 | import org.hanei.jaxcel.util.ExcelUtil; |
37 | +import static org.hanei.jaxcel.parser.Constants.*; | |
38 | 38 | import org.slf4j.LoggerFactory; |
39 | 39 | import org.slf4j.Logger; |
40 | 40 | |
41 | 41 | /** |
42 | - * Excelテンプレートシートの指示子(Template Language)、EL式(Expression Language)の検索、パースを行う | |
42 | + * Excelテンプレートシートのセルに記述された指示子(Template Language)、EL式(Expression Language)の検索、パースを行う | |
43 | 43 | * |
44 | 44 | * @since 1.01.00 |
45 | 45 | * @author Noboru Saito |
@@ -48,46 +48,26 @@ import org.slf4j.Logger; | ||
48 | 48 | public class CellParser { |
49 | 49 | |
50 | 50 | private static final Logger log = LoggerFactory.getLogger(CellParser.class); |
51 | + | |
52 | + private static final int PARSE_MAX = 10; // 同一セルパース最大回数 | |
51 | 53 | |
52 | - // プロパティファイル キー定義 | |
53 | - private static final String DEFAULT_PROPS_NAME = "/jaxcel.properties"; | |
54 | - private static final String P_EL_CLASS = "el.class"; | |
55 | - private static final String P_EL_DIRECTIVE = "el.directive"; | |
56 | - private static final String P_EL_BRACKET_START = "el.bracket.start"; | |
57 | - private static final String P_EL_BRACKET_END = "el.bracket.end"; | |
58 | - private static final String P_TL_DIRECTIVE = "tl.directive"; | |
59 | - private static final String P_TL_PREFIX = "tl."; | |
60 | - private static final String P_CLASS_SUFFIX = ".class"; | |
61 | - private static final String P_TL_BRACKET_START = "tl.bracket.start"; | |
62 | - private static final String P_TL_BRACKET_END = "tl.bracket.end"; | |
63 | - private static final String P_DATE_FORMAT = "format.date"; | |
64 | - private static final String P_TIME_FORMAT = "format.time"; | |
65 | - | |
66 | - // 文字・文字列定義 | |
67 | - private static final String TL_START_SYMBOL = "#"; | |
68 | - private static final char VALUE_BRACKET = '"'; | |
69 | - private static final char ATTR_DELIMITER = ':'; | |
70 | - private static final char ESCAPE_CHAR = '\\'; | |
71 | - private static final char EM_SPACE = ' '; | |
72 | - | |
73 | - private static final int PARSE_MAX = 10; | |
74 | - | |
75 | - private ExtendedProperties config = null; | |
54 | + private ExtendedProperties config = new ExtendedProperties(); | |
76 | 55 | private Map<String, TLParser> parserMap = null; |
77 | 56 | private JaxcelContext context = null; |
78 | 57 | private Cell cell = null; |
79 | - private char elDirective; | |
80 | 58 | |
81 | - private boolean reParseFlg; // 同一セル再パース要否フラグ | |
82 | - private int parseCount; // 同一セルパース回数 | |
59 | + private boolean needReParse; // 同一セル再パース要否フラグ | |
60 | + private int parseCount; // 同一セルパース回数 | |
61 | + | |
62 | + private char elDirective; | |
83 | 63 | private char elStartBracket; |
84 | 64 | private char elEndBracket; |
85 | 65 | private char tlEndBracket; |
86 | - private String matchPattern = ""; | |
66 | + private char tlSymbol; | |
87 | 67 | |
88 | 68 | private StringCharacterIterator valueIterator = new StringCharacterIterator(""); |
89 | - private Pattern ptAll = null; | |
90 | - private Matcher mtAll = null; | |
69 | + private Pattern parserPattern = null; | |
70 | + private Matcher parserMatches = null; | |
91 | 71 | |
92 | 72 | /** |
93 | 73 | * コンストラクタ |
@@ -97,32 +77,36 @@ public class CellParser { | ||
97 | 77 | * @throws JaxcelInputException デフォルトプロパティファイル入力例外発生時 |
98 | 78 | */ |
99 | 79 | public CellParser(JaxcelContext context) { |
80 | + | |
81 | + // コンテキスト設定 | |
82 | + this.context = context; | |
83 | + | |
100 | 84 | // デフォルトプロパティロード |
101 | 85 | try(InputStream inStream = getClass().getResourceAsStream(DEFAULT_PROPS_NAME)) { |
102 | - config = new ExtendedProperties(); | |
103 | 86 | config.load(inStream); |
104 | 87 | log.debug("default properties load"); |
105 | - } catch (IOException e) { | |
106 | - throw new JaxcelInputException("default properties load error"); | |
88 | + } catch (Exception e) { | |
89 | + throw new JaxcelInputException("default properties load error", e); | |
107 | 90 | } |
108 | - | |
109 | - // コンテキスト取得 | |
110 | - this.context = context; | |
91 | + // 外部プロパティロード | |
111 | 92 | if(this.context != null) { |
112 | - //TODO: 外部プロパティロードを予定。デフォルトプロパティ上書く | |
93 | + ExtendedProperties customProperties = this.context.getCustomProperties(); | |
94 | + if(customProperties != null) { | |
95 | + // 日付・時刻書式 | |
96 | + if(customProperties.containsKey(P_DATE_FORMAT)) | |
97 | + config.addProperty(P_DATE_FORMAT, Arrays.asList(customProperties.getStringArray(P_DATE_FORMAT))); | |
98 | + if(customProperties.containsKey(P_TIME_FORMAT)) | |
99 | + config.addProperty(P_TIME_FORMAT, Arrays.asList(customProperties.getStringArray(P_TIME_FORMAT))); | |
100 | + // テンプレート指示子 | |
101 | + if(customProperties.containsKey(P_TL_DIRECTIVE)) { | |
102 | + config.addProperty(P_TL_DIRECTIVE, Arrays.asList(customProperties.getStringArray(P_TL_DIRECTIVE))); | |
103 | + for(String tlDirective : customProperties.getStringArray(P_TL_DIRECTIVE)) { | |
104 | + config.addProperty(String.format(P_TL_CLASS, tlDirective), customProperties.getString(String.format(P_TL_CLASS, tlDirective))); | |
105 | + } | |
106 | + } | |
107 | + } | |
113 | 108 | } |
114 | 109 | |
115 | - // 初期処理 | |
116 | - init(); | |
117 | - } | |
118 | - | |
119 | - | |
120 | - /** | |
121 | - * 初期処理 | |
122 | - */ | |
123 | - private void init() { | |
124 | - log.trace("init start"); | |
125 | - | |
126 | 110 | // パーサ ロード |
127 | 111 | parserMap = new HashMap<>(); |
128 | 112 | try { |
@@ -136,14 +120,16 @@ public class CellParser { | ||
136 | 120 | elStartBracket = config.getString(P_EL_BRACKET_START).charAt(0); |
137 | 121 | elEndBracket = config.getString(P_EL_BRACKET_END).charAt(0); |
138 | 122 | parserMap.put(String.valueOf(elDirective) + String.valueOf(elStartBracket), parser); |
139 | - log.debug("ELParser load"); | |
123 | + log.debug("parser load: {} ", cls.getSimpleName()); | |
140 | 124 | |
125 | + // テンプレート指示子パーサ | |
126 | + tlSymbol = config.getString(P_TL_SYMBOL).charAt(0); | |
141 | 127 | tlEndBracket = config.getString(P_TL_BRACKET_END).charAt(0); |
142 | 128 | for(String tlDirective : config.getStringArray(P_TL_DIRECTIVE)) { |
143 | - cls = Class.forName(config.getString(P_TL_PREFIX + tlDirective + P_CLASS_SUFFIX)); | |
129 | + cls = Class.forName(config.getString(String.format(P_TL_CLASS, tlDirective))); | |
144 | 130 | parser = (TLParser) cls.newInstance(); |
145 | - parserMap.put(TL_START_SYMBOL + tlDirective + config.getString(P_TL_BRACKET_START), parser); | |
146 | - log.debug("{} load", cls.getSimpleName()); | |
131 | + parserMap.put(tlSymbol + tlDirective + config.getString(P_TL_BRACKET_START), parser); | |
132 | + log.debug("parser load: {} ", cls.getSimpleName()); | |
147 | 133 | } |
148 | 134 | } |
149 | 135 | catch(Exception e) { |
@@ -151,13 +137,12 @@ public class CellParser { | ||
151 | 137 | } |
152 | 138 | |
153 | 139 | // 正規表現Pattern生成 |
140 | + String matchPattern = ""; | |
154 | 141 | for(String key : parserMap.keySet()) { |
155 | 142 | matchPattern += (matchPattern.length() > 0 ? "|" : "") + Pattern.quote(key); |
156 | 143 | } |
157 | - log.debug("matchPattern: {}", matchPattern); | |
158 | - ptAll = Pattern.compile(matchPattern); | |
159 | - | |
160 | - log.trace("init end"); | |
144 | + log.debug("parser matchPattern: {}", matchPattern); | |
145 | + parserPattern = Pattern.compile(matchPattern); | |
161 | 146 | } |
162 | 147 | |
163 | 148 | /** |
@@ -174,7 +159,7 @@ public class CellParser { | ||
174 | 159 | if(cell == null) { |
175 | 160 | // 再パースフラグクリア |
176 | 161 | this.cell = null; |
177 | - reParseFlg = false; | |
162 | + needReParse = false; | |
178 | 163 | parseCount = 0; |
179 | 164 | log.debug("cell is null"); |
180 | 165 | log.trace("parse end"); |
@@ -186,18 +171,16 @@ public class CellParser { | ||
186 | 171 | log.debug("start new cell parse"); |
187 | 172 | this.cell = cell; |
188 | 173 | // 再パースフラグ・カウントクリア |
189 | - reParseFlg = false; | |
174 | + needReParse = false; | |
190 | 175 | parseCount = 0; |
191 | 176 | } |
192 | 177 | // 保持しているセルと同一セルのパースの場合 |
193 | 178 | else if(ExcelUtil.equalsCellAddress(this.cell, cell, true)) { |
194 | - // 再パースカウント加算 | |
195 | - parseCount++; | |
196 | 179 | // 再パース回数チェック |
197 | - if(parseCount >= PARSE_MAX) { | |
180 | + if(++parseCount >= PARSE_MAX) { | |
198 | 181 | log.warn("parse repeat count over"); |
199 | 182 | // 再パースフラグクリア |
200 | - reParseFlg = false; | |
183 | + needReParse = false; | |
201 | 184 | log.trace("parse end"); |
202 | 185 | return; |
203 | 186 | } |
@@ -209,7 +192,7 @@ public class CellParser { | ||
209 | 192 | // セルを保持 |
210 | 193 | this.cell = cell; |
211 | 194 | // 再パースフラグ・カウントクリア |
212 | - reParseFlg = false; | |
195 | + needReParse = false; | |
213 | 196 | parseCount = 0; |
214 | 197 | } |
215 | 198 |
@@ -218,16 +201,16 @@ public class CellParser { | ||
218 | 201 | switch(cell.getCellType()){ |
219 | 202 | case Cell.CELL_TYPE_STRING: |
220 | 203 | cellValue = cell.getStringCellValue(); |
221 | - log.debug("cell[{}] cellType: string value: {}", (new CellReference(cell)).formatAsString(), cellValue); | |
204 | + log.debug("value: {}", cellValue); | |
222 | 205 | break; |
223 | 206 | case Cell.CELL_TYPE_FORMULA: |
224 | 207 | cellValue = cell.getCellFormula(); |
225 | - log.debug("cell[{}] cellType: formula value: {}", (new CellReference(cell)).formatAsString(), cellValue); | |
208 | + log.debug("formula: {}", cellValue); | |
226 | 209 | break; |
227 | 210 | default: |
228 | - log.debug("cell type is not string or formula"); | |
211 | + log.debug("cell type is not string or )formula"); | |
229 | 212 | // 再パースフラグクリア |
230 | - reParseFlg = false; | |
213 | + needReParse = false; | |
231 | 214 | log.trace("parse end"); |
232 | 215 | return; |
233 | 216 | } |
@@ -235,26 +218,26 @@ public class CellParser { | ||
235 | 218 | // 解析開始 |
236 | 219 | TLParser parser = null; |
237 | 220 | Map<String, Object> attributeMap = new HashMap<>(); |
238 | - String matchString = null; | |
239 | - String mapKey = null; | |
221 | + String matchParser = null; | |
222 | + String attrKey = null; | |
240 | 223 | StringBuilder strBuf = new StringBuilder(1024); |
241 | - StringBuilder parseString = new StringBuilder(1024); | |
224 | + StringBuilder templateString = new StringBuilder(1024); | |
242 | 225 | Deque<Character> bracketStack = new ArrayDeque<>(); |
243 | - boolean attrValueFlag, expressionFlag; | |
244 | - boolean findFlag = false; | |
245 | - boolean escapeFlag = false; | |
246 | - boolean keyParseFlag = false; | |
226 | + boolean parserExist = false; | |
227 | + boolean attrValueParsing; | |
228 | + boolean expressionParsing; | |
229 | + boolean attrKeyParsed; | |
230 | + boolean escaping; | |
247 | 231 | |
248 | 232 | // 指示子(TL)の検索 |
249 | - mtAll = ptAll.matcher(cellValue); | |
233 | + parserMatches = parserPattern.matcher(cellValue); | |
250 | 234 | // 指示子検索ループ |
251 | - findLoop: | |
252 | - while(mtAll.find()) { | |
253 | - matchString = mtAll.group(); | |
254 | - log.debug("match: {}", matchString); | |
235 | + findLoop: while(parserMatches.find()) { | |
236 | + matchParser = parserMatches.group(); | |
237 | + log.debug("match: {}", matchParser); | |
255 | 238 | |
256 | 239 | // パーサ取得 |
257 | - parser = parserMap.get(matchString); | |
240 | + parser = parserMap.get(matchParser); | |
258 | 241 | if(parser == null) { |
259 | 242 | log.warn("parser is null"); |
260 | 243 | continue; |
@@ -262,33 +245,33 @@ public class CellParser { | ||
262 | 245 | log.debug("parser: {}", parser.getClass().getSimpleName()); |
263 | 246 | |
264 | 247 | // クリア |
265 | - parseString.setLength(0); | |
266 | - parseString.append(matchString); | |
248 | + templateString.setLength(0); | |
249 | + templateString.append(matchParser); | |
267 | 250 | strBuf.setLength(0); |
268 | 251 | bracketStack.clear(); |
269 | 252 | attributeMap.clear(); |
270 | - expressionFlag = false; | |
271 | - attrValueFlag = false; | |
272 | - keyParseFlag = false; | |
273 | - escapeFlag = false; | |
253 | + attrValueParsing = false; | |
254 | + expressionParsing = false; | |
255 | + attrKeyParsed = false; | |
256 | + escaping = false; | |
274 | 257 | |
275 | 258 | // 指示子がEL式 |
276 | - if(matchString.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) { | |
259 | + if(matchParser.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) { | |
277 | 260 | // elValueFlag ON |
278 | - expressionFlag = true; | |
279 | - log.debug("expressionFlag: {}", expressionFlag); | |
261 | + expressionParsing = true; | |
262 | + log.debug("expressionParsing: {}", expressionParsing); | |
280 | 263 | // 括弧スタックに開始括弧push |
281 | 264 | bracketStack.offerFirst(elStartBracket); |
282 | 265 | } |
283 | 266 | |
284 | 267 | // 開始括弧以降読込み |
285 | 268 | valueIterator.setText(cellValue); |
286 | - for(char c = valueIterator.setIndex(mtAll.start() + matchString.length()); c != CharacterIterator.DONE; c = valueIterator.next()) { | |
269 | + for(char c = valueIterator.setIndex(parserMatches.start() + matchParser.length()); c != CharacterIterator.DONE; c = valueIterator.next()) { | |
287 | 270 | // カーソル文字抜出し |
288 | - parseString.append(c); | |
271 | + templateString.append(c); | |
289 | 272 | log.trace("current char: {}", c); |
290 | 273 | // EL式解析中 |
291 | - if(expressionFlag) { | |
274 | + if(expressionParsing) { | |
292 | 275 | log.trace("parsing EL"); |
293 | 276 | // カーソル文字がEL式終端括弧であれば |
294 | 277 | if(elEndBracket == c) { |
@@ -297,19 +280,19 @@ public class CellParser { | ||
297 | 280 | // 括弧スタック空ならEL式解析終了 |
298 | 281 | if(bracketStack.size() == 0) { |
299 | 282 | // Mapに登録 |
300 | - attributeMap.put(Constants.EXPRESSION, getTrimString(strBuf)); | |
301 | - log.debug("attributeMap : {}", attributeMap); | |
283 | + attributeMap.put(EXPRESSION, getTrimString(strBuf)); | |
284 | + log.debug("attributeMap: {}", attributeMap); | |
302 | 285 | // 指示子がEL式なら解析終了 |
303 | - if(matchString.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) { | |
286 | + if(matchParser.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) { | |
304 | 287 | log.trace("parse end"); |
305 | - findFlag = true; | |
288 | + parserExist = true; | |
306 | 289 | break findLoop; |
307 | 290 | } |
308 | 291 | // 指示子がEL式以外なら |
309 | 292 | else { |
310 | 293 | log.trace("EL parse end"); |
311 | 294 | // フラグクリア |
312 | - expressionFlag = false; | |
295 | + expressionParsing = false; | |
313 | 296 | // バッファクリア |
314 | 297 | strBuf.setLength(0); |
315 | 298 | } |
@@ -334,43 +317,43 @@ public class CellParser { | ||
334 | 317 | } |
335 | 318 | } |
336 | 319 | // 属性値解析中 |
337 | - else if(attrValueFlag) { | |
320 | + else if(attrValueParsing) { | |
338 | 321 | log.trace("parsing attrValue"); |
339 | 322 | // カーソル文字がダブルクォートであれば |
340 | 323 | if(VALUE_BRACKET == c) { |
341 | - log.trace("escapeFlag : {}", escapeFlag); | |
324 | + log.trace("escaping : {}", escaping); | |
342 | 325 | // エスケープ中なら |
343 | - if(escapeFlag) { | |
326 | + if(escaping) { | |
344 | 327 | // 括弧とはみなさずバッファに保持 |
345 | 328 | strBuf.append(c); |
346 | 329 | // エスケープフラグクリア |
347 | - escapeFlag = false; | |
330 | + escaping = false; | |
348 | 331 | log.trace("parsing: {}", strBuf.toString()); |
349 | 332 | } |
350 | 333 | // エスケープ中でないなら |
351 | 334 | else { |
352 | 335 | // Mapに登録 |
353 | - attributeMap.put(mapKey, strBuf.toString()); | |
354 | - log.debug("attributeMap : {}", attributeMap); | |
336 | + attributeMap.put(attrKey, strBuf.toString()); | |
337 | + log.trace("attributeMap: {}", attributeMap); | |
355 | 338 | // 属性値解析中フラグクリア |
356 | - attrValueFlag = false; | |
339 | + attrValueParsing = false; | |
357 | 340 | // バッファクリア |
358 | 341 | strBuf.setLength(0); |
359 | - mapKey = null; | |
342 | + attrKey = null; | |
360 | 343 | } |
361 | 344 | } |
362 | 345 | // カーソル文字がダブルクォートでないなら |
363 | 346 | else { |
364 | - log.trace("escapeFlag : {}", escapeFlag); | |
347 | + log.trace("escaping : {}", escaping); | |
365 | 348 | // エスケープ中なら |
366 | - if(escapeFlag) { | |
349 | + if(escaping) { | |
367 | 350 | // エスケープフラグクリア |
368 | - escapeFlag = false; | |
351 | + escaping = false; | |
369 | 352 | } |
370 | 353 | // エスケープ中でなくエスケープ文字(\)なら |
371 | 354 | else if(ESCAPE_CHAR == c) { |
372 | 355 | // エスケープフラグON |
373 | - escapeFlag = true; | |
356 | + escaping = true; | |
374 | 357 | } |
375 | 358 | // バッファに保持 |
376 | 359 | strBuf.append(c); |
@@ -385,18 +368,18 @@ public class CellParser { | ||
385 | 368 | log.trace("next char: {}", c); |
386 | 369 | // 次の文字がEL式開始括弧であれば |
387 | 370 | if(elStartBracket == c) { |
388 | - parseString.append(c); | |
371 | + templateString.append(c); | |
389 | 372 | // EL式解析中フラグセット |
390 | - expressionFlag = true; | |
391 | - log.trace("expressionFlag : {}", expressionFlag); | |
373 | + expressionParsing = true; | |
374 | + log.trace("expressionParsing : {}", expressionParsing); | |
392 | 375 | // バッファ空でなければ |
393 | 376 | if(strBuf.length() > 0) { |
394 | 377 | // Mapに登録(値はnull) |
395 | 378 | attributeMap.put(strBuf.toString(), null); |
396 | - log.debug("attributeMap : {}", attributeMap); | |
379 | + log.trace("attributeMap: {}", attributeMap); | |
397 | 380 | } |
398 | 381 | // バッファクリア |
399 | - mapKey = null; | |
382 | + attrKey = null; | |
400 | 383 | strBuf.setLength(0); |
401 | 384 | } |
402 | 385 | // 次の文字がEL式開始括弧でなければ |
@@ -413,26 +396,26 @@ public class CellParser { | ||
413 | 396 | else if(ATTR_DELIMITER == c) { |
414 | 397 | log.trace("parsing attr delimiter"); |
415 | 398 | // 属性文字列空でないなら |
416 | - if(mapKey != null) { | |
399 | + if(attrKey != null) { | |
417 | 400 | // 属性解析済みフラグセット |
418 | - keyParseFlag = true; | |
419 | - log.trace("keyParseFlag: {}", keyParseFlag); | |
401 | + attrKeyParsed = true; | |
402 | + log.trace("attrKeyParsed: {}", attrKeyParsed); | |
420 | 403 | } |
421 | 404 | // 属性文字列空なら |
422 | 405 | else { |
423 | 406 | // バッファ空でなければ |
424 | 407 | if(strBuf.length() > 0) { |
425 | 408 | // 属性文字列保持 |
426 | - mapKey = strBuf.toString(); | |
427 | - log.trace("mapKey: {}", mapKey); | |
409 | + attrKey = strBuf.toString(); | |
410 | + log.trace("mapKey: {}", attrKey); | |
428 | 411 | // Mapに登録(値はnull) |
429 | - attributeMap.put(mapKey, null); | |
430 | - log.debug("attributeMap : {}", attributeMap); | |
412 | + attributeMap.put(attrKey, null); | |
413 | + log.trace("attributeMap: {}", attributeMap); | |
431 | 414 | // バッファクリア |
432 | 415 | strBuf.setLength(0); |
433 | 416 | // 属性解析済みフラグセット |
434 | - keyParseFlag = true; | |
435 | - log.trace("keyParseFlag: {}", keyParseFlag); | |
417 | + attrKeyParsed = true; | |
418 | + log.trace("attrKeyParsed: {}", attrKeyParsed); | |
436 | 419 | } |
437 | 420 | // 上記以外 |
438 | 421 | else { |
@@ -446,12 +429,12 @@ public class CellParser { | ||
446 | 429 | else if(VALUE_BRACKET == c) { |
447 | 430 | log.trace("parsing attr value bracket"); |
448 | 431 | // 属性解析済みなら |
449 | - if(keyParseFlag == true && mapKey != null) { | |
432 | + if(attrKeyParsed == true && attrKey != null) { | |
450 | 433 | // 属性値解析中フラグセット |
451 | - attrValueFlag = true; | |
452 | - keyParseFlag = false; | |
453 | - log.trace("attrValueFlag: {}", attrValueFlag); | |
454 | - log.trace("keyParseFlag: {}", keyParseFlag); | |
434 | + attrValueParsing = true; | |
435 | + attrKeyParsed = false; | |
436 | + log.trace("attrValueParsing: {}", attrValueParsing); | |
437 | + log.trace("attrKeyParsed: {}", attrKeyParsed); | |
455 | 438 | // バッファクリア |
456 | 439 | strBuf.setLength(0); |
457 | 440 | } |
@@ -468,10 +451,10 @@ public class CellParser { | ||
468 | 451 | // バッファ空でなければ |
469 | 452 | if(strBuf.length() > 0) { |
470 | 453 | // Mapに登録(値はnull) |
471 | - mapKey = strBuf.toString(); | |
472 | - log.trace("mapKey: {}", mapKey); | |
473 | - attributeMap.put(mapKey, null); | |
474 | - log.debug("attributeMap : {}", attributeMap); | |
454 | + attrKey = strBuf.toString(); | |
455 | + log.trace("mapKey: {}", attrKey); | |
456 | + attributeMap.put(attrKey, null); | |
457 | + log.trace("attributeMap: {}", attributeMap); | |
475 | 458 | // バッファクリア |
476 | 459 | strBuf.setLength(0); |
477 | 460 | } |
@@ -483,13 +466,13 @@ public class CellParser { | ||
483 | 466 | if(strBuf.length() > 0) { |
484 | 467 | // Mapに登録(値はnull) |
485 | 468 | attributeMap.put(strBuf.toString(), null); |
486 | - log.debug("attributeMap : {}", attributeMap); | |
469 | + log.trace("attributeMap: {}", attributeMap); | |
487 | 470 | // バッファクリア |
488 | - mapKey = null; | |
471 | + attrKey = null; | |
489 | 472 | strBuf.setLength(0); |
490 | 473 | } |
491 | 474 | log.trace("parse end"); |
492 | - findFlag = true; | |
475 | + parserExist = true; | |
493 | 476 | break findLoop; |
494 | 477 | } |
495 | 478 | // 上記以外 |
@@ -501,15 +484,15 @@ public class CellParser { | ||
501 | 484 | } |
502 | 485 | } |
503 | 486 | |
504 | - log.trace("findFlag: {}", findFlag); | |
487 | + log.trace("parser exist: {}", parserExist); | |
505 | 488 | // 指示子検索ヒット |
506 | - if(findFlag) { | |
489 | + if(parserExist) { | |
507 | 490 | // コンテキスト設定 |
508 | 491 | context.setCurrentCell(this.cell); |
509 | - context.setParseString(parseString.toString()); | |
492 | + context.setTemplateString(templateString.toString()); | |
510 | 493 | context.setAttributeMap(attributeMap); |
511 | 494 | if(log.isDebugEnabled()) { |
512 | - log.debug("parseString: {}", parseString); | |
495 | + log.debug("templateString: {}", templateString.toString()); | |
513 | 496 | log.debug("attributeMap: {}", attributeMap); |
514 | 497 | } |
515 | 498 | try { |
@@ -521,27 +504,27 @@ public class CellParser { | ||
521 | 504 | case Cell.CELL_TYPE_STRING: |
522 | 505 | case Cell.CELL_TYPE_FORMULA: |
523 | 506 | // セルタイプが文字列、数式の場合は再パースフラグ設定 |
524 | - reParseFlg = true; | |
507 | + needReParse = true; | |
525 | 508 | break; |
526 | 509 | default: |
527 | 510 | // 上記以外は再パースフラグクリア |
528 | - reParseFlg = false; | |
511 | + needReParse = false; | |
529 | 512 | } |
530 | 513 | } |
531 | 514 | catch(Exception e) { |
532 | 515 | log.error("parser.parse error. {}", e.getMessage(), e); |
533 | 516 | // 再パースフラグクリア |
534 | - reParseFlg = false; | |
517 | + needReParse = false; | |
535 | 518 | } |
536 | 519 | } |
537 | 520 | // 検索ヒットせず |
538 | 521 | else { |
539 | 522 | log.debug("not found TL"); |
540 | 523 | // 再パースフラグクリア |
541 | - reParseFlg = false; | |
524 | + needReParse = false; | |
542 | 525 | } |
543 | 526 | |
544 | - log.trace("parse end: reParseFlg: {} parseCount: {}", reParseFlg, parseCount); | |
527 | + log.trace("parse end: needReParse: {} parseCount: {}", needReParse, parseCount); | |
545 | 528 | } |
546 | 529 | |
547 | 530 | /** |
@@ -550,7 +533,7 @@ public class CellParser { | ||
550 | 533 | * @return 再パース必要であればはtrue。不要であればfalse |
551 | 534 | */ |
552 | 535 | public boolean isReParseCell() { |
553 | - return reParseFlg; | |
536 | + return needReParse; | |
554 | 537 | } |
555 | 538 | |
556 | 539 | /** |
@@ -19,12 +19,32 @@ | ||
19 | 19 | package org.hanei.jaxcel.parser; |
20 | 20 | |
21 | 21 | /** |
22 | - * テンプレートパーサー用定数クラス<br> | |
22 | + * パーサー用定数クラス<br> | |
23 | 23 | * |
24 | 24 | * @since 1.01.00 |
25 | 25 | * @author Noboru Saito |
26 | 26 | */ |
27 | 27 | public class Constants { |
28 | + // プロパティファイル キー定義 | |
29 | + static final String DEFAULT_PROPS_NAME = "/jaxcel.properties"; | |
30 | + static final String P_EL_CLASS = "el.class"; | |
31 | + static final String P_EL_DIRECTIVE = "el.directive"; | |
32 | + static final String P_EL_BRACKET_START = "el.bracket.start"; | |
33 | + static final String P_EL_BRACKET_END = "el.bracket.end"; | |
34 | + static final String P_TL_DIRECTIVE = "tl.directive"; | |
35 | + static final String P_TL_CLASS = "tl.%s.class"; | |
36 | + static final String P_TL_BRACKET_START = "tl.bracket.start"; | |
37 | + static final String P_TL_BRACKET_END = "tl.bracket.end"; | |
38 | + static final String P_DATE_FORMAT = "format.date"; | |
39 | + static final String P_TIME_FORMAT = "format.time"; | |
40 | + static final String P_TL_SYMBOL = "tl.symbol"; | |
41 | + | |
42 | + // 文字・文字列定義 | |
43 | + static final char VALUE_BRACKET = '"'; | |
44 | + static final char ATTR_DELIMITER = ':'; | |
45 | + static final char ESCAPE_CHAR = '\\'; | |
46 | + static final char EM_SPACE = ' '; | |
47 | + | |
28 | 48 | public static final short FORMAT_GENERAL = 0; |
29 | 49 | public static final short FORMAT_TEXT = 49; |
30 | 50 | public static final String EXPRESSION = "_expression"; |
@@ -35,6 +35,10 @@ import org.hanei.jaxcel.util.ExcelUtil; | ||
35 | 35 | /** |
36 | 36 | * EL式のパーサー実装<br> |
37 | 37 | * |
38 | + * <h4>テンプレートの書式</h4> | |
39 | + * <h5>${expression}</h5> | |
40 | + * <p>expressionを解析し値を出力する。</p> | |
41 | + * | |
38 | 42 | * @since 1.01.00 |
39 | 43 | * @author Noboru Saito |
40 | 44 | */ |
@@ -44,14 +48,14 @@ public class ELParser extends AbstractTLParser { | ||
44 | 48 | public void parse(JaxcelContext context) { |
45 | 49 | log.trace("parse start"); |
46 | 50 | |
47 | - Object elResult; // パース結果保持用 | |
51 | + Object elResult; | |
48 | 52 | String newValue; |
49 | 53 | |
50 | 54 | Cell cell = context.getCurrentCell(); |
51 | 55 | ELManager elMgr = context.getElManager(); |
52 | 56 | |
53 | 57 | // evaluate |
54 | - elResult = elMgr.evaluate((String) context.getAttributeMap().get(Constants.EXPRESSION)); | |
58 | + elResult = elMgr.evaluate(StringUtils.defaultString((String) context.getAttributeMap().get(Constants.EXPRESSION))); | |
55 | 59 | if(elResult == null) { |
56 | 60 | log.debug("evaluate result is null"); |
57 | 61 | } |
@@ -60,7 +64,7 @@ public class ELParser extends AbstractTLParser { | ||
60 | 64 | } |
61 | 65 | |
62 | 66 | // マッチ1件目をevaluate結果で置換 |
63 | - newValue = ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), (elResult == null ? "" : elResult.toString())); | |
67 | + newValue = ExcelUtil.replaceFirstCellValue(cell, context.getTemplateString(), (elResult == null ? "" : elResult.toString())); | |
64 | 68 | log.debug("replace value: {}", newValue); |
65 | 69 | |
66 | 70 | // newValueがnullの場合パース終了 |
@@ -102,7 +102,7 @@ public class ForeachParser extends AbstractTLParser { | ||
102 | 102 | // セルタイプのチェック、指示子をクリア |
103 | 103 | switch(cell.getCellType()) { |
104 | 104 | case Cell.CELL_TYPE_STRING: |
105 | - cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), "")); | |
105 | + cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getTemplateString(), "")); | |
106 | 106 | break; |
107 | 107 | default: |
108 | 108 | // セルタイプが文字列でない場合パース終了 |
@@ -66,7 +66,7 @@ public class IfParser extends AbstractTLParser { | ||
66 | 66 | // セルタイプのチェック、指示子をクリア |
67 | 67 | switch(cell.getCellType()) { |
68 | 68 | case Cell.CELL_TYPE_STRING: |
69 | - cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getParseString(), "")); | |
69 | + cell.setCellValue(ExcelUtil.replaceFirstCellValue(cell, context.getTemplateString(), "")); | |
70 | 70 | break; |
71 | 71 | default: |
72 | 72 | // セルタイプが文字列でない場合パース終了 |
@@ -20,6 +20,7 @@ package org.hanei.jaxcel.report; | ||
20 | 20 | |
21 | 21 | import java.util.Map; |
22 | 22 | |
23 | +import org.apache.commons.collections.ExtendedProperties; | |
23 | 24 | import org.apache.poi.ss.usermodel.Cell; |
24 | 25 | import org.apache.poi.ss.usermodel.Sheet; |
25 | 26 |
@@ -33,11 +34,12 @@ public class JaxcelContext { | ||
33 | 34 | |
34 | 35 | private Sheet currentSheet = null; |
35 | 36 | private Cell currentCell = null; |
36 | - private String parseString = null; | |
37 | + private String templateString = null; | |
37 | 38 | private Map<String, Object> attributeMap =null; |
38 | 39 | private String[] dateFormat = null; |
39 | 40 | private String[] timeFormat = null; |
40 | 41 | private ELManager elManager = null; |
42 | + private ExtendedProperties customProperties = null; | |
41 | 43 | |
42 | 44 | /** |
43 | 45 | * コンストラクタ |
@@ -85,17 +87,17 @@ public class JaxcelContext { | ||
85 | 87 | * |
86 | 88 | * @return パース対象の文字列 |
87 | 89 | */ |
88 | - public String getParseString() { | |
89 | - return parseString; | |
90 | + public String getTemplateString() { | |
91 | + return templateString; | |
90 | 92 | } |
91 | 93 | |
92 | 94 | /** |
93 | 95 | * パース対象とする指示子文字列を設定 |
94 | 96 | * |
95 | - * @param parseString パース対象の文字列 | |
97 | + * @param templateString パース対象の文字列 | |
96 | 98 | */ |
97 | - public void setParseString(String parseString) { | |
98 | - this.parseString = parseString; | |
99 | + public void setTemplateString(String parseString) { | |
100 | + this.templateString = parseString; | |
99 | 101 | } |
100 | 102 | |
101 | 103 | /** |
@@ -169,5 +171,23 @@ public class JaxcelContext { | ||
169 | 171 | public void setElManager(ELManager elManager) { |
170 | 172 | this.elManager = elManager; |
171 | 173 | } |
174 | + | |
175 | + /** | |
176 | + * カスタムパーサー用のプロパティを返却 | |
177 | + * | |
178 | + * @return customPropertiesオブジェクト | |
179 | + */ | |
180 | + public ExtendedProperties getCustomProperties() { | |
181 | + return customProperties; | |
182 | + } | |
183 | + | |
184 | + /** | |
185 | + * カスタムパーサー用のプロパティを設定 | |
186 | + * | |
187 | + * @param customProperties customPropertiesオブジェクト | |
188 | + */ | |
189 | + public void setCustomProperties(ExtendedProperties customProperties) { | |
190 | + this.customProperties = customProperties; | |
191 | + } | |
172 | 192 | |
173 | 193 | } |
@@ -20,6 +20,7 @@ package org.hanei.jaxcel.report; | ||
20 | 20 | |
21 | 21 | import java.io.BufferedInputStream; |
22 | 22 | import java.io.File; |
23 | +import java.io.FileInputStream; | |
23 | 24 | import java.io.FileOutputStream; |
24 | 25 | import java.io.IOException; |
25 | 26 | import java.io.InputStream; |
@@ -27,6 +28,7 @@ import java.io.OutputStream; | ||
27 | 28 | import java.util.Map; |
28 | 29 | |
29 | 30 | |
31 | +import org.apache.commons.collections.ExtendedProperties; | |
30 | 32 | import org.apache.poi.hssf.usermodel.HSSFSheet; |
31 | 33 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; |
32 | 34 | import org.apache.poi.openxml4j.exceptions.InvalidFormatException; |
@@ -197,14 +199,66 @@ public class ReportMaker { | ||
197 | 199 | /** |
198 | 200 | * テンプレート一時ファイル |
199 | 201 | */ |
200 | - private File templateFile = null; | |
202 | + private File tempFile = null; | |
203 | + | |
204 | + /** | |
205 | + * カスタムパーサ用ファイル | |
206 | + */ | |
207 | + private ExtendedProperties customProperties = null; | |
201 | 208 | |
202 | 209 | /** |
203 | - * コンストラクタ | |
210 | + * デフォルトコンストラクタ | |
204 | 211 | */ |
205 | 212 | public ReportMaker() {} |
206 | 213 | |
207 | 214 | /** |
215 | + * カスタムパーサ用のプロパティファイルを指定 | |
216 | + * | |
217 | + * @param properties カスタムパーサ用プロパティファイル | |
218 | + */ | |
219 | + public ReportMaker(File properties) { | |
220 | + setCustomProperties(properties); | |
221 | + } | |
222 | + | |
223 | + /** | |
224 | + * カスタムパーサ用のプロパティファイルを指定 | |
225 | + * | |
226 | + * @param properties カスタムパーサ用プロパティファイル | |
227 | + */ | |
228 | + public ReportMaker(InputStream properties) { | |
229 | + setCustomProperties(properties); | |
230 | + } | |
231 | + | |
232 | + /** | |
233 | + * カスタムパーサ用のプロパティファイルセッター | |
234 | + * | |
235 | + * @param properties カスタムパーサ用プロパティファイル | |
236 | + */ | |
237 | + public void setCustomProperties(File properties) { | |
238 | + if(properties != null) { | |
239 | + try(InputStream stream = new FileInputStream(properties)) { | |
240 | + setCustomProperties(stream); | |
241 | + } | |
242 | + catch(Exception e) {} | |
243 | + } | |
244 | + } | |
245 | + | |
246 | + /** | |
247 | + * カスタムパーサ用のプロパティファイルセッター | |
248 | + * | |
249 | + * @param properties カスタムパーサ用プロパティファイル | |
250 | + */ | |
251 | + public void setCustomProperties(InputStream properties) { | |
252 | + try { | |
253 | + if(properties != null) { | |
254 | + customProperties = new ExtendedProperties(); | |
255 | + customProperties.load(properties); | |
256 | + } | |
257 | + } | |
258 | + catch(Exception e) {} | |
259 | + } | |
260 | + | |
261 | + /** | |
208 | 262 | * 入力ストリームのExcelテンプレートファイルにデータを挿入することでExcel帳票を生成、Workbookオブジェクトを返却する。<br> |
209 | 263 | * 返却されたWorkbookオブジェクトはPOIを使用し、加工・出力が可能。<br> |
210 | 264 | * 入力ストリームは別途クローズが必要。<br> |
@@ -227,15 +281,15 @@ public class ReportMaker { | ||
227 | 281 | } |
228 | 282 | |
229 | 283 | // 一時ファイル作成 |
230 | - if (templateFile != null && templateFile.exists()) { | |
231 | - templateFile.delete(); | |
232 | - log.debug("template file delete: {}", templateFile.getPath()); | |
233 | - templateFile = null; | |
284 | + if (tempFile != null && tempFile.exists()) { | |
285 | + tempFile.delete(); | |
286 | + log.debug("template file delete: {}", tempFile.getPath()); | |
287 | + tempFile = null; | |
234 | 288 | } |
235 | - templateFile = createTempFile(template); | |
289 | + tempFile = createTempFile(template); | |
236 | 290 | |
237 | 291 | // Excel帳票生成 |
238 | - Workbook book = makeReport(templateFile, parameter); | |
292 | + Workbook book = makeReport(tempFile, parameter); | |
239 | 293 | |
240 | 294 | log.trace("makeReport end"); |
241 | 295 | return book; |
@@ -538,10 +592,10 @@ public class ReportMaker { | ||
538 | 592 | log.debug("template file close."); |
539 | 593 | pkg = null; |
540 | 594 | } |
541 | - if (templateFile != null && templateFile.exists()) { | |
542 | - templateFile.delete(); | |
543 | - log.debug("template file delete: {}", templateFile.getPath()); | |
544 | - templateFile = null; | |
595 | + if (tempFile != null && tempFile.exists()) { | |
596 | + tempFile.delete(); | |
597 | + log.debug("template file delete: {}", tempFile.getPath()); | |
598 | + tempFile = null; | |
545 | 599 | } |
546 | 600 | } catch (IOException e) { |
547 | 601 | log.error("template file close error: {}", e.getMessage(), e); |
@@ -688,6 +742,9 @@ public class ReportMaker { | ||
688 | 742 | // 文字列セル、計算式セル |
689 | 743 | case Cell.CELL_TYPE_STRING: |
690 | 744 | case Cell.CELL_TYPE_FORMULA: |
745 | + if(log.isDebugEnabled()) { | |
746 | + log.debug("cell[{}] type is {}", (new CellReference(cell)).formatAsString(), (cell.getCellType() == Cell.CELL_TYPE_STRING ? "string" : "formula")); | |
747 | + } | |
691 | 748 | // パース |
692 | 749 | cellParser.parse(cell); |
693 | 750 | // 再パースフラグONなら |
@@ -698,7 +755,7 @@ public class ReportMaker { | ||
698 | 755 | break; |
699 | 756 | // 文字列セル、計算式セル以外 |
700 | 757 | default: |
701 | - log.debug("cell type is not string or formula"); | |
758 | + log.debug("cell[{}] type is not string or formula", (new CellReference(cell)).formatAsString()); | |
702 | 759 | continue; |
703 | 760 | } |
704 | 761 |