2 * Copyright 2014 Hanei Management Co.,Ltd.
4 * This file is part of Jaxcel
6 * Jaxcel is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Jaxcel is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 package org.hanei.jaxcel.parser;
21 import java.io.InputStream;
22 import java.text.CharacterIterator;
23 import java.text.StringCharacterIterator;
24 import java.util.ArrayDeque;
25 import java.util.Arrays;
26 import java.util.Deque;
27 import java.util.HashMap;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
32 import org.apache.commons.collections.ExtendedProperties;
33 import org.apache.poi.ss.usermodel.Cell;
34 import org.hanei.jaxcel.exception.JaxcelInputException;
35 import org.hanei.jaxcel.report.JaxcelContext;
36 import org.hanei.jaxcel.util.ExcelUtil;
37 import static org.hanei.jaxcel.parser.Constants.*;
38 import org.slf4j.LoggerFactory;
39 import org.slf4j.Logger;
42 * Excelテンプレートシートのセルに記述された指示子(Template Language)、EL式(Expression Language)の検索、パースを行う
45 * @author Noboru Saito
48 public class CellParser {
50 private static final Logger log = LoggerFactory.getLogger(CellParser.class);
52 private static final int PARSE_MAX = 10; // 同一セルパース最大回数
54 private ExtendedProperties config = new ExtendedProperties();
55 private Map<String, TLParser> parserMap = null;
56 private JaxcelContext context = null;
57 private Cell cell = null;
59 private boolean needReParse; // 同一セル再パース要否フラグ
60 private int parseCount; // 同一セルパース回数
62 private char elDirective;
63 private char elStartBracket;
64 private char elEndBracket;
65 private char tlEndBracket;
66 private char tlSymbol;
68 private StringCharacterIterator valueIterator = new StringCharacterIterator("");
69 private Pattern parserPattern = null;
70 private Matcher parserMatches = null;
75 * @param context Jaxlsコンテキストオブジェクト
77 * @throws JaxcelInputException デフォルトプロパティファイル入力例外発生時
79 public CellParser(JaxcelContext context) {
82 this.context = context;
85 try(InputStream inStream = getClass().getResourceAsStream(DEFAULT_PROPS_NAME)) {
86 config.load(inStream);
87 log.debug("default properties load");
88 } catch (Exception e) {
89 throw new JaxcelInputException("default properties load error", e);
92 if(this.context != null) {
93 ExtendedProperties customProperties = this.context.getCustomProperties();
94 if(customProperties != null) {
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)));
101 if(customProperties.containsKey(P_TL_DIRECTIVE)) {
102 for(String tlDirective : customProperties.getStringArray(P_TL_DIRECTIVE)) {
103 config.addProperty(P_TL_DIRECTIVE, Arrays.asList(customProperties.getStringArray(P_TL_DIRECTIVE)));
104 config.addProperty(String.format(P_TL_CLASS, tlDirective), customProperties.getString(String.format(P_TL_CLASS, tlDirective)));
111 parserMap = new HashMap<>();
114 context.setDateFormat(config.getStringArray(P_DATE_FORMAT));
115 context.setTimeFormat(config.getStringArray(P_TIME_FORMAT));
117 Class<?> cls = Class.forName(config.getString(P_EL_CLASS));
118 TLParser parser = (TLParser) cls.newInstance();
119 elDirective = config.getString(P_EL_DIRECTIVE).charAt(0);
120 elStartBracket = config.getString(P_EL_BRACKET_START).charAt(0);
121 elEndBracket = config.getString(P_EL_BRACKET_END).charAt(0);
122 parserMap.put(String.valueOf(elDirective) + String.valueOf(elStartBracket), parser);
123 log.debug("parser load: {} ", cls.getSimpleName());
126 tlSymbol = config.getString(P_TL_SYMBOL).charAt(0);
127 tlEndBracket = config.getString(P_TL_BRACKET_END).charAt(0);
128 for(String tlDirective : config.getStringArray(P_TL_DIRECTIVE)) {
129 cls = Class.forName(config.getString(String.format(P_TL_CLASS, tlDirective)));
130 parser = (TLParser) cls.newInstance();
131 parserMap.put(tlSymbol + tlDirective + config.getString(P_TL_BRACKET_START), parser);
132 log.debug("parser load: {} ", cls.getSimpleName());
136 log.error("TLParser load error. {}", e.getMessage(), e);
140 String matchPattern = "";
141 for(String key : parserMap.keySet()) {
142 matchPattern += (matchPattern.length() > 0 ? "|" : "") + Pattern.quote(key);
144 log.debug("parser matchPattern: {}", matchPattern);
145 parserPattern = Pattern.compile(matchPattern);
153 * @throws JaxcelInputException 入力例外発生時
155 public void parse(Cell cell) {
156 log.trace("parse start");
164 log.debug("cell is null");
165 log.trace("parse end");
170 if(this.cell == null) {
171 log.debug("start new cell parse");
177 // 保持しているセルと同一セルのパースの場合
178 else if(ExcelUtil.equalsCellAddress(this.cell, cell, true)) {
180 if(++parseCount >= PARSE_MAX) {
181 log.warn("parse repeat count over");
184 log.trace("parse end");
187 log.debug("start repeat cell parse");
189 // 保持しているセルと異なるセルのパースの場合
191 log.debug("start new cell parse");
199 // 文字列・計算式の場合は値保持。以外は終了
200 String cellValue = null;
201 switch(cell.getCellType()){
202 case Cell.CELL_TYPE_STRING:
203 cellValue = cell.getStringCellValue();
204 log.debug("value: {}", cellValue);
206 case Cell.CELL_TYPE_FORMULA:
207 cellValue = cell.getCellFormula();
208 log.debug("formula: {}", cellValue);
211 log.debug("cell type is not string or )formula");
214 log.trace("parse end");
219 TLParser parser = null;
220 Map<String, Object> attributeMap = new HashMap<>();
221 String matchParser = null;
222 String attrKey = null;
223 StringBuilder strBuf = new StringBuilder(1024);
224 StringBuilder templateString = new StringBuilder(1024);
225 Deque<Character> bracketStack = new ArrayDeque<>();
226 boolean parserExist = false;
227 boolean attrValueParsing;
228 boolean expressionParsing;
229 boolean attrKeyParsed;
233 parserMatches = parserPattern.matcher(cellValue);
235 findLoop: while(parserMatches.find()) {
236 matchParser = parserMatches.group();
237 log.debug("match: {}", matchParser);
240 parser = parserMap.get(matchParser);
242 log.warn("parser is null");
245 log.debug("parser: {}", parser.getClass().getSimpleName());
248 templateString.setLength(0);
249 templateString.append(matchParser);
251 bracketStack.clear();
252 attributeMap.clear();
253 attrValueParsing = false;
254 expressionParsing = false;
255 attrKeyParsed = false;
259 if(matchParser.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
261 expressionParsing = true;
262 log.debug("expressionParsing: {}", expressionParsing);
264 bracketStack.offerFirst(elStartBracket);
268 valueIterator.setText(cellValue);
269 for(char c = valueIterator.setIndex(parserMatches.start() + matchParser.length()); c != CharacterIterator.DONE; c = valueIterator.next()) {
271 templateString.append(c);
272 log.trace("current char: {}", c);
274 if(expressionParsing) {
275 log.trace("parsing EL");
276 // カーソル文字がEL式終端括弧であれば
277 if(elEndBracket == c) {
279 bracketStack.pollFirst();
281 if(bracketStack.size() == 0) {
283 attributeMap.put(EXPRESSION, getTrimString(strBuf));
284 log.debug("attributeMap: {}", attributeMap);
286 if(matchParser.equals(String.valueOf(elDirective) + String.valueOf(elStartBracket))) {
287 log.trace("parse end");
293 log.trace("EL parse end");
295 expressionParsing = false;
300 // 括弧スタック空でないならバッファに保持
303 log.trace("parsing: {}", strBuf.toString());
309 if(elStartBracket == c) {
311 bracketStack.offerFirst(c);
312 log.trace("bracketStack: {}", bracketStack.size());
316 log.trace("parsing: {}", strBuf.toString());
320 else if(attrValueParsing) {
321 log.trace("parsing attrValue");
322 // カーソル文字がダブルクォートであれば
323 if(VALUE_BRACKET == c) {
324 log.trace("escaping : {}", escaping);
331 log.trace("parsing: {}", strBuf.toString());
336 attributeMap.put(attrKey, strBuf.toString());
337 log.trace("attributeMap: {}", attributeMap);
339 attrValueParsing = false;
345 // カーソル文字がダブルクォートでないなら
347 log.trace("escaping : {}", escaping);
353 // エスケープ中でなくエスケープ文字(\)なら
354 else if(ESCAPE_CHAR == c) {
360 log.trace("parsing: {}", strBuf.toString());
364 else if(elDirective == c) {
365 log.trace("parsing elDirective");
367 c = valueIterator.next();
368 log.trace("next char: {}", c);
370 if(elStartBracket == c) {
371 templateString.append(c);
373 expressionParsing = true;
374 log.trace("expressionParsing : {}", expressionParsing);
376 if(strBuf.length() > 0) {
378 attributeMap.put(strBuf.toString(), null);
379 log.trace("attributeMap: {}", attributeMap);
388 c = valueIterator.previous();
389 log.trace("current char previous: {}", c);
392 log.trace("parsing: {}", strBuf.toString());
395 // カーソル文字が属性・属性値結合子であれば
396 else if(ATTR_DELIMITER == c) {
397 log.trace("parsing attr delimiter");
399 if(attrKey != null) {
401 attrKeyParsed = true;
402 log.trace("attrKeyParsed: {}", attrKeyParsed);
407 if(strBuf.length() > 0) {
409 attrKey = strBuf.toString();
410 log.trace("mapKey: {}", attrKey);
412 attributeMap.put(attrKey, null);
413 log.trace("attributeMap: {}", attributeMap);
417 attrKeyParsed = true;
418 log.trace("attrKeyParsed: {}", attrKeyParsed);
423 log.warn("parse end");
428 // カーソル文字がダブルクォートであれば
429 else if(VALUE_BRACKET == c) {
430 log.trace("parsing attr value bracket");
432 if(attrKeyParsed == true && attrKey != null) {
434 attrValueParsing = true;
435 attrKeyParsed = false;
436 log.trace("attrValueParsing: {}", attrValueParsing);
437 log.trace("attrKeyParsed: {}", attrKeyParsed);
444 log.warn("parse end");
449 else if(Character.isWhitespace(c) || EM_SPACE == c) {
450 log.trace("parsing white space");
452 if(strBuf.length() > 0) {
454 attrKey = strBuf.toString();
455 log.trace("mapKey: {}", attrKey);
456 attributeMap.put(attrKey, null);
457 log.trace("attributeMap: {}", attributeMap);
463 else if(tlEndBracket == c) {
464 log.trace("parsing tlEndBracket");
466 if(strBuf.length() > 0) {
468 attributeMap.put(strBuf.toString(), null);
469 log.trace("attributeMap: {}", attributeMap);
474 log.trace("parse end");
482 log.trace("parsing: {}", strBuf.toString());
487 log.trace("parser exist: {}", parserExist);
491 context.setCurrentCell(this.cell);
492 context.setTemplateString(templateString.toString());
493 context.setAttributeMap(attributeMap);
494 if(log.isDebugEnabled()) {
495 log.debug("templateString: {}", templateString.toString());
496 log.debug("attributeMap: {}", attributeMap);
500 parser.parse(context);
503 switch(this.cell.getCellType()){
504 case Cell.CELL_TYPE_STRING:
505 case Cell.CELL_TYPE_FORMULA:
506 // セルタイプが文字列、数式の場合は再パースフラグ設定
515 log.error("parser.parse error. {}", e.getMessage(), e);
522 log.debug("not found TL");
527 log.trace("parse end: needReParse: {} parseCount: {}", needReParse, parseCount);
533 * @return 再パース必要であればはtrue。不要であればfalse
535 public boolean isReParseCell() {
540 * StringBuilderで保持している文字列のトリム
545 private String getTrimString(StringBuilder string) {
546 if(string == null) return "";
549 String str = string.toString();
550 char[] val = str.toCharArray();
551 int len = val.length;
553 while ((st < len) && (val[st] <= ' ' || val[st] == ' ')) {
556 while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == ' ')) {
559 return ((st > 0) || (len < val.length)) ? str.substring(st, len) : str;