/**
 * Copyright 2014 Hanei Management Co.,Ltd. 
 * 
 * This file is part of Jaxcel
 * 
 *  Jaxcel is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Jaxcel is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.hanei.jaxcel.report;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.hssf.record.cf.CellRangeUtil;
import org.apache.poi.ss.formula.FormulaParseException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.hanei.jaxcel.exception.JaxcelInputException;
import org.hanei.jaxcel.util.ExcelUtil;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

/**
 * Excelテンプレートシートの指示子(Template Language)、EL式(Expression Language)の検索、パースを行う
 * 
 * @author Noboru Saito
 *
 */
public class TLParser {

	private static final Logger log = LoggerFactory.getLogger(TLParser.class);

	private static final String EL = "el";
	private static final String TL_IF = "if";
	private static final String TL_FOREACH = "foreach";
	private static final String TLP_EXPR = "${";
	private static final String TLP_DELETE = "delete";
	private static final String TLP_DIRECTION = "direction";
	private static final String TLP_SHIFT = "shift";
	private static final String TLP_BLOCK = "block";
	private static final String TLP_STYLE = "style";
	private static final String TLP_START = "start";
	private static final String TLP_END = "end";
	private static final String TLP_ROWS = "rows";
	private static final String TLP_COLS = "cols";
	
	private static final String LEFT = "left";
	private static final String UP = "up";
	private static final String CLEAR = "clear";
	private static final String ROW = "row";
	private static final String COL = "col";
	private static final String COPY = "copy";
	private static final int SPAN_DEF = 1;
	private static final int IDX_BASE = 1;
	private static final int PARSE_MAX = 10;
	
	private static final short FORMAT_GENERAL = 0;
	private static final short FORMAT_TEXT = 49;

	private Sheet sheet = null;
	private JaxcelContext context = null;
	private ELManager elMgr = null;
	private Cell cell = null;
	private int startRowIdx;
	private int startColIdx;

	private String type;		// TL式のタイプ
	private String expression;	// EL式
	private String delete;		// delete
	private int rowSpan;		// row
	private int colSpan;		// column
	private String direction;	// direction
	private boolean shift;		// shift
	private boolean block;		// block
	private String style;		// style
	private String start;		// start
	private String end;			// end
	private String list;		// list
	private String object;		// object
	private String matchString;	// マッチした文字列保持用
	private boolean reParseFlg;	// 同一セル再パース要否フラグ
	private int parseCount;		// 同一セルパース回数

	/**
	 *  ${expression}
	 */
	private final String  rgEl = "\\$\\{([^\\{\\}]+)\\}";
	private final Pattern ptEl = Pattern.compile(rgEl);
	private Matcher mtEl;

    /**
     *  #if(${expression} delete:"up|left|clear" block:"true|false" rows:"number" cols:"number")
     */
	private final String  rgIf = "#if\\(\\s*(\\$\\{[^\\{\\}]+\\})(?:\\s+(?:(delete\\s*:\\s*\"[^\"]*\")|(block\\s*:\\s*\"[^\"]*\")|(rows\\s*:\\s*\"\\d+\")|(cols\\s*:\\s*\"\\d+\"))){0,4}\\s*\\)";
	private final Pattern ptIf = Pattern.compile(rgIf);
	private Matcher mtIf;
	
    /**
     *  #foreach(${object in list} direction:"row|col" style:"copy|^copy" shift:"true|false" block:"true|false" start:"number" end:"number" rows:"number" cols:"number")
     */
	private final String  rgForeach = "#foreach\\(\\s*(\\$\\{\\s*\\S+\\s+in\\s+\\S+\\s*\\})(?:\\s+(?:(direction\\s*:\\s*\"[^\"]*\")|(style\\s*:\\s*\"[^\"]*\")|(shift\\s*:\\s*\"[^\"]*\")|(block\\s*:\\s*\"[^\"]*\")|(start\\s*:\\s*\"[^\"]*\")|(end\\s*:\\s*\"[^\"]*\")|(rows\\s*:\\s*\"\\d+\")|(cols\\s*:\\s*\"\\d+\"))){0,8}\\s*\\)";
	private final Pattern ptForeach = Pattern.compile(rgForeach);
	private Matcher mtForeach;

	/**
	 *  attribute
	 */
	private final String rgAttrEL = "\\$\\{([^\\{\\}]*)\\}";
	private final String rgAttr = "\\s*:\\s*\"([^\"]*)\"";
	private final String $1 = "$1";

	/**
	 *  指示子・EL式一括
	 */
	private final Pattern ptAll = Pattern.compile(rgIf + "|" + rgForeach + "|" + rgEl);
	private Matcher mtAll = null;

	/**
	 * 日付形式書式パターン
	 */
	private final String[] DATE_FORMATS = {
			"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
			"yyyy-MM-dd'T'HH:mm:ss.SSXXX",
			"yyyy-MM-dd'T'HH:mm:ss.SXXX",
			"yyyy-MM-dd'T'HH:mm:ss.SSSXX",
			"yyyy-MM-dd'T'HH:mm:ss.SSXX",
			"yyyy-MM-dd'T'HH:mm:ss.SXX",
			"yyyy-MM-dd'T'HH:mm:ss.SXXX",
			"yyyy-MM-dd'T'HH:mm:ss.SXX",
			"yyyy-MM-dd'T'HH:mm:ss.SX",
			"yyyy-MM-dd'T'HH:mm:ssXXX",
			"yyyy-MM-dd'T'HH:mm:ssXX",
			"yyyy-MM-dd'T'HH:mm:ssX",
			"yyyy-MM-dd'T'HH:mm:ss.S",
			"yyyy-MM-dd'T'HH:mm:ss",
			"yyyy-MM-dd'T'HH:mm",
			"yyyy-MM-dd HH:mm:ss.S",
			"yyyy-MM-dd HH:mm:ss",
			"yyyy-MM-dd HH:mm",
			"yyyy-MM-dd", 
			"yyyy/MM/dd HH:mm:ss.S", 
			"yyyy/MM/dd HH:mm:ss",
			"yyyy/MM/dd HH:mm",
			"yyyy/MM/dd"
	};
	private final String[] TIME_FORMATS = {
			"HH:mm:ss.S", 
			"HH:mm:ss",
			"HH:mm"
	};

	/**
	 * コンストラクタ
	 * 
	 * @param context	Jaxlsコンテキストオブジェクト
	 */
	public TLParser(JaxcelContext context) {
		this.context = context;
		if(this.context != null) {
			sheet = this.context.getCurrentSheet();
			elMgr = this.context.getElManager();
		}
	}
	
	/**
	 * EL式、If句、Foreach句のパース
	 * 
	 * @param cell	対象セル
	 * 
	 * @throws JaxcelInputException 入力例外発生時
	 */
	public void parse(Cell cell) {
		log.trace("parse start");

		// チェック
		if(elMgr == null) {
			log.error("ELManager is null");
			log.trace("parse end");
			throw new JaxcelInputException("ELManager is null");
		}
		if(cell == null) {
			// 再パースフラグクリア
			this.cell = null;
			reParseFlg = false;		
			parseCount = 0;
			log.debug("cell is null");
			log.trace("parse end");
			return;
		}
		
		// 新規対象セルの場合、保持
		if(this.cell == null) {
			log.debug("start new cell parse");
			this.cell = cell;
			// 再パースフラグ・カウントクリア
			reParseFlg = false;
			parseCount = 0;
		}
		else if(!this.cell.equals(cell)) {
			log.debug("start new cell parse");
			this.cell = cell;
			// 再パースフラグ・カウントクリア
			reParseFlg = false;
			parseCount = 0;
		}
		// 新規対象セルでない場合
		else {
			log.debug("start repeat cell parse");
			// 再パースカウント加算
			parseCount++;
		}

		// 再パース回数チェック
		if(parseCount >= PARSE_MAX) {
			log.warn("parse repeat count over");
			// 再パースフラグクリア
			reParseFlg = false;
			log.trace("parse end");
			return;
		}
		
		// 指示子,EL式検索
		if(find()) {
			// 検索ヒット

			// EL式
			if(isEL()) {
				evaluate();
			}
			// EL式以外
			else {
				// 指示子の消去
				switch (cell.getCellType()) {
				case Cell.CELL_TYPE_STRING:
					cell.setCellValue(replaceFirst(""));
					break;
				case Cell.CELL_TYPE_FORMULA:
					// まずはそのまま
					try {
						cell.setCellFormula(replaceFirst(""));
					}
					catch(FormulaParseException e) {
						log.debug("formula set error: {}", e.getMessage(), e);
						cell.setCellType(Cell.CELL_TYPE_BLANK);
						cell.setCellType(Cell.CELL_TYPE_STRING);
						cell.setCellValue(replaceFirst(""));
						log.debug("set value type: String");
					}
				}
				if(isIf()) {
					parseIf();
				}
				else if(isForeach()) {
					parseForeach();
				}
				// EL式以外は再パースフラグセット
				reParseFlg = true;
			}
		}
		else {
			// 検索ヒットせず
			log.debug("not found TL");
			// 再パースフラグクリア
			reParseFlg = false;
		}
		
		log.trace("parse end: reParseFlg: {} parseCount: {}", reParseFlg, parseCount);
	}

	/**
	 * テンプレート指示子の検索、データ保持
	 * 
	 * @param cell	検索対象セル
	 * 
	 * @return 検索結果
	 */
	private boolean find() {
		log.trace("matchTL start");
		
		boolean findFlg = false;

		// 初期化
		type = null;		// TL式のタイプ
		expression = null;	// EL式
		rowSpan = SPAN_DEF;	// rows
		colSpan = SPAN_DEF;	// cols
		delete = LEFT;		// delete
		list = null;		// list
		object = null;		// object
		direction = ROW;	// direction
		shift = true;		// shift
		block = true;		// block
		style = COPY;		// style
		start = null;		// start
		end = null;			// end
		matchString = null;
		String cellVal = null;
		
		// セルインデックス保持
		startRowIdx = cell.getRowIndex();
		startColIdx = cell.getColumnIndex();

		// セルタイプにより分岐
		// 文字列・計算式の場合は値保持。以外は終了
		switch(cell.getCellType()){
		case Cell.CELL_TYPE_STRING:
			cellVal = cell.getStringCellValue();
			log.debug("cell[{}] cellType: string  value: {}", (new CellReference(cell)).formatAsString(), cellVal);
			break;
		case Cell.CELL_TYPE_FORMULA:
			cellVal = cell.getCellFormula();
			log.debug("cell[{}] cellType: formula  value: {}", (new CellReference(cell)).formatAsString(), cellVal);
			break;
		default:
			log.debug("cell type is not string or formula");
			log.trace("matchTL end");
			return findFlg;
		}

		// 指示子（TL）の検索
		mtAll = ptAll.matcher(cellVal);
		// 指示子ヒット
		if(mtAll.find()) {
			matchString = mtAll.group();
			log.debug("match: {}", matchString);

			// IF文であるか
			if((mtIf = ptIf.matcher(matchString)).find()) {
				log.debug("type: {}", TL_IF);

				// タイプ保持
				type = TL_IF;
				findFlg = true;

				// データ保持
				for(int i = 1; i <= mtIf.groupCount(); i++) {
					if(mtIf.group(i) == null) continue;
					// ${...}  式
					if(mtIf.group(i).startsWith(TLP_EXPR)) {
						expression = mtIf.group(i).replaceAll(rgAttrEL, $1).trim();
						log.debug("expression: {}", expression);
					}
					// delete:"up|left|clear"  削除動作 デフォルト left
					else if(mtIf.group(i).startsWith(TLP_DELETE)) {
						delete = mtIf.group(i).replaceAll(TLP_DELETE + rgAttr, $1).trim();
						log.debug("{}: {}", TLP_DELETE, delete);
						if(!UP.equalsIgnoreCase(delete) && !LEFT.equalsIgnoreCase(delete) && !CLEAR.equalsIgnoreCase(delete)) {
							log.debug("{} is illegal argument. set default: {}", TLP_DELETE, LEFT);
							delete = LEFT;
						}
					}
					// blick:"true|false"  ブロック範囲で削除操作をするかの指定 デフォルト true
					else if(mtIf.group(i).startsWith(TLP_BLOCK)) {
						block = BooleanUtils.toBoolean(mtIf.group(i).replaceAll(TLP_BLOCK + rgAttr, $1).trim());
						log.debug("{}: {}", TLP_BLOCK, block);
					}
					// rows:"number"  範囲 デフォルト 1
					else if(mtIf.group(i).startsWith(TLP_ROWS)) {
						rowSpan = NumberUtils.toInt(mtIf.group(i).replaceAll(TLP_ROWS + rgAttr, $1).trim());
						log.debug("{}: {}", TLP_ROWS, rowSpan);
						if(rowSpan <= 0) {
							log.warn("{} is illegal argument. set default: {}", TLP_ROWS, SPAN_DEF);
							rowSpan = SPAN_DEF;
						}
					}
					// cols:"number"  範囲 デフォルト 1
					else if(mtIf.group(i).startsWith(TLP_COLS)) {
						colSpan = NumberUtils.toInt(mtIf.group(i).replaceAll(TLP_COLS + rgAttr, $1).trim());
						log.debug("{}: {}", TLP_COLS, colSpan);
						if(colSpan <= 0) {
							log.warn("{} is illegal argument. set default: {}", TLP_COLS, SPAN_DEF);
							colSpan = SPAN_DEF;
						}
					}
				}
			}
			// Foreach文であるか
			else if((mtForeach = ptForeach.matcher(matchString)).find()) {
				log.debug("type: {}", TL_FOREACH);

				// タイプ保持
				type = TL_FOREACH;
				findFlg = true;
				
				// データ保持
				String[] tmp;
				for(int i = 1; i <= mtForeach.groupCount(); i++) {
					if(mtForeach.group(i) == null) continue;
					// ${objct in list}
					if(mtForeach.group(i).startsWith(TLP_EXPR)) {
						expression = mtForeach.group(i).replaceAll(rgAttrEL, $1).trim();
						log.debug("expression: {}", expression);
						tmp = expression.split("\\s+");
						if(tmp != null && tmp.length == 3) {
							object = tmp[0];
							list = tmp[2];
							if(log.isDebugEnabled()) {
								log.debug("object: {}", object);
								log.debug("list: {}", list);
							}
						}
						else {
							log.warn("format error: ex) object in list");
						}
					}
				    // direction:"row|col"  繰返し方向 デフォルト row
					else if(mtForeach.group(i).startsWith(TLP_DIRECTION)) {
						direction = mtForeach.group(i).replaceAll(TLP_DIRECTION + rgAttr, $1);
						log.debug("{}: {}", TLP_DIRECTION, direction);
						if(!ROW.equalsIgnoreCase(direction) && !COL.equalsIgnoreCase(direction)) {
							log.warn("{} is illegal argument. set default: {}", TLP_DIRECTION, ROW);
							direction = ROW;
						}
					}
					// shift:"true|false"  foreachレンジより後のセルに対するシフト指定 デフォルト false
					else if(mtForeach.group(i).startsWith(TLP_SHIFT)) {
						shift = BooleanUtils.toBoolean(mtForeach.group(i).replaceAll(TLP_SHIFT + rgAttr, $1).trim());
						log.debug("{}: {}", TLP_SHIFT, shift);
					}
					// blick:"true|false"  foreachレンジをブロック範囲で繰り返すかの指定 デフォルト true
					else if(mtForeach.group(i).startsWith(TLP_BLOCK)) {
						block = BooleanUtils.toBoolean(mtForeach.group(i).replaceAll(TLP_BLOCK + rgAttr, $1).trim());
						log.debug("{}: {}", TLP_BLOCK, block);
					}
					// style:"copy|^copy"  foreachレンジのセルスタイルのコピー指定 デフォルト copy
					else if(mtForeach.group(i).startsWith(TLP_STYLE)) {
						style = mtForeach.group(i).replaceAll(TLP_STYLE + rgAttr, $1).trim();
						log.debug("{}: {}", TLP_STYLE, style);
					}
				    // start:"numberExpression"
					else if(mtForeach.group(i).startsWith(TLP_START)) {
						start = mtForeach.group(i).replaceAll(TLP_START + rgAttr, $1);
						log.debug("{}: {}", TLP_START, start);
					}
				    // end:"numberExpression"
					else if(mtForeach.group(i).startsWith(TLP_END)) {
						end = mtForeach.group(i).replaceAll(TLP_END + rgAttr, $1);
						log.debug("{}: {}", TLP_END, end);
					}
					// rows:"number"  範囲 デフォルト 1
					else if(mtForeach.group(i).startsWith(TLP_ROWS)) {
						rowSpan = NumberUtils.toInt(mtForeach.group(i).replaceAll(TLP_ROWS + rgAttr, $1).trim());
						log.debug("{}: {}", TLP_ROWS, rowSpan);
						if(rowSpan <= 0) {
							log.warn("{} is illegal argument. set default: {}", TLP_ROWS, SPAN_DEF);
							rowSpan = SPAN_DEF;
						}
					}
					// cols:"number"  範囲 デフォルト 1
					else if(mtForeach.group(i).startsWith(TLP_COLS)) {
						colSpan = NumberUtils.toInt(mtForeach.group(i).replaceAll(TLP_COLS + rgAttr, $1).trim());
						log.debug("{}: {}", TLP_COLS, colSpan);
						if(colSpan <= 0) {
							log.warn("{} is illegal argument. set default: {}", TLP_COLS, SPAN_DEF);
							colSpan = SPAN_DEF;
						}
					}
				}
			}
			// オブジェクト（EL）のヒット
			else if((mtEl = ptEl.matcher(matchString)).find()) {
				log.debug("type: {}", EL);

				// タイプ保持
				type = EL;
				findFlg = true;

				// ${...}
				expression = mtEl.group(1).trim();
				log.debug("expression: {}", expression);
			}
			// 不明
			else {
				log.warn("TL type: ???");
				// 再パースフラグクリア
				reParseFlg = false;
			}
		}
		else {
			log.debug("unmatch");
		}
		
		log.trace("matchTL end: {}", findFlg);
		return findFlg;
	}

	/**
	 * EL式の評価
	 */
	private void evaluate() {
		log.trace("evaluate start");

		Object elResult;		// パース結果保持用
		String newCellVal;		// セル値

		// evaluate
		elResult = elMgr.evaluate(expression);
		if(elResult == null) {
			log.debug("evaluate result is null");
		}
		else {
			log.debug("evaluate result: {}", elResult.toString());
		}
		// マッチ１件目をevaluate結果で置換 
		newCellVal = replaceFirst(elResult == null ? "" : elResult.toString());
		log.debug("replace value: {}", newCellVal);
		
		// newCellValが空白でないなら
		if(!"".equals(newCellVal.trim())) {
			// 置換後の値の形式チェック
			// 置換後の値が数値型
			if(NumberUtils.isNumber(newCellVal)) {
				// もともと文字列セルなら数値セルに変更し値をセット
				if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
					cell.setCellType(Cell.CELL_TYPE_BLANK);
					cell.setCellType(Cell.CELL_TYPE_NUMERIC);
					// まずは整数変換
					try {
						cell.setCellValue(Integer.parseInt(newCellVal));
						log.debug("set value type: Integer");
					}
					// エラー発生で実数変換
					catch(NumberFormatException e) {
						cell.setCellValue(NumberUtils.toDouble(newCellVal));
						log.debug("set value type: Double");
					}
				}
				// もともと計算式セルの場合は計算式として値をセット
				else {
					// まずはそのまま
					try {
						cell.setCellFormula(newCellVal);
						log.debug("set value type: Formula");
					}
					// エラー発生で数値としてセット
					catch(FormulaParseException e) {
						log.warn("formula set error: {}", e.getMessage(), e);
						cell.setCellType(Cell.CELL_TYPE_BLANK);
						cell.setCellType(Cell.CELL_TYPE_NUMERIC);
						// まずは整数変換
						try {
							cell.setCellValue(Integer.parseInt(newCellVal));
							log.debug("set value type: Integer");
						}
						// エラー発生で実数変換
						catch(NumberFormatException e2) {
							cell.setCellValue(NumberUtils.toDouble(newCellVal));
							log.debug("set value type: Double");
						}
					}
				}
				// 再パースフラグクリア
				reParseFlg = false;
			}
			// 置換後の値がBool型
			else if(StringUtils.equalsIgnoreCase(newCellVal, "true") || StringUtils.equalsIgnoreCase(newCellVal, "false")) {
				// もともと文字列セルならBoolセルに変更し値をセット
				if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
					cell.setCellType(Cell.CELL_TYPE_BLANK);
					cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
					cell.setCellValue(BooleanUtils.toBoolean(newCellVal));
					log.debug("set value type: Boolean");
				}
				// もともと計算式セルの場合は計算式として値をセット
				else {
					// まずはそのまま
					try {
						cell.setCellFormula(newCellVal);
						log.debug("set value type: Formula");
					}
					// エラー発生でBoolとしてセット
					catch(FormulaParseException e) {
						log.warn("formula set error: {}", e.getMessage(), e);
						cell.setCellType(Cell.CELL_TYPE_BLANK);
						cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
						cell.setCellValue(BooleanUtils.toBoolean(newCellVal));
						log.debug("set value type: Boolean");
					}
				}
				// 再パースフラグクリア
				reParseFlg = false;
			}
			// 置換後の値が上記以外
			else {
				Date tmpDate = null;
				int valType = -1;
				for(int i = 0; i < 3; i++) {
					switch(i) {
					case 0:
						try {
							// 置換後の値が日付・日付時刻型
							tmpDate = DateUtils.parseDateStrictly(newCellVal, Locale.getDefault(), DATE_FORMATS);
							valType = i;
						} catch (ParseException e1) {}
						break;
					case 1:
						try {
							// 置換後の値が時刻型
							tmpDate = DateUtils.parseDateStrictly(newCellVal, TIME_FORMATS);
							valType = i;
						} catch (ParseException e1) {}
						break;
					default:
						// 置換後の値が上記以外（文字列）
						valType = i;
						break;
					}
					if(valType >= 0) break;
				}
				
				switch(valType) {
				case 0:
				case 1:
					// 置換後の値が日付・日付時刻・時刻型
					// もともと文字列セルなら数値セルに変更し値をセット
					if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
						cell.setCellType(Cell.CELL_TYPE_BLANK);
						cell.setCellType(Cell.CELL_TYPE_NUMERIC);
						// 書式文字列なら文字列のまま挿入
						if(FORMAT_GENERAL == cell.getCellStyle().getDataFormat() || FORMAT_TEXT == cell.getCellStyle().getDataFormat()) {
							cell.setCellValue(newCellVal);
							log.debug("set value type: String");
						}
						else {
							switch(valType) {
							case 0:
								// 置換後の値が日付・日付時刻型ならDateでセット
								cell.setCellValue(tmpDate);
								log.debug("set value type: Date");
								break;
							case 1:
								// 置換後の値が時刻型ならDoubleでセット(デフォルトタイムゾーン分加算）
								cell.setCellValue((tmpDate.getTime() + TimeZone.getDefault().getRawOffset()) / (1000.0 * 60 * 60 * 24));
								log.debug("set value type: double");
								break;
							}
						}
					}
					// もともと計算式セルの場合は計算式として値をセット
					else {
						// まずはそのまま
						try {
							cell.setCellFormula(newCellVal);
							log.debug("set value type: Formula");
						}
						// エラー発生で数値としてセット
						catch(FormulaParseException e) {
							log.warn("formula set error: {}", e.getMessage(), e);
							cell.setCellType(Cell.CELL_TYPE_BLANK);
							cell.setCellType(Cell.CELL_TYPE_NUMERIC);
							cell.setCellValue(tmpDate);
							log.debug("set value type: Date");
						}
					}
					// 再パースフラグクリア
					reParseFlg = false;
					break;
				default:
					// 置換後の値が日付・日付時刻・時刻型以外（文字列と判断）
					// 元のセルタイプのまま値をセット
					if(cell.getCellType() == Cell.CELL_TYPE_STRING) {
						cell.setCellValue(newCellVal);
						log.debug("set value type: String");
					} 
					else {
						// 式
						try {
							cell.setCellFormula(newCellVal);
							log.debug("set value type: Formula");
						}
						catch(FormulaParseException e2) {
							log.warn("formula set error: {}", e2.getMessage(), e2);
							cell.setCellType(Cell.CELL_TYPE_BLANK);
							cell.setCellType(Cell.CELL_TYPE_STRING);
							cell.setCellValue(newCellVal);
							log.debug("set value type: String");
						}
					}
					// 再パースフラグON
					reParseFlg = true;
				}
			}
		}
		// newCellValが空白
		else {
			cell.setCellType(Cell.CELL_TYPE_BLANK);
			// 再パースフラグクリア
			reParseFlg = false;
		}

		log.trace("evaluate end");
	}

	/**
	 * Foreach句のパース
	 */
	private void parseForeach() {
		log.trace("parseForeach start");

		Row fromRow, toRow;
		Cell fromCell, toCell;
		Object evalObject;
		Object listObject;
		Object[] mapKeys = null;
		int listSize;
		ArrayList<CellRangeAddress> rangeList = new ArrayList<>(); 
		CellRangeAddress fromRange, toRange, tmpBfRange, tmpAfRange;

		// リスト
		listObject = elMgr.evaluate(list);
		if(listObject == null) {
			log.debug("list is null eval response.");
			log.trace("parseForeach end");
			return; 
		}
		log.debug("list class: {}", listObject.getClass().getName());

		// listObject Mapならキー取得
		if(listObject instanceof Map) {
			mapKeys = ((Map<?, ?>)listObject).keySet().toArray();
		}

		// listのサイズ取得
		evalObject = elMgr.evaluate("size(" + list + ")");
		// evalした結果がintでなければリスト不可なオブジェクトと判断
		if(evalObject == null) {
			log.error("list size unknown");
			log.trace("parseForeach end");
			return; 
		}
		else if(!(evalObject instanceof Integer)) {
			log.error("list size is not Integer instance");
			log.trace("parseForeach end");
			return; 
		}
		listSize = (int) evalObject;
		log.debug("list size: {}", listSize);

		// start インデックス
		int startIdx;
		if(start != null) {
			log.debug("start: {}", start);
			// size(list) 等 式の可能性もあるのでevalする
			evalObject = elMgr.evaluate(start);
			if(evalObject != null) {
				// evalした結果がintでなければデフォルト
				if(!(evalObject instanceof Integer)) {
					log.warn("start is not Integer instance. start set default: {}", IDX_BASE);
					startIdx = IDX_BASE;
				}
				else {
					startIdx = (int) evalObject;
					// startIdxの補正
					if(startIdx == 0) {
						// startIdxが0の場合、1に補正
						startIdx = IDX_BASE;
					}
					else if(startIdx < 0) {
						// startIdxがマイナス値の場合、listのサイズからの逆順とする
						// -1 の場合リストのサイズ
						startIdx = listSize + startIdx + IDX_BASE;
					}
					else if(startIdx > listSize) {
						// startIdxがリストのサイズより大きい場合、リストのサイズに切り詰める
						startIdx = listSize;
					}
				}
			}
			// evalした結果がnullの場合はデフォルト
			else {
				log.debug("start is null eval response. start set default: {}", IDX_BASE);
				startIdx = IDX_BASE;
			}
		}
		else {
			log.debug("start is null. start set default: {}", IDX_BASE);
			startIdx = IDX_BASE;
		}
		log.debug("startIdx: {}", startIdx);

		// end インデックス
		int endIdx;
		if(end != null) {
			log.debug("end: {}", end);
			// size(list) 等 式の可能性もあるのでevalする
			evalObject = elMgr.evaluate(end);
			if(evalObject != null) {
				// evalした結果がintでなければリストサイズ
				if(!(evalObject instanceof Integer)) {
					log.warn("end is not Integer instance. end set list size: {}", listSize);
					endIdx = listSize;
				}
				else {
					endIdx = (int) evalObject;
					// endIdxの補正
					if(endIdx == 0) {
						// endIdxが0の場合、リストサイズ
						endIdx = listSize;
					}
					else if(endIdx < 0) {
						// endIdxがマイナス値の場合、listのサイズからの逆順とする
						// -1 の場合リストのサイズ
						endIdx = listSize + endIdx + IDX_BASE;
					}
					else if(endIdx > listSize) {
						// endIdxがリストのサイズより大きい場合、リストのサイズに切り詰める
						endIdx = listSize;
					}
				}
			}
			// evalした結果がnullの場合はリストサイズ
			else {
				log.debug("end is null eval response. end set list size: {}", listSize);
				endIdx = listSize;
			}
		}
		else {
			log.debug("end is null. end set list size: {}", listSize);
			endIdx = listSize;
		}
		
		if(log.isDebugEnabled()) {
			log.debug("endIdx: {}", endIdx);
			log.debug("direction: {}", direction);
			log.debug("shift: {}", shift);
			log.debug("block: {}", block);
			log.debug("style: {}", style);
		}
		
		// コピー元範囲 block:falseの場合、行・列全体に範囲を広げる
		if(!block) {
			// 繰返し方向横（列）（行がデフォルト）
			if(COL.equalsIgnoreCase(direction)) {
				fromRange = new CellRangeAddress(sheet.getFirstRowNum(), sheet.getLastRowNum(), startColIdx, startColIdx + colSpan - 1);
			}
			// 繰返し方向縦（行）の場合
			else {
				fromRange = new CellRangeAddress(startRowIdx, startRowIdx + rowSpan - 1, 0, ExcelUtil.getLastColNum(sheet));
			}
		}
		else {
			fromRange = new CellRangeAddress(startRowIdx, startRowIdx + rowSpan - 1, startColIdx, startColIdx + colSpan - 1);
		}
		// シートに収まる範囲にリサイズ
		fromRange = ExcelUtil.getIntersectRange(sheet, fromRange);

		// 繰返しコピー回数１以上なら、コピー先範囲シフト・クリア等実施
		if(startIdx != endIdx) {
			// コピー先範囲の範囲特定
			int copyCount = startIdx < endIdx ? endIdx - startIdx : startIdx - endIdx;
			// 繰返し方向横（列）（行がデフォルト）
			if(COL.equalsIgnoreCase(direction)) {
				toRange = new CellRangeAddress(
						fromRange.getFirstRow(), 
						fromRange.getLastRow(), 
						fromRange.getLastColumn() + 1, 
						fromRange.getLastColumn() + (fromRange.getLastColumn() - fromRange.getFirstColumn() + 1) * copyCount);
			}
			// 繰返し方向縦（行）の場合
			else {
				toRange = new CellRangeAddress(
						fromRange.getLastRow() + 1, 
						fromRange.getLastRow() + (fromRange.getLastRow() - fromRange.getFirstRow() + 1) * copyCount, 
						fromRange.getFirstColumn(), 
						fromRange.getLastColumn());
			}

			// shift:trueの場合、コピー先範囲の後続範囲をシフト
			if(shift) {
				// 移動量
				int distance = (startIdx > endIdx ? startIdx - endIdx : endIdx - startIdx);

				// 繰返し方向横（列）（行がデフォルト）
				if(COL.equalsIgnoreCase(direction)) {
					distance *= colSpan;
					// 繰返し範囲以降をずらす
					ExcelUtil.shift(sheet, new CellRangeAddress(fromRange.getFirstRow(), fromRange.getLastRow(), fromRange.getLastColumn() + 1, fromRange.getLastColumn() + 1), COL, distance, block);
				}
				// 繰返し方向縦（行）の場合
				else {
					distance *= rowSpan;
					// 繰返し範囲以降をずらす
					ExcelUtil.shift(sheet, new CellRangeAddress(fromRange.getLastRow() + 1, fromRange.getLastRow() + 1, fromRange.getFirstColumn(), fromRange.getLastColumn()), ROW, distance, block);
				}
			}
			// shift:falseの場合、コピー先範囲をクリア
			else {
				ExcelUtil.clearRange(sheet, toRange, COPY.equalsIgnoreCase(style), COPY.equalsIgnoreCase(style));
			}
			// style:copyの場合、コピー元範囲の結合保持・解除
			if(COPY.equalsIgnoreCase(style)) {
				// コピー元範囲の結合状態チェック・保持。コピー先範囲の結合解除
				CellRangeAddress tmpRange;
				for(int i = 0; i < sheet.getNumMergedRegions(); i++) {
					tmpRange = sheet.getMergedRegion(i);
					// コピー元範囲の結合状態保持・解除
					switch(CellRangeUtil.intersect(fromRange, tmpRange)) {
					case CellRangeUtil.INSIDE:
						rangeList.add(tmpRange);
						log.debug("from range inside mergedRegion. save: {}", tmpRange.formatAsString());
						break;
					case CellRangeUtil.ENCLOSES:
						log.warn("from range encloses mergedRegion. remove: {}", tmpRange.formatAsString());
						sheet.removeMergedRegion(i);
						break;
					case CellRangeUtil.OVERLAP:
						log.warn("from range overlap mergedRegion. remove: {}", tmpRange.formatAsString());
						sheet.removeMergedRegion(i);
						break;
					}
				}
			}
		}

		// 繰返し回数でループしコピー
		boolean copyFlg = true;
		for(int i = startIdx, cpCount = 0; (startIdx <= endIdx && i <= endIdx || startIdx > endIdx && i >= endIdx); i = (startIdx <= endIdx ? i + 1 : i - 1), cpCount++) {
			// 範囲行でループ
			for(int r = fromRange.getFirstRow(); r <= fromRange.getLastRow(); r++) {
				// 繰返し方向縦（行）の場合、コピー先の範囲チェック
				if(ROW.equalsIgnoreCase(direction) && (r + rowSpan * cpCount < 0 || r + rowSpan * cpCount > ExcelUtil.getMaxRowIndex(sheet))) {
					// コピー先行がシート範囲外
					log.warn("to row outside sheet");
					// 繰返しコピーフラグfalse
					copyFlg = false;
					break;
				}

				// コピー元行
				fromRow = sheet.getRow(r);
				// コピー先行
				toRow = ROW.equalsIgnoreCase(direction) ? sheet.getRow(r + rowSpan * cpCount) : fromRow;

				// コピー元行がnullならコピー先行もnull。continue
				if(fromRow == null) {
					if(toRow != null) {
						log.debug("continue. from row [{}] is null. to row [{}] remove", (r + 1), (toRow.getRowNum() + 1));
						sheet.removeRow(toRow);
					}
					else {
						log.debug("continue. from row [{}] and to row [{}] is null", (r + 1), ((ROW.equalsIgnoreCase(direction) ? (r + rowSpan * cpCount) : r) + 1));
					}
					continue;
				} 
				
				// コピー先行がnullなら生成
				if(toRow == null) toRow = sheet.createRow(r + rowSpan * cpCount);
				
				// コピー元行・コピー先行 行番号が異なり、block falseなら行情報(高さ)もコピー
				if(fromRow.getRowNum() != toRow.getRowNum() && !block) toRow.setHeight(fromRow.getHeight());

				// 範囲列でループ
				for(int c = fromRange.getFirstColumn(); c <= fromRange.getLastColumn(); c++) {
					// 繰返し方向横（列）の場合、コピー先の範囲チェック
					if(COL.equalsIgnoreCase(direction) && (c + colSpan * cpCount < 0 || c + colSpan * cpCount > ExcelUtil.getMaxColumnIndex(sheet))) {
						// コピー先列がシート範囲外
						log.warn("to cell outside sheet");
						break;
					}

					// コピー元セル
					fromCell = fromRow.getCell(c);
					// コピー先セル
					toCell = toRow.getCell(COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c);
					
					// コピー元セルがnullならコピー先セルもnull
					if(fromCell == null) {
						if(toCell != null) {
							log.debug("continue. from cell [{}] is null. to cell [{}] remove", (new CellReference(fromRow.getRowNum(), c)).formatAsString(), (new CellReference(toCell.getRowIndex(), toCell.getColumnIndex())).formatAsString());
							toRow.removeCell(toCell);
						}
						else {
							log.debug("continue. from cell [{}] and to cell [{}] is null", (new CellReference(fromRow.getRowNum(), c)).formatAsString(), (new CellReference(toRow.getRowNum(), COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c)).formatAsString());
						}
						continue;
					}

					// コピー先セルがnullなら生成
					if(toCell == null) toCell = toRow.createCell(COL.equalsIgnoreCase(direction) ? c + colSpan * cpCount : c);

					// コピー元列・コピー先列 列番号が異なり、block falseなら列情報(幅)もコピー  （ 繰返しループ１回目のみ）
					if(cpCount == 0 && fromCell.getColumnIndex() != toCell.getColumnIndex() && !block)
						sheet.setColumnWidth(toCell.getColumnIndex(), sheet.getColumnWidth(fromCell.getColumnIndex()));

					// スタイルコピーなら
					if(cpCount > 0 && COPY.equalsIgnoreCase(style)) {
						// スタイルのコピー
						toCell.setCellStyle(fromCell.getCellStyle());
					}
					// 値のコピー
					switch(fromCell.getCellType()) {
					case Cell.CELL_TYPE_BLANK:
						toCell.setCellType(Cell.CELL_TYPE_BLANK);
						break;
					case Cell.CELL_TYPE_BOOLEAN:
						toCell.setCellType(Cell.CELL_TYPE_BOOLEAN);
						toCell.setCellValue(fromCell.getBooleanCellValue());
						break;
					case Cell.CELL_TYPE_ERROR:
						toCell.setCellType(Cell.CELL_TYPE_ERROR);
						toCell.setCellErrorValue(fromCell.getErrorCellValue());
						break;
					case Cell.CELL_TYPE_FORMULA:
						toCell.setCellType(Cell.CELL_TYPE_FORMULA);
						// 数式のパース・移動先に合わせる
						toCell.setCellFormula(
								ExcelUtil.getMoveFormula(
										sheet, 
										fromCell.getCellFormula().replace(
												(cpCount == 0 ? object : "(" + list + ")[" + (mapKeys == null ? (startIdx - 1) : "'" + mapKeys[startIdx - 1] + "'") + "]"), "(" + list + ")[" + (mapKeys == null ? (i - 1) : "'" + mapKeys[i - 1] + "'") + "]"), 
										toCell.getRowIndex() - fromCell.getRowIndex(), 
										toCell.getColumnIndex() - fromCell.getColumnIndex()
								)
						);
						log.debug("to cell Formula: {}", toCell.getCellFormula());
						break;
					case Cell.CELL_TYPE_NUMERIC:
						toCell.setCellType(Cell.CELL_TYPE_NUMERIC);
						toCell.setCellValue(fromCell.getNumericCellValue());
						break;
					case Cell.CELL_TYPE_STRING:
						toCell.setCellType(Cell.CELL_TYPE_STRING);
						toCell.setCellValue(fromCell.getStringCellValue().replace((cpCount == 0 ? object : "(" + list + ")[" + (mapKeys == null ? (startIdx - 1) : "'" + mapKeys[startIdx - 1] + "'") + "]"), "(" + list + ")[" + (mapKeys == null ? (i - 1) : "'" + mapKeys[i - 1] + "'") + "]"));
						log.debug("to cell string: {}", toCell.getStringCellValue());
						break;
					}
				}
			}
			// 繰返しコピーフラグfalseならbreak
			if(!copyFlg) break;
		}
//		// コピー元範囲を繰返しコピー
//		// 範囲行でループ
//		for(int r = fromRange.getFirstRow(); r <= fromRange.getLastRow(); r++) {
//			row = sheet.getRow(r);
//			// コピー元行がnullで繰返し方向横（列）の場合
//			if(row == null && COL.equalsIgnoreCase(direction)) {
//				// コピー先行もnullとなるのでセルの処理はせず終了
//				log.debug("continue. direction: col from row [{}] is null", (r + 1));
//				continue;
//			}
//
//			// 範囲列でループ
//			for(int c = fromRange.getFirstColumn(); c <= fromRange.getLastColumn(); c++) {
//				// コピー元行がnullの場合セルもnull
//				if(row == null) {
//					cell = null;
//				} else {
//					cell = row.getCell(c);
//				}
//
//				// 繰返し回数でループ
//				for(int i = startIdx, cpCount = 0; (startIdx <= endIdx && i <= endIdx || startIdx > endIdx && i >= endIdx); i = (startIdx <= endIdx ? i + 1 : i - 1), cpCount++) {
//					// 繰返し方向横（列）（行がデフォルト）
//					if(COL.equalsIgnoreCase(direction)) {
//						// 範囲チェック
//						if(c + colSpan * cpCount < 0 || c + colSpan * cpCount > ExcelUtil.getMaxColumnIndex(sheet)) {
//							log.warn("to cell outside sheet");
//							break;
//						}
//						toRow = row;
//						toCell = row.getCell(c + colSpan * cpCount);
//						// コピー元セルがnullならコピー先セルもnull
//						if(cell == null) {
//							if(toCell != null) {
//								toRow.removeCell(toCell);
//								log.debug("continue. from cell [{}] is null. to cell [{}] remove", (new CellReference(r, c)).formatAsString(), (new CellReference(r, c + colSpan * cpCount)).formatAsString());
//							}
//							else {
//								log.debug("continue. from cell [{}] and to cell [{}] is null", (new CellReference(r, c)).formatAsString(), (new CellReference(r, c + colSpan * cpCount)).formatAsString());
//							}
//							continue;
//						}
//						else {
//							if(toCell == null) toCell = row.createCell(c + colSpan * cpCount);
//							// block falseなら列情報(幅)もコピー
//							if(!block) {
//								sheet.setColumnWidth(toCell.getColumnIndex(), sheet.getColumnWidth(cell.getColumnIndex()));
//							}
//						}
//					}
//					// 繰返し方向縦（行）の場合
//					else {
//						// 範囲チェック
//						if(r + rowSpan * cpCount < 0 || r + rowSpan * cpCount > ExcelUtil.getMaxRowIndex(sheet)) {
//							log.warn("to row outside sheet");
//							break;
//						}
//						// 繰返し先の行がなければ生成
//						toRow = sheet.getRow(r + rowSpan * cpCount);
//						toCell = null;
//						// コピー元行がnullならコピー先行もnull
//						if(row == null) {
//							if(toRow != null) {
//								sheet.removeRow(toRow);
//								log.debug("continue. from row [{}] is null. to row [{}] remove", (r + 1));
//							}
//							else {
//								log.debug("continue. from and to row is null");
//							}
//							continue;
//						} 
//						else {
//							if(toRow == null) toRow = sheet.createRow(r + rowSpan * cpCount);
//							// block falseなら行情報(高さ)もコピー
//							if(!block) {
//								toRow.setHeight(row.getHeight());
//							}
//							toCell = toRow.getCell(c);
//							// コピー元セルがnullならコピー先セルもnull
//							if(cell == null) {
//								if(toCell != null) {
//									toRow.removeCell(toCell);
//									log.debug("continue. from cell is null. to cell remove");
//								}
//								else {
//									log.debug("continue. from cell [{}] and to cell [{}] is null", (new CellReference(r, c)).formatAsString(), (new CellReference(r + rowSpan * cpCount, c)).formatAsString());
//								}
//								continue;
//							}
//							else {
//								if(toCell == null) toCell = toRow.createCell(c);
//							}
//						}
//					}
//
//					// スタイルコピーなら
//					if(cpCount > 0 && COPY.equalsIgnoreCase(style)) {
//						// スタイルのコピー
//						toCell.setCellStyle(cell.getCellStyle());
//					}
//					// 値のコピー
//					switch(cell.getCellType()) {
//					case Cell.CELL_TYPE_BLANK:
//						toCell.setCellType(Cell.CELL_TYPE_BLANK);
//						break;
//					case Cell.CELL_TYPE_BOOLEAN:
//						toCell.setCellType(Cell.CELL_TYPE_BOOLEAN);
//						toCell.setCellValue(cell.getBooleanCellValue());
//						break;
//					case Cell.CELL_TYPE_ERROR:
//						toCell.setCellType(Cell.CELL_TYPE_ERROR);
//						toCell.setCellErrorValue(cell.getErrorCellValue());
//						break;
//					case Cell.CELL_TYPE_FORMULA:
//						toCell.setCellType(Cell.CELL_TYPE_FORMULA);
//						// 数式のパース・移動先に合わせる
//						toCell.setCellFormula(
//								ExcelUtil.getMoveFormula(
//										sheet, 
//										cell.getCellFormula().replace(
//												(i == startIdx ? object : list + "[" + (mapKeys == null ? (startIdx - 1) : "'" + mapKeys[startIdx - 1] + "'") + "]"), list + "[" + (mapKeys == null ? (i - 1) : "'" + mapKeys[i - 1] + "'") + "]"), 
//										toCell.getRowIndex() - cell.getRowIndex(), 
//										toCell.getColumnIndex() - cell.getColumnIndex()
//								)
//						);
//						log.debug("to cell Formula: {}", toCell.getCellFormula());
//						break;
//					case Cell.CELL_TYPE_NUMERIC:
//						toCell.setCellType(Cell.CELL_TYPE_NUMERIC);
//						toCell.setCellValue(cell.getNumericCellValue());
//						break;
//					case Cell.CELL_TYPE_STRING:
//						toCell.setCellType(Cell.CELL_TYPE_STRING);
//						toCell.setCellValue(cell.getStringCellValue().replace((i == startIdx ? object : list + "[" + (mapKeys == null ? (startIdx - 1) : "'" + mapKeys[startIdx - 1] + "'") + "]"), list + "[" + (mapKeys == null ? (i - 1) : "'" + mapKeys[i - 1] + "'") + "]"));
//						log.debug("to cell string: {}", toCell.getStringCellValue());
//						break;
//					}
//				}
//			}
//		}
		// スタイルコピーなら
		if(COPY.equalsIgnoreCase(style)) {
			// 行列加算用
			int addRow = 0;
			int addCol = 0;
			if(COL.equalsIgnoreCase(direction)) {
				addCol = colSpan;
			}
			else {
				addRow = rowSpan;
			}
			
			// コピー元範囲の結合保持なら
			if(!rangeList.isEmpty()) {
				// 結合をコピー
				// 結合保持数でループ
				for(int c = 0; c < rangeList.size(); c++) {
					tmpBfRange = rangeList.get(c);
					// 繰返し回数でループ
					for(int i = startIdx, cpCount = 0; (startIdx <= endIdx && i <= endIdx || startIdx > endIdx && i >= endIdx); i = (startIdx <= endIdx ? i + 1 : i - 1), cpCount++) {
						// 初回はコピーしない（コピー元だから）
						if(i == startIdx) continue;
						// コピー先レンジ
						tmpAfRange = new CellRangeAddress(
								tmpBfRange.getFirstRow() + (addRow * cpCount),
								tmpBfRange.getLastRow() + (addRow * cpCount),
								tmpBfRange.getFirstColumn() + (addCol * cpCount),
								tmpBfRange.getLastColumn() + (addCol * cpCount));
						if(ExcelUtil.validateRange(sheet, tmpAfRange)) {
							sheet.addMergedRegion(tmpAfRange);
							log.debug("mergedRegion copy. from: [{}] to: [{}]", tmpBfRange.formatAsString(), tmpAfRange.formatAsString());
						}
					}
				}
			}
/**
 			// オブジェクトのコピー
			xls形式非対応			
			if(sheet instanceof HSSFSheet) {
				CellRangeAddress cpRange;
				HSSFShape cpShape;
				HSSFPatriarch patriarch = ((HSSFSheet)sheet).getDrawingPatriarch();
				if(patriarch != null) {
					List<HSSFShape> shapes = ((HSSFSheet)sheet).getDrawingPatriarch().getChildren();
					if(shapes != null) {
						HSSFClientAnchor fAnchor, tAnchor;
						HSSFShape tShape;
						int r1, r2;
						short c1, c2;
						for (int i = 0; i < shapes.size(); i++) {
							fAnchor = (HSSFClientAnchor)shapes.get(i).getAnchor();
							if(fAnchor != null) {
								r1 = fAnchor.getRow1();
								c1 = fAnchor.getCol1();
								r2 = fAnchor.getRow2();
								c2 = fAnchor.getCol2();
								tmpRange = new CellRangeAddress(r1, r2, c1, c2);
								switch(CellRangeUtil.intersect(fRange, tmpRange)) {
								case CellRangeUtil.INSIDE:
								case CellRangeUtil.OVERLAP:
									// 繰返し方向縦（行）でない場合
									if(!ROW.equalsIgnoreCase(direction)) {
										tAnchor = new HSSFClientAnchor(
												fAnchor.getDx1(), fAnchor.getDy1(),
												fAnchor.getDx2(), fAnchor.getDy2(),
												(short) (c1 + (colSpan * cpCount)), r1,
												(short) (c2 + (colSpan * cpCount)), r2);
										cpRange = new CellRangeAddress(r1, r2, c1 + (colSpan * cpCount), c2 + (colSpan * cpCount));
									}
									// 繰返し方向縦（行）の場合
									else {
										tAnchor = new HSSFClientAnchor(
												fAnchor.getDx1(), fAnchor.getDy1(),
												fAnchor.getDx2(), fAnchor.getDy2(),
												c1, r1 + (rowSpan * cpCount),
												c2, r2 + (rowSpan * cpCount));
										cpRange = new CellRangeAddress(r1 + (rowSpan * cpCount), r2 + (rowSpan * cpCount), c1, c2);
									}
									if(log.isDebugEnabled()) log.debug("object copy from: " + tmpRange.formatAsString() + " to: "  + cpRange.formatAsString());
									HSSFShape = new 
									patriarch.addShape(new shapes.get(i),)
								}
							}
						}
					}
				}
			}
*/
			// オブジェクトのコピー Excel2007以降 ooxml形式のみ対応
			if(sheet instanceof XSSFSheet) {
				int r1, c1, r2, c2;
				CTDrawing ctDrawing;
				CTTwoCellAnchor fAnchor, cpAnchor;
				CTMarker from, to; 
				try {
					// DocumentPartでループ
					for(POIXMLDocumentPart part : ((XSSFSheet)sheet).getRelations()) {
						if(part == null) continue;
						log.debug("DocumentPart class: {}", part.getClass().getName());
						// DocumentPartがDrawingオブジェクトの場合
						if(part instanceof XSSFDrawing) {
							ctDrawing = ((XSSFDrawing) part).getCTDrawing();
							if(ctDrawing != null) {
								// アンカーでループ
								int alSize = ctDrawing.getTwoCellAnchorList().size();
								for (int i = 0; i < alSize; i++) {
									fAnchor = ctDrawing.getTwoCellAnchorList().get(i);
									// GraphicFrameをもつグラフ、スマートアートは簡単にはコピーできないので非対応
									if(fAnchor.isSetGraphicFrame()) continue;
									
									// アンカーの位置情報
									from = fAnchor.getFrom();
									r1 = from.getRow();
									c1 = from.getCol();
									to = fAnchor.getTo();
									r2 = to.getRow();
									c2 = to.getCol();
									tmpBfRange = new CellRangeAddress(r1, r2, c1, c2);
									
									// コピー元レンジに含まれている、掛かっているならコピー
									switch(CellRangeUtil.intersect(fromRange, tmpBfRange)) {
									case CellRangeUtil.INSIDE:
									case CellRangeUtil.OVERLAP:
										// 繰返し回数でループ
										for(int j = startIdx, cpCount = 0; (startIdx <= endIdx && j <= endIdx || startIdx > endIdx && j >= endIdx); j = (startIdx <= endIdx ? j + 1 : j - 1), cpCount++) {
											// 初回はコピーしない（コピー元だから）
											if(j == startIdx) continue;
											// オブジェクト（アンカー）のコピー
											cpAnchor = ctDrawing.addNewTwoCellAnchor();
											cpAnchor.set(fAnchor.copy());
											from = cpAnchor.getFrom();
											from.setRow(from.getRow() + (addRow * cpCount));
											from.setCol(from.getCol() + (addCol * cpCount));
											to = cpAnchor.getTo();
											to.setRow(to.getRow() + (addRow * cpCount));
											to.setCol(to.getCol() + (addCol * cpCount));
											if(log.isDebugEnabled()) {
												tmpAfRange = new CellRangeAddress(from.getRow(), to.getRow(), from.getCol(), to.getCol());
												log.debug("object copy from: [{}] to: [{}]", tmpBfRange.formatAsString(), tmpAfRange.formatAsString());
											}
										}
									}
								}
							}
						}
					}
				}
				catch(Exception e) {
					log.error("object copy error: " + e.getMessage(), e);
				}
			}
		}
		log.trace("parseForeach end");
	}
	
	/**
	 * If句のパース
	 */
	private void parseIf() {
		log.trace("parseIf start");

		// 判定
		boolean result;
		Object elResult = elMgr.evaluate(expression);
		if(elResult == null) {
			log.debug("evaluate result is null");
			result = false;
		}
		else if(elResult instanceof Boolean) {
			result = (boolean) elResult;
		}
		else {
			result = true;
		}
		log.debug("evaluate result: {}", result);

		// 判定falseの場合、範囲の操作
		if(!result) {
			if(delete == null) {
				log.debug("{} is null set default: {}", TLP_DELETE, LEFT);
				delete = LEFT;
			}
			else if(!LEFT.equalsIgnoreCase(delete) && !UP.equalsIgnoreCase(delete) && !CLEAR.equalsIgnoreCase(delete)) {
				log.warn("{} is illegal argument. set default: {}", TLP_DELETE, LEFT);
				delete = LEFT;
			}
			else {
				log.debug("delete: {}", delete);
				delete = delete.toLowerCase().trim();
			}
			
			switch(delete) {
			// クリア
			case CLEAR:
				ExcelUtil.clearRange(sheet, (new CellRangeAddress(startRowIdx, startRowIdx + rowSpan - 1, startColIdx, startColIdx + colSpan - 1)), false, false);
				break;
			// 左詰め
			case LEFT:
				// IF範囲以降をずらす
				ExcelUtil.shift(sheet, new CellRangeAddress(startRowIdx, startRowIdx + rowSpan - 1, startColIdx + colSpan, startColIdx + colSpan), COL, -colSpan, block);
				break;
			// 上詰め
			case UP:
				// IF範囲以降をずらす
				ExcelUtil.shift(sheet, new CellRangeAddress(startRowIdx + rowSpan, startRowIdx + rowSpan, startColIdx, startColIdx + colSpan - 1), ROW, -rowSpan, block);
			}
		}
		log.trace("parseIf end");
	}
	
	/**
	 * マッチした結果がEL式であるかを返却
	 * 
	 * @return	マッチした結果がEL式であればtrue。以外の場合false
	 */
	private boolean isEL() {
		return EL.equalsIgnoreCase(type);
	}

	/**
	 * マッチした結果がIf句であるかを返却
	 * 
	 * @return	マッチした結果がIf句であればtrue。以外の場合false
	 */
	private boolean isIf() {
		return TL_IF.equalsIgnoreCase(type);
	}

	/**
	 * マッチした結果がForeach句であるかを返却
	 * 
	 * @return	マッチした結果がForeach句であればtrue。以外の場合false
	 */
	private boolean isForeach() {
		return TL_FOREACH.equalsIgnoreCase(type);
	}
	
	/**
	 * 指示子全体検索結果から１つ目の指示子を置換
	 * 
	 * @param replacement	置換文字列
	 * 
	 * @return	置換後の文字列
	 */
	private String replaceFirst(String replacement) {
		return mtAll.replaceFirst(replacement == null ? "" : replacement);
	}
	
	/**
	 * 同一セルの再パース要否を返却
	 * 
	 * @return 再パース必要であればはtrue。不要であればfalse
	 */
	public boolean isReParseCell() {
		return reParseFlg;
	}
}
